import os import logging from typing import NamedTuple, Optional import unittest import datetime from ppp_enmappings import PPPExtraNetworkMappings # pylint: disable=import-error from ppp_wildcards import PPPWildcards # pylint: disable=import-error from ppp import PromptPostProcessor # pylint: disable=import-error from ppp_logging import DEBUG_LEVEL, PromptPostProcessorLogFactory # pylint: disable=import-error class PromptPair(NamedTuple): prompt: str = "" negative_prompt: str = "" class TestPromptPostProcessorBase(unittest.TestCase): """ A test case class for testing the PromptPostProcessor class. """ def setUp(self, enable_file_logging=False): """ Set up the test case by initializing the necessary objects and configurations. Args: enable_file_logging (bool): Whether to enable logging to a file. Defaults to True. """ self.enable_file_logging = enable_file_logging test_name = self.id().split(".")[-1] # Extract the test method name timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") if self.enable_file_logging: log_filename = f"tests/logs/{test_name}_{timestamp}.log" else: log_filename = None # Disable file logging self.lf = PromptPostProcessorLogFactory(None, log_filename) self.ppp_logger = self.lf.log self.ppp_logger.setLevel(logging.DEBUG) self.grammar_content = None self.interrupted = False self.defopts = { "debug_level": DEBUG_LEVEL.full.value, "on_warning": PromptPostProcessor.ONWARNING_CHOICES.stop.value, "process_wildcards": True, "if_wildcards": PromptPostProcessor.IFWILDCARDS_CHOICES.ignore.value, "choice_separator": ", ", "keep_choices_order": False, "stn_separator": ", ", "stn_ignore_repeats": True, "do_cleanup": True, "cleanup_variables": True, "cleanup_empty_constructs": True, "cleanup_extra_separators": True, "cleanup_extra_separators2": True, "cleanup_extra_separators_include_eol": False, "cleanup_extra_spaces": True, "cleanup_breaks": True, "cleanup_breaks_eol": False, "cleanup_ands": True, "cleanup_ands_eol": False, "cleanup_extranetwork_tags": True, "cleanup_merge_attention": True, "remove_extranetwork_tags": False, } self.def_env_info = { "app": "tests", "ppp_config": None, "is_sdxl": True, "model_class": "DiffusionEngine", "models_path": "./webui/models", "model_filename": "./webui/models/Stable-diffusion/testmodel.safetensors", } self.interrupted = False self.wildcards_obj = PPPWildcards(self.lf.log) self.extranetwork_maps_obj = PPPExtraNetworkMappings(self.lf.log) self.wildcards_obj.refresh_wildcards( DEBUG_LEVEL.full, [ os.path.abspath(os.path.join(os.path.dirname(__file__), "wildcards")), os.path.abspath(os.path.join(os.path.dirname(__file__), "wildcards2")), ], """ yaml_input: wildcardI: - choice1 - choice2 - choice3 """, ) self.extranetwork_maps_obj.refresh_extranetwork_mappings( DEBUG_LEVEL.full, [os.path.abspath(os.path.join(os.path.dirname(__file__), "enmappings"))], """ """, ) grammar_filename = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../grammar.lark") with open(grammar_filename, "r", encoding="utf-8") as file: self.grammar_content = file.read() def interrupt(self): self.interrupted = True def process( self, input_prompts: PromptPair, expected_output_prompts: Optional[PromptPair | list[PromptPair]] = None, seed: int = 1, ppp: Optional[str | PromptPostProcessor] = None, interrupted: bool = False, variables: dict[str, str] | None = None, ): """ Process the prompt and compare the results with the expected prompts. Args: input_prompts (PromptPair): The input prompts. expected_output_prompts (PromptPair | list[PromptPair], optional): The expected prompts. seed (int, optional): The seed value. Defaults to 1. ppp (Optional[str | PromptPostProcessor], optional): The PromptPostProcessor instance or type. Defaults to None. interrupted (bool, optional): The interrupted flag. Defaults to False. variables (dict[str,str]|None, optional): Output variables to check. Defaults to None. Returns: None """ if isinstance(ppp, str): if ppp == "nocup": the_obj = PromptPostProcessor( self.ppp_logger, self.interrupt, self.def_env_info, { **self.defopts, "do_cleanup": False, "cleanup_variables": False, "cleanup_empty_constructs": False, "cleanup_extra_separators": False, "cleanup_extra_separators2": False, "cleanup_extra_separators_include_eol": False, "cleanup_extra_spaces": False, "cleanup_breaks": False, "cleanup_breaks_eol": False, "cleanup_ands": False, "cleanup_ands_eol": False, "cleanup_extranetwork_tags": False, "cleanup_merge_attention": False, }, self.grammar_content, self.wildcards_obj, self.extranetwork_maps_obj, ) elif ppp == "comfyui": the_obj = PromptPostProcessor( self.ppp_logger, self.interrupt, { **self.def_env_info, "app": "comfyui", "model_class": "SDXL", }, self.defopts, self.grammar_content, self.wildcards_obj, self.extranetwork_maps_obj, ) else: the_obj = ppp if not the_obj: the_obj = PromptPostProcessor( self.ppp_logger, self.interrupt, self.def_env_info, self.defopts, self.grammar_content, self.wildcards_obj, self.extranetwork_maps_obj, ) out = ( [PromptPair("", "")] if expected_output_prompts is None else expected_output_prompts if isinstance(expected_output_prompts, list) else [expected_output_prompts] ) for eo in out: result_prompt, result_negative_prompt, output_variables = the_obj.process_prompt( input_prompts.prompt, input_prompts.negative_prompt, seed, ) self.assertEqual(self.interrupted, interrupted, "Interrupted flag is incorrect") if not self.interrupted: if expected_output_prompts is not None: self.assertEqual(result_prompt, eo.prompt, "Incorrect prompt") self.assertEqual(result_negative_prompt, eo.negative_prompt, "Incorrect negative prompt") if variables is not None: for var_name, var_value in variables.items(): self.assertIn(var_name, output_variables, f"Variable '{var_name}' not found in output variables") self.assertEqual( output_variables[var_name], var_value, f"Variable '{var_name}' has incorrect value", ) seed += 1 class TestPromptPostProcessor(TestPromptPostProcessorBase): def setUp(self): # pylint: disable=arguments-differ super().setUp(enable_file_logging=False) # Send To Negative tests def test_stn_simple(self): # negtags with different parameters and separations self.process( PromptPair( "flowersred, green, blueyellow, purpleblack", "normal quality, worse quality", ), PromptPair("flowers", "red, green, yellow, normal quality, purple, worse quality, black, blue"), ) def test_stn_complex(self): # complex negtags self.process( PromptPair( "red ((pink)), flowers purple, mauveblue, yellow green", "normal quality, , bad quality, worse quality", ), PromptPair( "flowers", "red, (pink:1.21), normal quality, mauve, yellow, bad quality, green, worse quality, purple, blue", ), ) def test_stn_complex_nocleanup(self): # complex negtags with no cleanup self.process( PromptPair( "red ((pink)), flowers purple, mauveblue, yellow green", "normal quality, , bad quality, worse quality", ), PromptPair( " (()), flowers , , ", "red, ((pink)), normal quality, mauve, yellow, bad quality, green, worse quality, purple, blue", ), ppp="nocup", ) def test_stn_inside_attention(self): # negtag inside attention self.process( PromptPair( "[neg1] this is a ((testneg2) (test:2.0): 1.5 ) (red[square]:1.5)", "normal quality", ), PromptPair( "this is a ((test) (test:2):1.5) (red:1.5)", "[neg1], ([square]:1.5), normal quality, (neg2:1.65)" ), ) def test_stn_inside_alternation(self): # negtag inside alternation self.process( PromptPair( "this is a (([complexneg1|simpleneg2|regularneg3] test)(test:2.0):1.5)", "normal quality", ), PromptPair( "this is a (([complex|simple|regular] test)(test:2):1.5)", "([neg1||]:1.65), ([|neg2|]:1.65), ([||neg3]:1.65), normal quality", ), ) def test_stn_inside_alternation_recursive(self): # negtag inside alternation (recursive alternation) self.process( PromptPair( "this is a (([complexneg1[one|twoneg12||three|four(neg14)]|simpleneg2|regularneg3] test)(test:2.0):1.5)", "normal quality", ), PromptPair( "this is a (([complex[one|two||three|four]|simple|regular] test)(test:2):1.5)", "([neg1||]:1.65), ([[|neg12|||]||]:1.65), ([[||||(neg14)]||]:1.65), ([|neg2|]:1.65), ([||neg3]:1.65), normal quality", ), ) def test_stn_inside_scheduling(self): # negtag inside scheduling self.process( PromptPair("this is [abcneg1:defneg2: 5 ]", "normal quality"), [PromptPair("this is [abc:def:5]", "[neg1::5], normal quality, [neg2:5]")], ) def test_stn_complex_features(self): # complex negtags with AND, BREAK and other features self.process( PromptPair( "[neg5] this \\(is\\): a (([complex|simpleneg6|regular] testneg1)(test:2.0):1.5) \nBREAK, BREAK with [abcneg4:defneg2(neg3:1.6):5]:0.5 AND loratrigger AND AND hypernettrigger :0.3", "normal quality, ", ), PromptPair( "this \\(is\\): a (([complex|simple|regular] test)(test:2):1.5)\nBREAK with [abc:def:5]:0.5 AND loratrigger AND hypernettrigger :0.3", "[neg5], ([|neg6|]:1.65), (neg1:1.65), [neg4::5], normal quality, [neg2(neg3:1.6):5]", ), ) def test_stn_complex_features_newformat(self): # complex negtags with AND, BREAK and other features (new format) self.process( PromptPair( "[neg5] this \\(is\\): a (([complex|simpleneg6|regular] testneg1)(test:2.0):1.5) \nBREAK, BREAK with [abcneg4:defneg2(neg3:1.6):5]:0.5 AND loratrigger AND AND hypernettrigger :0.3", "normal quality, ", ), PromptPair( "this \\(is\\): a (([complex|simple|regular] test)(test:2):1.5)\nBREAK with [abc:def:5]:0.5 AND loratrigger AND hypernettrigger :0.3", "[neg5], ([|neg6|]:1.65), (neg1:1.65), [neg4::5], normal quality, [neg2(neg3:1.6):5]", ), ) def test_stn_inside_alternation_recursive_2(self): # negtag inside alternation (recursive alternation) self.process( PromptPair( "[pos1neg1[pos11|pos12neg12||pos14|pos15neg15]|pos2neg2|pos3neg3]", "", ), PromptPair( "[pos1[pos11|pos12||pos14|pos15]|pos2|pos3]", "[neg1||], [[|neg12|||]||], [[||||neg15]||], [|neg2|], [||neg3]", # "[neg1[|neg12|||neg15]|neg2|neg3]", # expected output if the constructs were unified ), ) # Cleanup tests def test_cl_simple(self): # simple cleanup self.process( PromptPair(" this is a ((test ), , , (), , [] ( , test ,:2.0):1.5), (red:1.5) ", " normal quality "), PromptPair("this is a ((test), (test,:2):1.5), (red:1.5)", "normal quality"), ) def test_cl_complex(self): # complex cleanup self.process( PromptPair( " this is BREAKABLE a ((test)), ,AND AND(() [] ANDERSON (test:2.0):1.5) :o BREAK \n BREAK (red:1.5) ", " [:hands, feet, :0.15]normal quality ", ), PromptPair( "this is BREAKABLE a (test:1.21) AND( ANDERSON (test:2):1.5) :o BREAK (red:1.5)", "[:hands, feet, :0.15]normal quality", ), ) def test_cl_removenetworktags(self): # remove network tags self.process( PromptPair("this is a test__yaml/wildcard7__", ""), PromptPair("this is a test", ""), ppp=PromptPostProcessor( self.ppp_logger, self.interrupt, self.def_env_info, {**self.defopts, "remove_extranetwork_tags": True}, self.grammar_content, self.wildcards_obj, self.extranetwork_maps_obj, ), ) def test_cl_dontremoveseparatorsoneol(self): # don't remove separators on eol self.process( PromptPair("this is a test,\nsecond line", ""), PromptPair("this is a test,\nsecond line", ""), ppp=PromptPostProcessor( self.ppp_logger, self.interrupt, self.def_env_info, { **self.defopts, "cleanup_extra_separators2": False, "cleanup_extra_separators_include_eol": False, }, self.grammar_content, self.wildcards_obj, self.extranetwork_maps_obj, ), ) def test_cl_separatorswitheol(self): # don't remove eols with the separators self.process( PromptPair( """{ (d:0.9) ,, (l:1.1) | (l:1.1) (d:0.9),,, } (l:1.1) (d:0.9)""", "", ), PromptPair( """ (l:1.1) (d:0.9), (l:1.1) (d:0.9)""", "", ), ppp=PromptPostProcessor( self.ppp_logger, self.interrupt, self.def_env_info, { **self.defopts, "cleanup_empty_constructs": False, "cleanup_extra_separators": True, "cleanup_extra_separators2": False, "cleanup_extra_separators_include_eol": False, "cleanup_extra_spaces": False, "cleanup_breaks": False, "cleanup_breaks_eol": False, "cleanup_ands": False, "cleanup_ands_eol": False, "cleanup_extranetwork_tags": False, "cleanup_merge_attention": False, }, self.grammar_content, self.wildcards_obj, self.extranetwork_maps_obj, ), ) def test_cl_mergeattention(self): # merge attention self.process( PromptPair( "this is (a test:0.9) of (attention (merging:1.2)) where ((this)) ((is joined:1.2)) and ([this too]:1.3)", "", ), PromptPair( "this is [a test] of (attention (merging:1.2)) where (this:1.21) (is joined:1.32) and (this too:1.17)", "", ), ) def test_cl_not_mergeattention(self): # not merge attention self.process( PromptPair( "this is (a test:0.9) of not (attention (merging:1.2)) where ((this)) ((is not joined:1.2)) and neither is ([this]:1.3)", "", ), PromptPair( "this is (a test:0.9) of not (attention (merging:1.2)) where ((this)) ((is not joined:1.2)) and neither is ([this]:1.3)", "", ), ppp="nocup", ) # Command tests def test_cmd_stn_complex_features(self): # complex stn command with AND, BREAK and other features self.process( PromptPair( "[neg5] this \\(is\\): a (([complex|simpleneg6|regular] testneg1)(test:2.0):1.5) \nBREAK, BREAK with [abcneg4:defneg2(neg3:1.6):5]:0.5 AND loratrigger AND AND hypernettrigger :0.3", "normal quality, ", ), PromptPair( "this \\(is\\): a (([complex|simple|regular] test)(test:2):1.5)\nBREAK with [abc:def:5]:0.5 AND loratrigger AND hypernettrigger :0.3", "[neg5], ([|neg6|]:1.65), (neg1:1.65), [neg4::5], normal quality, [neg2(neg3:1.6):5]", ), ) def test_cmd_if_complex_features(self): # complex if command self.process( PromptPair( "this \\(is\\): a (([complex|simple|regular] test)(test:2.0):1.5) \nBREAK, BREAK with [abcneg4:def:5]:0.5 AND loratrigger hypernettrigger nothing:0.3", "normal quality", ), PromptPair( "this \\(is\\): a (([complex|simple|regular] test)(test:2):1.5)\nBREAK :0.5 AND hypernettrigger :0.3", "normal quality", ), ) def test_cmd_if_nested(self): # nested if command self.process( PromptPair( "this is SD1PONYSD2NOPONYNOPONY", "", ), PromptPair("this is PONY", ""), ppp=PromptPostProcessor( self.ppp_logger, self.interrupt, { **self.def_env_info, "model_filename": "./webui/models/Stable-diffusion/ponymodel.safetensors", }, self.defopts, self.grammar_content, self.wildcards_obj, self.extranetwork_maps_obj, ), ) def test_cmd_set_if(self): # set and if commands self.process( PromptPair("valuethis test is OKnot OK", ""), PromptPair("this test is OK", ""), ) def test_cmd_set_empty(self): # set to empty self.process( PromptPair("${v2=}this test is not OKOK", ""), PromptPair("this test is OK", ""), ) def test_cmd_set_eval_if(self): # set and if commands self.process( PromptPair("valuethis test is OKnot OK", ""), PromptPair("this test is OK", ""), ) def test_cmd_set_if_echo_nested(self): # nested set, if and echo commands self.process( PromptPair( "1OKnot OK NOK OK", "", ), PromptPair("OK OK OK", ""), ) def test_cmd_set_if_complex_conditions_1(self): # complex conditions (or) self.process( PromptPair( "truefalsethis test is OKnot OK", "", ), PromptPair("this test is OK", ""), ) def test_cmd_set_if_complex_conditions_2(self): # complex conditions (and) self.process( PromptPair( "truetruethis test is OKnot OK", "", ), PromptPair("this test is OK", ""), ) def test_cmd_set_if_complex_conditions_3(self): # complex conditions (not) self.process( PromptPair("falsethis test is OKnot OK", ""), PromptPair("this test is OK", ""), ) def test_cmd_set_if_complex_conditions_4(self): # complex conditions (not, precedence) self.process( PromptPair( "truefalsethis test is OKnot OK", "", ), PromptPair("this test is OK", ""), ) def test_cmd_set_if_complex_conditions_5(self): # complex conditions (not, precedence, comparison) self.process( PromptPair( "1falsethis test is OKnot OK", "", ), PromptPair("this test is OK", ""), ) def test_cmd_set_if_complex_conditions_6(self): # complex conditions self.process( PromptPair( "123this test is OKnot OK", "", ), PromptPair("this test is OK", ""), ) def test_cmd_set_if_complex_conditions_7(self): # complex conditions self.process( PromptPair( "123this test is OKnot OK", "", ), PromptPair("this test is OK", ""), ) def test_var_nested_1(self): # variable default nested in variable set self.process( PromptPair( "${v1=test ${v2:OK}}${v1}", "", ), PromptPair("test OK", ""), variables={"v1": "test OK", "v2": "OK"}, ) def test_var_nested_2(self): # variable set nested in variable default self.process( PromptPair( "${v1:test ${v2=OK}${v2}}", "", ), PromptPair("test OK", ""), variables={"v1": "test OK", "v2": "OK"}, ) def test_var_nested_3(self): # variable default nested in variable default self.process( PromptPair( "${v1:test ${v2:OK}}", "", ), PromptPair("test OK", ""), variables={"v1": "test OK", "v2": "OK"}, ) def test_cmd_set_if2(self): # set and more complex if commands self.process( PromptPair( "First: value1this test is OKOK2not OK\nSecond: value3this test is OKnot OK", "", ), PromptPair("First: this test is OK\nSecond: this test is OK", ""), ) def test_cmd_set_add_if(self): # set, add and if commands self.process( PromptPair( "value2this test is OKnot OK", "", ), PromptPair("this test is OK", ""), ) def test_cmd_set_add_DP_if(self): # set, add (DP format) and if commands self.process( PromptPair( "${v=value}${v+=2}this test is OKnot OK", "", ), PromptPair("this test is OK", ""), ) def test_cmd_set_immediateeval(self): # set (DP format) with mixed evaluation self.process( PromptPair( "${var=!__yaml/wildcard1__}the choices are: ${var}, ${var}, ${var2:default}, ${var3=__yaml/wildcard1__}${var3}, ${var3}", "", ), PromptPair("the choices are: choice2, choice2, default, choice3, choice1", ""), ppp="nocup", ) def test_cmd_set_mixeval(self): # set and add (DP format) with mixed evaluation self.process( PromptPair( "${var=__yaml/wildcard1__}the choices are: ${var}, ${var}, ${var+=, __yaml/wildcard2__}${var}, ${var}, ${var+=!, __yaml/wildcard3__}${var}, ${var}", "", ), PromptPair( "the choices are: choice2, choice3, choice1, choice1- choice2 -choice3, choice2, choice2 -choice1-choice3, choice2, choice3-choice1- choice2 , choice1, choice2 , choice2, choice3-choice1- choice2 , choice1, choice2 ", "", ), ppp="nocup", ) def test_cmd_set_ifundefined_if(self): # set, ifundefined and if commands self.process( PromptPair( "valuethis test is OKnot OK", "", ), PromptPair("this test is OK", ""), ) def test_cmd_set_ifundefined_if_2(self): # set, ifundefined and if commands self.process( PromptPair( "valuevalue2this test is OKnot OK", "", ), PromptPair("this test is OK", ""), ) def test_cmd_set_ifundefined_DP_if(self): # set, ifundefined (DP format) and if commands self.process( PromptPair( "${v?=value}this test is OKnot OK", "", ), PromptPair("this test is OK", ""), ) def test_cmd_set_ifundefined_DP_if_2(self): # set, ifundefined (DP format) and if commands self.process( PromptPair( "${v=!value}${v?=!value2}this test is OKnot OK", "", ), PromptPair("this test is OK", ""), ) def test_cmd_ext(self): # ext self.process( PromptPair( "trigger1trigger2trigger4trigger5", "", ), PromptPair( "trigger1,trigger2,trigger4,trigger5", "", ), ) def test_cmd_ext_map_notrigger(self): # ext mapping, no trigger self.process( PromptPair( "", "", ), PromptPair("triggergeneric1, triggergeneric2, two, triggergeneric1, triggergeneric2, two", ""), ) def test_cmd_ext_map1(self): # ext mapping, no lora self.process( PromptPair( "inlinetrigger", "", ), PromptPair("inlinetrigger, triggergeneric1, triggergeneric2, two", ""), ) def test_cmd_ext_map2(self): # ext mapping, lora with weight self.process( PromptPair( "inlinetrigger", "", ), PromptPair("inlinetrigger, triggerpony1, triggerpony2", ""), ppp=PromptPostProcessor( self.ppp_logger, self.interrupt, { **self.def_env_info, "model_filename": "./webui/models/Stable-diffusion/ponymodel.safetensors", }, self.defopts, self.grammar_content, self.wildcards_obj, self.extranetwork_maps_obj, ), ) def test_cmd_ext_map3(self): # ext mapping, lora with weight adjusted self.process( PromptPair( "inlinetrigger", "", ), PromptPair("inlinetrigger, triggerpony1, triggerpony2", ""), ppp=PromptPostProcessor( self.ppp_logger, self.interrupt, { **self.def_env_info, "model_filename": "./webui/models/Stable-diffusion/ponymodel.safetensors", }, self.defopts, self.grammar_content, self.wildcards_obj, self.extranetwork_maps_obj, ), ) def test_cmd_ext_map4(self): # ext mapping, lora with parameters self.process( PromptPair( "inlinetrigger", "", ), PromptPair("inlinetrigger, triggerpony1, triggerpony2", ""), ppp=PromptPostProcessor( self.ppp_logger, self.interrupt, { **self.def_env_info, "model_filename": "./webui/models/Stable-diffusion/ponymodel.safetensors", }, self.defopts, self.grammar_content, self.wildcards_obj, self.extranetwork_maps_obj, ), ) def test_cmd_ext_map5(self): # ext mapping, lora with no parameters self.process( PromptPair( "inlinetrigger", "", ), PromptPair("inlinetrigger, triggerillustrious1, triggerillustrious2", ""), ppp=PromptPostProcessor( self.ppp_logger, self.interrupt, { **self.def_env_info, "model_filename": "./webui/models/Stable-diffusion/ilxlmodel.safetensors", }, self.defopts, self.grammar_content, self.wildcards_obj, self.extranetwork_maps_obj, ), ) # Choices tests def test_ch_choices(self): # simple choices with weights self.process( PromptPair("the choices are: {3::choice1|2::choice2|choice3}", ""), PromptPair("the choices are: choice2", ""), ppp="nocup", ) def test_ch_unsupportedsampler(self): # unsupported sampler self.process( PromptPair("the choices are: {@choice1|choice2|choice3}", ""), PromptPair("", ""), ppp="nocup", interrupted=True, ) def test_ch_choices_withcomments(self): # choices with comments and multiline self.process( PromptPair( "the choices are: {\n3::choice1 # this is option 1\n|2::choice2\n# this was option 2\n|choice3 # this is option 3\n}", "", ), PromptPair("the choices are: choice2", ""), ppp="nocup", ) def test_ch_choices_multiple(self): # choices with multiple selection self.process( PromptPair("the choices are: {~2$$, $$3::choice1|2:: choice2 |choice3}", ""), PromptPair("the choices are: choice2 , choice3", ""), ppp="nocup", ) def test_ch_choices_if_multiple(self): # choices with if and multiple selection self.process( PromptPair("the choices are: {2$$, $$3::choice1|2 if _is_sd1::choice2|choice3}", ""), PromptPair("the choices are: choice1, choice3", ""), ppp="nocup", ) def test_ch_choices_set_if_multiple(self): # choices with if user variable and multiple selection self.process( PromptPair("${var=test}the choices are: {2$$, $$3::choice1|2 if not var eq 'test'::choice2|choice3}", ""), PromptPair("the choices are: choice1, choice3", ""), ppp="nocup", ) def test_ch_choices_set_if_nested(self): # nested choices with if user variable and multiple selection self.process( PromptPair( "${var=test}the choices are: {2$$, $$3::choice1${var2=test2} {if var2 eq 'test2'::choice11|choice12}|2 if not var eq 'test'::choice2|choice3}", "", ), PromptPair("the choices are: choice1 choice11, choice3", ""), ppp="nocup", ) def test_ch_choicesinsidelora(self): # simple choices inside a lora self.process( PromptPair("", ""), PromptPair("", ""), ppp="nocup", ) def test_ch_removelorawithchoices(self): self.process( PromptPair("", ""), PromptPair("", ""), ppp=PromptPostProcessor( self.ppp_logger, self.interrupt, self.def_env_info, {**self.defopts, "remove_extranetwork_tags": True}, self.grammar_content, self.wildcards_obj, self.extranetwork_maps_obj, ), ) def test_ch_cmd_includewildcard(self): self.process( PromptPair("{ch_one|ch_two|%0.5::include yaml/wildcard1}", ""), PromptPair("ch_two", ""), ppp="nocup", ) # Wildcards tests def test_wc_ignore(self): # wildcards with ignore option self.process( PromptPair("__bad_wildcard__", "{option1|option2}"), PromptPair("__bad_wildcard__", "{option1|option2}"), ppp=PromptPostProcessor( self.ppp_logger, self.interrupt, self.def_env_info, { **self.defopts, "process_wildcards": False, "if_wildcards": PromptPostProcessor.IFWILDCARDS_CHOICES.ignore.value, }, self.grammar_content, self.wildcards_obj, self.extranetwork_maps_obj, ), ) def test_wc_remove(self): # wildcards with remove option self.process( PromptPair( "[neg5] this is: __bad_wildcard__ a (([complex|simpleneg6|regular] testneg1)(test:2.0):1.5) \nBREAK, BREAK with [abcneg4:defneg2(neg3:1.6):5] ", "normal quality, {option1|option2}", ), PromptPair( "this is: a (([complex|simple|regular] test)(test:2):1.5)\nBREAK with [abc:def:5]", "[neg5], ([|neg6|]:1.65), (neg1:1.65), [neg4::5], normal quality, [neg2(neg3:1.6):5]", ), ppp=PromptPostProcessor( self.ppp_logger, self.interrupt, self.def_env_info, { **self.defopts, "process_wildcards": False, "if_wildcards": PromptPostProcessor.IFWILDCARDS_CHOICES.remove.value, }, self.grammar_content, self.wildcards_obj, self.extranetwork_maps_obj, ), ) def test_wc_warn(self): # wildcards with warn option self.process( PromptPair("__bad_wildcard__", "{option1|option2}"), PromptPair(PromptPostProcessor.WILDCARD_WARNING + "__bad_wildcard__", "{option1|option2}"), ppp=PromptPostProcessor( self.ppp_logger, self.interrupt, self.def_env_info, { **self.defopts, "process_wildcards": False, "if_wildcards": PromptPostProcessor.IFWILDCARDS_CHOICES.warn.value, }, self.grammar_content, self.wildcards_obj, self.extranetwork_maps_obj, ), ) def test_wc_stop(self): # wildcards with stop option self.process( PromptPair("__bad_wildcard__", "{option1|option2}"), PromptPair( PromptPostProcessor.WILDCARD_STOP.format("__bad_wildcard__") + "__bad_wildcard__", "{option1|option2}", ), ppp=PromptPostProcessor( self.ppp_logger, self.interrupt, self.def_env_info, { **self.defopts, "process_wildcards": False, "if_wildcards": PromptPostProcessor.IFWILDCARDS_CHOICES.stop.value, }, self.grammar_content, self.wildcards_obj, self.extranetwork_maps_obj, ), interrupted=True, ) def test_wcinvar_warn(self): # wildcards in var with warn option self.process( PromptPair("${v=__bad_wildcard__}${v}", ""), PromptPair(PromptPostProcessor.WILDCARD_WARNING + "__bad_wildcard__", ""), ppp=PromptPostProcessor( self.ppp_logger, self.interrupt, self.def_env_info, { **self.defopts, "process_wildcards": False, "if_wildcards": PromptPostProcessor.IFWILDCARDS_CHOICES.warn.value, }, self.grammar_content, self.wildcards_obj, self.extranetwork_maps_obj, ), ) def test_wc_invalid_name(self): self.process( PromptPair("the choices are: ___invalid__", ""), PromptPair("the choices are: ___invalid__", ""), ppp="nocup", ) def test_wc_wildcard1a_text(self): # simple text wildcard self.process( PromptPair("the choices are: __text/wildcard1__", ""), PromptPair("the choices are: choice2", ""), ppp="nocup", ) def test_wc_wildcard1a_json(self): # simple json wildcard self.process( PromptPair("the choices are: __json/wildcard1__", ""), PromptPair("the choices are: choice2", ""), ppp="nocup", ) def test_wc_wildcard1a_yaml(self): # simple yaml wildcard self.process( PromptPair("the choices are: __yaml/wildcard1__", ""), PromptPair("the choices are: choice2", ""), ppp="nocup", ) def test_wc_wildcard1b_text(self): # simple text wildcard with multiple choices self.process( PromptPair("the choices are: __2-$$text/wildcard1__", ""), PromptPair("the choices are: choice3, choice1", ""), ppp="nocup", ) def test_wc_wildcard1b_json(self): # simple json wildcard with multiple choices self.process( PromptPair("the choices are: __2-$$json/wildcard1__", ""), PromptPair("the choices are: choice3, choice1", ""), ppp="nocup", ) def test_wc_wildcard1b_yaml(self): # simple yaml wildcard with multiple choices self.process( PromptPair("the choices are: __2-$$yaml/wildcard1__", ""), PromptPair("the choices are: choice3, choice1", ""), ppp="nocup", ) def test_wc_wildcard2_text(self): # simple text wildcard with default options self.process( PromptPair("the choices are: __text/wildcard2__", ""), PromptPair("the choices are: choice3-choice1", ""), ppp="nocup", ) def test_wc_wildcard2_json(self): # simple json wildcard with default options self.process( PromptPair("the choices are: __json/wildcard2__", ""), PromptPair("the choices are: choice3-choice1", ""), ppp="nocup", ) def test_wc_wildcard2_yaml(self): # simple yaml wildcard with default options self.process( PromptPair("the choices are: __yaml/wildcard2__", ""), PromptPair("the choices are: choice3-choice1", ""), ppp="nocup", ) def test_wc_test2_yaml(self): # simple yaml wildcard self.process( PromptPair("the choice is: __testwc/test2__", ""), PromptPair("the choice is: 2", ""), ppp="nocup", ) def test_wc_test3_yaml(self): # simple yaml wildcard self.process( PromptPair("the choice is: __testwc/test3__", ""), PromptPair("the choice is: one choice", ""), ppp="nocup", ) def test_wc_wildcard_filter_index(self): # wildcard with positional index filter self.process( PromptPair("the choice is: __yaml/wildcard2'2'__", ""), PromptPair("the choice is: choice3-choice3", ""), ppp="nocup", ) def test_wc_wildcard_filter_label(self): # wildcard with label filter self.process( PromptPair("the choice is: __yaml/wildcard2'label1'__", ""), PromptPair("the choice is: choice3-choice1", ""), ppp="nocup", ) def test_wc_wildcard_filter_label2(self): # wildcard with label filter in multiple choices self.process( PromptPair("the choice is: __yaml/wildcard2'label2'__", ""), PromptPair("the choice is: choice1-choice1", ""), ppp="nocup", ) def test_wc_wildcard_filter_label3(self): # wildcard with multiple label filter self.process( PromptPair("the choice is: __yaml/wildcard2'label1,label2'__", ""), PromptPair("the choice is: choice3-choice1", ""), ppp="nocup", ) def test_wc_wildcard_filter_indexlabel(self): # wildcard with mixed index and label filter self.process( PromptPair("the choice is: __yaml/wildcard2'2,label2'__", ""), PromptPair("the choice is: choice3-choice1", ""), ppp="nocup", ) def test_wc_wildcard_filter_compound(self): # wildcard with compound filter self.process( PromptPair("the choice is: __yaml/wildcard2'label1+label3'__", ""), PromptPair("the choice is: choice3-choice3", ""), ppp="nocup", ) def test_wc_wildcard_filter_compound2(self): # wildcard with inherited compound filter self.process( PromptPair("the choice is: __yaml/wildcard2bis'#label1+label3'__", ""), PromptPair("the choice is: choice3bis", ""), ppp="nocup", ) def test_wc_wildcard_filter_compound3(self): # wildcard with doubly inherited compound filter self.process( PromptPair("the choice is: __yaml/wildcard2bisbis'#label1+label3'__", ""), PromptPair("the choice is: choice3bisbis", ""), ppp="nocup", ) def test_wc_wildcard_default_filter(self): # wildcard with default filter self.process( PromptPair("the choice is: __yaml/wildcard2__, __yaml/wildcard2__", ""), PromptPair("the choice is: choice3-choice3, choice3-choice1- choice2 ", ""), ppp="nocup", ) def test_wc_nested_wildcard_text(self): # nested text wildcard with repeating multiple choices self.process( PromptPair("the choices are: __r3$$-$$text/wildcard3__", ""), PromptPair("the choices are: choice3,choice1- choice2 ,choice3", ""), ppp="nocup", ) def test_wc_nested_wildcard_json(self): # nested json wildcard with repeating multiple choices self.process( PromptPair("the choices are: __r3$$-$$json/wildcard3__", ""), PromptPair("the choices are: choice3,choice1- choice2 ,choice3", ""), ppp="nocup", ) def test_wc_nested_wildcard_yaml(self): # nested yaml wildcard with repeating multiple choices self.process( PromptPair("the choices are: __r3$$-$$yaml/wildcard3__", ""), PromptPair("the choices are: choice3,choice1- choice2 ,choice3", ""), ppp="nocup", ) def test_wc_wildcard_optional(self): # empty wildcard with no error self.process( PromptPair("the choices are: __yaml/empty_wildcard__", ""), PromptPair("the choices are: ", ""), ppp="nocup", ) def test_wc_wildcard4_yaml(self): # simple yaml wildcard with one option self.process( PromptPair("the choices are: __yaml/wildcard4__", ""), PromptPair("the choices are: inline text", ""), ppp="nocup", ) def test_wc_wildcard6_yaml(self): # simple yaml wildcard with object formatted choices self.process( PromptPair("the choices are: __yaml/wildcard6__", ""), PromptPair("the choices are: choice2", ""), ppp="nocup", ) def test_wc_choice_wildcard_mix(self): # choices with wildcard mix self.process( PromptPair("the choices are: {__~2$$yaml/wildcard2__|choice0}", ""), [ PromptPair("the choices are: choice0", ""), PromptPair("the choices are: choice1, choice3", ""), PromptPair("the choices are: choice1, choice3", ""), ], ppp="nocup", ) def test_wc_unsupportedsampler(self): # unsupported sampler self.process( PromptPair("the choices are: __@yaml/wildcard2__", ""), PromptPair("", ""), ppp="nocup", interrupted=True, ) def test_wc_wildcard_globbing(self): # wildcard with globbing self.process( PromptPair("the choices are: __yaml/wildcard[12]__, __yaml/wildcard?__", ""), PromptPair("the choices are: choice3-choice2, - choice2 -choice3", ""), ppp="nocup", ) def test_wc_wildcardwithvar(self): # wildcard with inline variable self.process( PromptPair("the choices are: __yaml/wildcard5(var=test)__, __yaml/wildcard5__", ""), PromptPair("the choices are: inline test, inline default", ""), ppp="nocup", ) def test_wc_wildcardPS_yaml(self): # yaml wildcard with object formatted choices and options and prefix and suffix self.process( PromptPair("the choices are: __yaml/wildcardPS__", ""), PromptPair("the choices are: prefix-choice2/choice3-suffix", ""), ppp="nocup", ) def test_wc_anonymouswildcard_yaml(self): # yaml anonymous wildcard self.process( PromptPair("the choices are: __yaml/anonwildcards__", ""), PromptPair("the choices are: six", ""), ppp="nocup", ) def test_wc_wildcard_input(self): # simple yaml wildcard input self.process( PromptPair("the choices are: __yaml_input/wildcardI__", ""), PromptPair("the choices are: choice2", ""), ppp="nocup", ) def test_wc_circular(self): # wildcard circular reference self.process( PromptPair("the choices are: __yaml/circular1__", ""), PromptPair("", ""), ppp="nocup", interrupted=True, ) def test_wc_including(self): # wildcard including another wildcard self.process( PromptPair("the choices are: __yaml/including__", ""), PromptPair("the choices are: choice4", ""), ppp="nocup", ) def test_wc_circular_including(self): # wildcard including another wildcard in a circular reference self.process( PromptPair("the choices are: __yaml/including1__", ""), PromptPair("", ""), ppp="nocup", interrupted=True, ) def test_wc_dynamicwildcard(self): # wildcard built from variables self.process( PromptPair( "the choices are: ${x={1|2|3}}${w=yaml/wildcard${x}}__yaml/wildcard${x}__ __${w}__ ____", "", ), PromptPair("the choices are: choice1-choice3-choice1 choice3- choice2 - choice2 choice3", ""), ppp="nocup", ) # Hosts tests def test_host_attention_parentheses(self): self.process( PromptPair( "[test1] (test2) (test3:1.5)", "", ), PromptPair("(test1:0.9) (test2) (test3:1.5)", ""), ppp=PromptPostProcessor( self.ppp_logger, self.interrupt, { **self.def_env_info, "ppp_config": {"hosts": {"tests": {"attention": "parentheses"}}}, }, self.defopts, self.grammar_content, self.wildcards_obj, self.extranetwork_maps_obj, ), ) def test_host_attention_disable(self): self.process( PromptPair( "[test1] (test2) (test3:1.5)", "", ), PromptPair("test1 test2 test3", ""), ppp=PromptPostProcessor( self.ppp_logger, self.interrupt, { **self.def_env_info, "ppp_config": {"hosts": {"tests": {"attention": "disable"}}}, }, self.defopts, self.grammar_content, self.wildcards_obj, self.extranetwork_maps_obj, ), ) def test_host_attention_remove(self): self.process( PromptPair( "[test1] (test2) (test3:1.5)", "", ), PromptPair("", ""), ppp=PromptPostProcessor( self.ppp_logger, self.interrupt, { **self.def_env_info, "ppp_config": {"hosts": {"tests": {"attention": "remove"}}}, }, self.defopts, self.grammar_content, self.wildcards_obj, self.extranetwork_maps_obj, ), ) def test_host_attention_error(self): self.process( PromptPair( "[test1] (test2) (test3:1.5)", "", ), PromptPair("", ""), ppp=PromptPostProcessor( self.ppp_logger, self.interrupt, { **self.def_env_info, "ppp_config": {"hosts": {"tests": {"attention": "error"}}}, }, self.defopts, self.grammar_content, self.wildcards_obj, self.extranetwork_maps_obj, ), interrupted=True, ) def test_host_scheduling_before(self): self.process( PromptPair( "[test1:test2:0.5]", "", ), PromptPair("test1", ""), ppp=PromptPostProcessor( self.ppp_logger, self.interrupt, { **self.def_env_info, "ppp_config": {"hosts": {"tests": {"scheduling": "before"}}}, }, self.defopts, self.grammar_content, self.wildcards_obj, self.extranetwork_maps_obj, ), ) def test_host_scheduling_after(self): self.process( PromptPair( "[test1:test2:0.5]", "", ), PromptPair("test2", ""), ppp=PromptPostProcessor( self.ppp_logger, self.interrupt, { **self.def_env_info, "ppp_config": {"hosts": {"tests": {"scheduling": "after"}}}, }, self.defopts, self.grammar_content, self.wildcards_obj, self.extranetwork_maps_obj, ), ) def test_host_scheduling_first(self): self.process( PromptPair( "[test1::0.5] [:test2:0.5] [test3:test4:0.5]", "", ), PromptPair("test1 test3", ""), ppp=PromptPostProcessor( self.ppp_logger, self.interrupt, { **self.def_env_info, "ppp_config": {"hosts": {"tests": {"scheduling": "first"}}}, }, self.defopts, self.grammar_content, self.wildcards_obj, self.extranetwork_maps_obj, ), ) def test_host_scheduling_remove(self): self.process( PromptPair( "[test1:test2:0.5]", "", ), PromptPair("", ""), ppp=PromptPostProcessor( self.ppp_logger, self.interrupt, { **self.def_env_info, "ppp_config": {"hosts": {"tests": {"scheduling": "remove"}}}, }, self.defopts, self.grammar_content, self.wildcards_obj, self.extranetwork_maps_obj, ), ) def test_host_scheduling_error(self): self.process( PromptPair( "[test1:test2:0.5]", "", ), PromptPair("", ""), ppp=PromptPostProcessor( self.ppp_logger, self.interrupt, { **self.def_env_info, "ppp_config": {"hosts": {"tests": {"scheduling": "error"}}}, }, self.defopts, self.grammar_content, self.wildcards_obj, self.extranetwork_maps_obj, ), interrupted=True, ) def test_host_alternation_first(self): self.process( PromptPair( "[test1|test2|test3]", "", ), PromptPair("test1", ""), ppp=PromptPostProcessor( self.ppp_logger, self.interrupt, { **self.def_env_info, "ppp_config": {"hosts": {"tests": {"alternation": "first"}}}, }, self.defopts, self.grammar_content, self.wildcards_obj, self.extranetwork_maps_obj, ), ) def test_host_alternation_remove(self): self.process( PromptPair( "[test1|test2|test3]", "", ), PromptPair("", ""), ppp=PromptPostProcessor( self.ppp_logger, self.interrupt, { **self.def_env_info, "ppp_config": {"hosts": {"tests": {"alternation": "remove"}}}, }, self.defopts, self.grammar_content, self.wildcards_obj, self.extranetwork_maps_obj, ), ) def test_host_alternation_error(self): self.process( PromptPair( "[test1|test2|test3]", "", ), PromptPair("", ""), ppp=PromptPostProcessor( self.ppp_logger, self.interrupt, { **self.def_env_info, "ppp_config": {"hosts": {"tests": {"alternation": "error"}}}, }, self.defopts, self.grammar_content, self.wildcards_obj, self.extranetwork_maps_obj, ), interrupted=True, ) def test_host_and_eol(self): self.process( PromptPair( "test1 AND test2:2", "", ), PromptPair("test1\ntest2", ""), ppp=PromptPostProcessor( self.ppp_logger, self.interrupt, { **self.def_env_info, "ppp_config": {"hosts": {"tests": {"and": "eol"}}}, }, self.defopts, self.grammar_content, self.wildcards_obj, self.extranetwork_maps_obj, ), ) def test_host_and_comma(self): self.process( PromptPair( "test1 AND test2:2", "", ), PromptPair("test1, test2", ""), ppp=PromptPostProcessor( self.ppp_logger, self.interrupt, { **self.def_env_info, "ppp_config": {"hosts": {"tests": {"and": "comma"}}}, }, self.defopts, self.grammar_content, self.wildcards_obj, self.extranetwork_maps_obj, ), ) def test_host_and_remove(self): self.process( PromptPair( "test1 AND test2:2", "", ), PromptPair("test1 test2", ""), ppp=PromptPostProcessor( self.ppp_logger, self.interrupt, { **self.def_env_info, "ppp_config": {"hosts": {"tests": {"and": "remove"}}}, }, self.defopts, self.grammar_content, self.wildcards_obj, self.extranetwork_maps_obj, ), ) def test_host_and_error(self): self.process( PromptPair( "test1 AND test2:2", "", ), PromptPair("", ""), ppp=PromptPostProcessor( self.ppp_logger, self.interrupt, { **self.def_env_info, "ppp_config": {"hosts": {"tests": {"and": "error"}}}, }, self.defopts, self.grammar_content, self.wildcards_obj, self.extranetwork_maps_obj, ), interrupted=True, ) def test_host_break_eol(self): self.process( PromptPair( "test1 BREAK test2", "", ), PromptPair("test1\ntest2", ""), ppp=PromptPostProcessor( self.ppp_logger, self.interrupt, { **self.def_env_info, "ppp_config": {"hosts": {"tests": {"break": "eol"}}}, }, self.defopts, self.grammar_content, self.wildcards_obj, self.extranetwork_maps_obj, ), ) def test_host_break_comma(self): self.process( PromptPair( "test1 BREAK test2", "", ), PromptPair("test1, test2", ""), ppp=PromptPostProcessor( self.ppp_logger, self.interrupt, { **self.def_env_info, "ppp_config": {"hosts": {"tests": {"break": "comma"}}}, }, self.defopts, self.grammar_content, self.wildcards_obj, self.extranetwork_maps_obj, ), ) def test_host_break_remove(self): self.process( PromptPair( "test1 BREAK test2", "", ), PromptPair("test1 test2", ""), ppp=PromptPostProcessor( self.ppp_logger, self.interrupt, { **self.def_env_info, "ppp_config": {"hosts": {"tests": {"break": "remove"}}}, }, self.defopts, self.grammar_content, self.wildcards_obj, self.extranetwork_maps_obj, ), ) def test_host_break_error(self): self.process( PromptPair( "test1 BREAK test2", "", ), PromptPair("", ""), ppp=PromptPostProcessor( self.ppp_logger, self.interrupt, { **self.def_env_info, "ppp_config": {"hosts": {"tests": {"break": "error"}}}, }, self.defopts, self.grammar_content, self.wildcards_obj, self.extranetwork_maps_obj, ), interrupted=True, ) # Model variants tests def test_variants(self): self.process( PromptPair( "test1test2test3test4", "", ), PromptPair("test1test2", ""), ppp=PromptPostProcessor( self.ppp_logger, self.interrupt, { **self.def_env_info, "model_filename": "./webui/models/Stable-diffusion/testmodel.safetensors", "ppp_config": { "models": { "sd1": { "variants": { "test3": {"find_in_filename": "testmodel"}, "sdxl": {"find_in_filename": "testmodel"}, } }, "sdxl": { "variants": { "test1": {"find_in_filename": "testmodel"}, "test2": {"find_in_filename": "testmodel"}, } }, "invalid": { "variants": { "test4": {"find_in_filename": "testmodel"}, } }, } }, }, { **self.defopts, "on_warning": PromptPostProcessor.ONWARNING_CHOICES.warn.value, }, self.grammar_content, self.wildcards_obj, self.extranetwork_maps_obj, ), ) # ComfyUI tests def test_comfyui_attention(self): # attention conversion self.process( PromptPair("(test1) (test2:1.5) [test3] [(test4)]", ""), PromptPair("(test1) (test2:1.5) (test3:0.9) (test4:0.99)", ""), ppp="comfyui", ) # Performance tests def test_parser_performance_simple_simpleparser( self, ): # performance test with a large prompt without new constructs large_prompt = ", ".join( ["(this:1.2) is a [test] using a [simple|low complexity] prompt with "] * 15 ) self.process( PromptPair(large_prompt, ""), ppp="nocup", ) def test_parser_performance_simple_fullparser( self, ): # performance test with a large prompt without new constructs but using full parser # we trick it to use the full parser by including some characters large_prompt = "{__${x:}}" + ", ".join( ["(this:1.2) is a [test] using a [simple|low complexity] prompt with "] * 15 ) self.process( PromptPair(large_prompt, ""), ppp="nocup", ) def test_parser_performance_complex_fullparser( self, ): # performance test with a large prompt with new constructs (full parser) large_prompt = ", ".join(["__yaml/wildcard1__, (__yaml/wildcard2__), __yaml/wildcard3__, {one|two|three}"] * 15) self.process( PromptPair(large_prompt, ""), ppp="nocup", ) # the following tests are performance tests with only one kind of the old constructs # same number of constructs and approximately the same full length def test_parser_performance_simple_attention(self): # performance test with only attention large_prompt = ", ".join(["(one:1.2) two (three) four [five] six"] * 20) self.process( PromptPair(large_prompt, ""), ppp="nocup", ) def test_parser_performance_simple_schedules(self): # performance test with only schedules large_prompt = ", ".join(["[one:1:0.5] two [three:0.8] four [five:5:0.2] six"] * 20) self.process( PromptPair(large_prompt, ""), ppp="nocup", ) def test_parser_performance_simple_alternation(self): # performance test with only alternation large_prompt = ", ".join(["[one|1] two [three|3] four [five|5] six"] * 20) self.process( PromptPair(large_prompt, ""), ppp="nocup", ) def test_parser_performance_simple_extranetwork(self): # performance test with only extra networks large_prompt = ", ".join([" two four six"] * 20) self.process( PromptPair(large_prompt, ""), ppp="nocup", ) # Variable-vs-variable comparison tests def test_cmd_if_var_vs_var_eq(self): # var eq var: both set to same value, if-branch taken self.process( PromptPair( "hellohelloYESNO", "", ), PromptPair("YES", ""), ) def test_cmd_if_var_vs_var_ne(self): # var ne var: different values, ne condition true self.process( PromptPair( "appleorangeYESNO", "", ), PromptPair("YES", ""), ) def test_cmd_if_var_vs_var_contains(self): # var contains var: var1 contains var2's value self.process( PromptPair( "hello worldhelloYESNO", "", ), PromptPair("YES", ""), ) def test_cmd_if_var_vs_var_not_contains(self): # var not contains var: var1 does not contain var2's value self.process( PromptPair( "hello worldgoodbyeYESNO", "", ), PromptPair("YES", ""), ) # NaN/undefined variable integer comparison tests def test_cmd_if_undefined_var_int_compare_warn(self): # undefined var integer compare with on_warning=warn self.process( PromptPair( "YESNO", "", ), PromptPair("NO", ""), ppp=PromptPostProcessor( self.ppp_logger, self.interrupt, self.def_env_info, {**self.defopts, "on_warning": PromptPostProcessor.ONWARNING_CHOICES.warn.value}, self.grammar_content, self.wildcards_obj, self.extranetwork_maps_obj, ), ) def test_cmd_if_undefined_var_int_compare_stop(self): # undefined var integer compare with on_warning=stop self.process( PromptPair( "YESNO", "", ), PromptPair("", ""), interrupted=True, ) def test_cmd_if_nonnumeric_var_int_compare_warn(self): # non-numeric var integer compare with on_warning=warn self.process( PromptPair( "abcYESNO", "", ), PromptPair("NO", ""), ppp=PromptPostProcessor( self.ppp_logger, self.interrupt, self.def_env_info, {**self.defopts, "on_warning": PromptPostProcessor.ONWARNING_CHOICES.warn.value}, self.grammar_content, self.wildcards_obj, self.extranetwork_maps_obj, ), ) def test_cmd_if_nonnumeric_var_int_compare_stop(self): # non-numeric var integer compare with on_warning=stop self.process( PromptPair( "abcYESNO", "", ), PromptPair("", ""), interrupted=True, ) def test_cmd_if_empty_var_int_compare(self): # empty string var integer compare with on_warning=warn self.process( PromptPair( "YESNO", "", ), PromptPair("NO", ""), ppp=PromptPostProcessor( self.ppp_logger, self.interrupt, self.def_env_info, {**self.defopts, "on_warning": PromptPostProcessor.ONWARNING_CHOICES.warn.value}, self.grammar_content, self.wildcards_obj, self.extranetwork_maps_obj, ), ) if __name__ == "__main__": unittest.main()