def test_attributes(self): # ARRANGE # cases = [ NameAndValue( 'plain string', 'the plain string' ), NameAndValue( 'to-string object', str_constructor.FormatPositional('template {}', 'value') ), ] for string_is_line_ended in [False, True]: for case in cases: with self.subTest(string_is_line_ended=string_is_line_ended, string=case.name): detail = sut.PreFormattedStringDetail(case.value, string_is_line_ended) # ACT & ASSERT # self.assertIs(case.value, detail.object_with_to_string, 'object_with_to_string') self.assertIs(string_is_line_ended, detail.string_is_line_ended, 'string_is_line_ended')
def runTest(self): # ARRANGE # cases = [ NameAndValue( 'pre sds', ( RelOptionType.REL_HDS_CASE, ValidationAssertions.pre_sds_fails__w_any_msg(), ), ), NameAndValue( 'post sds', ( RelOptionType.REL_ACT, ValidationAssertions.post_sds_fails__w_any_msg(), ), ), ] for case in cases: with self.subTest(case.name): CHECKER.check__w_source_variants( self, command_line( 'program', program_arguments.existing_path( path_arguments.RelOptPathArgument('name', case.value[0]), ) ).as_str, Arrangement.phase_agnostic(), MultiSourceExpectation.phase_agnostic( validation=case.value[1], ), )
def test_succeed(self): # ARRANGE # existing_dir = Dir.empty('a-dir') cwd_contents = DirContents([existing_dir]) cases = [ NameAndValue( 'existing dir', existing_dir.name_as_path, ), NameAndValue( 'non-existing dir', existing_dir.name_as_path / 'non-existing', ), NameAndValue( 'non-existing dir / multiple non-existing path components', existing_dir.name_as_path / 'non-existing-1' / 'non-existing-2', ), ] with tmp_dir.tmp_dir_as_cwd(cwd_contents): for case in cases: with self.subTest(case.name): path = case.value # ACT # sut.ensure_directory_exists(path) # ASSERT # self.assertTrue(path.is_dir())
def cases() -> Sequence[NameAndValue[List[FileSystemElement]]]: return [ NameAndValue( 'empty', [], ), NameAndValue( 'single regular file', [File.empty('an-empty-file')], ), NameAndValue( 'single empty dir', [Dir.empty('an-empty-dir')], ), NameAndValue('directory with contents - one level', [ File.empty('an-empty-file'), Dir('non-empty-dir', [ File.empty('file-in-dir'), Dir.empty('dir-in-dir'), ]), ]), NameAndValue('directory with contents - multiple levels', [ Dir('non-empty-dir-1', [File.empty('file-in-dir-1')]), Dir('non-empty-dir-2', [ File.empty('file-in-dir-2'), Dir('non-empty-dir-2-1', [ File.empty('file-in-dir-2-1'), Dir.empty('dir-in-dir-2-1'), ]) ]), ]), ]
def test_instruction_validation_step(self): # ARRANGE # with tmp_dir.tmp_dir() as root_dir: factory = sut.PhaseTmpFileSpaceFactory(root_dir) cases = [ NameAndValue(phase_identifier.BEFORE_ASSERT.identifier, (phase_identifier.BEFORE_ASSERT, 1)), NameAndValue(phase_identifier.CLEANUP.identifier, (phase_identifier.CLEANUP, 2)), NameAndValue( phase_identifier.SETUP.identifier, (phase_identifier.SETUP, 2), ), ] for case in cases: with self.subTest(case.name): expected_dir = ( root_dir / sut.PhaseTmpFileSpaceFactory.VALIDATION_SUB_DIR / _expected_phase_dir(case.value[0]) / _expected_instruction_dir(case.value[1])) # ACT & ASSERT # _check( self, expected_dir, factory.instruction__validation( case.value[0], case.value[1]), )
def test_that_symbols_from_arrangement_exist_in_environment(self): symbol = StringConstantSymbolContext('symbol_name', 'the symbol value') symbol_table_of_arrangement = symbol.symbol_table expected_symbol_table = symbol.symbol_table expectation = asrt_sym.equals_symbol_table(expected_symbol_table) sdv_that_checks_symbols = MatcherSdvThatAssertsThatSymbolsAreAsExpected(self, expectation) cases = [ NameAndValue('arrangement without tcds', arrangement_wo_tcds( symbol_table_of_arrangement) ), NameAndValue('arrangement with tcds', arrangement_w_tcds( symbols=symbol_table_of_arrangement) ), ] for arrangement in cases: with self.subTest(arrangement.name): self._check___single_and_multi( utils.single_line_arguments(), ARBITRARY_MODEL, _constant_line_matcher_type_parser_of_matcher_sdv(sdv_that_checks_symbols), arrangement.value, Expectation(), )
def test_set_value_that_references_an_env_var(self): # ARRANGE # existing_env_var = NameAndValue('MY_VAR', 'MY_VAL') defined_env_var = NameAndValue('name', existing_env_var.value) environ__before = { existing_env_var.name: existing_env_var.value, } environ__after = { defined_env_var.name: defined_env_var.value, existing_env_var.name: existing_env_var.value, } # ACT & ASSERT # CHECKER.check__abs_stx__std_layouts_and_source_variants( self, SetVariableArgumentsAbsStx.of_str(defined_env_var.name, env_var_ref_syntax( existing_env_var.name), phase_spec=None), arrangement=Arrangement.setup_phase_aware( process_execution_settings=ProcessExecutionSettings. with_environ(environ__before), ), expectation=MultiSourceExpectation.setup_phase_aware( main_side_effect_on_environment_variables=asrt.equals( environ__after), ))
def test_that_value_set_in_first_case_does_not_leak_to_second_case(self): # ARRANGE # containing_suite_file = File('test.suite', SUITE_WITH_CASES) suite_and_case_files = DirContents([ containing_suite_file, CASE_1_FILE, CASE_2_FILE, ]) case_processors = [ NameAndValue('processor_that_should_not_pollute_current_process', processors.new_processor_that_should_not_pollute_current_process), NameAndValue('processor_that_is_allowed_to_pollute_current_process', processors.new_processor_that_is_allowed_to_pollute_current_process), ] with tmp_dir(suite_and_case_files) as tmp_dir_path: suite_file_path = tmp_dir_path / containing_suite_file.file_name for case_processor_case in case_processors: with self.subTest(case_processor_case.name): registry = tr.Registry() processor = new_processor_with_no_env_vars(registry, case_processor_case.value) # ACT # return_value = processor.process(suite_file_path, StringStdOutFiles().reporting_environment) # ASSERT # self.assertEqual(ExecutionTracingRootSuiteReporter.VALID_SUITE_EXIT_CODE, return_value, 'Sanity check of result indicator') self.assertFalse(registry.observation)
def runTest(self): # ARRANGE # var_to_unset = NameAndValue('var_to_unset', 'value of var to unset') other_var = NameAndValue('other_var', 'val of other var') instruction_argument = UnsetVariableArgumentsAbsStx.of_str( var_to_unset.name, phase_spec=None) vars_from_defaults_getter = [var_to_unset, other_var] environ_wo_var_to_unset = NameAndValue.as_dict([other_var]) def get_default_environ() -> Dict[str, str]: return NameAndValue.as_dict(vars_from_defaults_getter) def mk_arrangement(): return self.conf.arrangement( environ=None, default_environ_getter=get_default_environ, ) # ACT & ASSERT # self.conf.instruction_checker.check_parsing__abs_stx( self, self.conf.parser(), instruction_argument, mk_arrangement, self.conf.expect_success(instruction_settings=asrt_is.matches( environ=asrt.equals(environ_wo_var_to_unset))))
def runTest(self): # ARRANGE # arg_pos_option_cases = [ NameAndValue( 'default', args.PathArgumentPositionDefault(), ), NameAndValue( 'last', args.PathArgumentPositionLast(), ), ] # ACT && ASSERT # for arg_pos_option_case in arg_pos_option_cases: with self.subTest(arg_pos_option_case.name): _check( self, arg_pos_option_case.value, command_line_arguments_cases=[ [], ['one'], ['one', 'two'], ], expected_arg_list=expected_arguments_to_program__last, )
def test_raise_exception_WHEN_invalid_assertion_variant(self): parser = sut.parsers(must_be_on_current_line=True).full cases = [ NameAndValue( 'Matcher is missing', args.complete_arguments_constructor( InvalidAssertionVariantArgumentsConstructor('')), ), NameAndValue( 'Matcher has invalid syntax', args.complete_arguments_constructor( InvalidAssertionVariantArgumentsConstructor( NOT_A_VALID_SYMBOL_NAME_NOR_PRIMITIVE_GRAMMAR_ELEMENT_NAME )), ), ] for case in cases: for expectation_type in ExpectationType: etc = pfh_expectation_type_config(expectation_type) instruction_arguments = case.value.apply(etc) source = remaining_source(instruction_arguments) with self.subTest(case_name=case.name, expectation_type=str(expectation_type)): with self.assertRaises( SingleInstructionInvalidArgumentException): parser.parse(source)
def _simple_expressions( ) -> Sequence[NameAndValue[grammar.Primitive[FilesMatcherSdv]]]: ret_val = [ NameAndValue( config.EMPTINESS_CHECK_ARGUMENT, grammar.Primitive(_parse_empty_check, documentation.EmptyDoc())), NameAndValue( config.MATCHES_ARGUMENT, grammar.Primitive(_parse_matches, documentation.MatchesDoc())), ] quantification_setup = parse_quantified_matcher.GrammarSetup( quant_over_files.ELEMENT_SETUP, parse_file_matcher.parsers().simple, ) ret_val += quantification_setup.quantification_grammar_expressions() ret_val += [ NameAndValue( config.NUM_FILES_CHECK_ARGUMENT, grammar.Primitive(num_files.parser().parse, documentation.NumFilesDoc())), NameAndValue( option_syntax.option_syntax(config.SELECTION_OPTION.name), grammar.Primitive(_parse_selection, documentation.SelectionDoc())), NameAndValue(option_syntax.option_syntax(config.PRUNE_OPTION.name), grammar.Primitive(_parse_prune, documentation.PruneDoc())), ] return ret_val
def validation_pre_sds_should_fail_when_limits_are_integer_out_of_range( ) -> Sequence[NameAndValue[SingleCaseGenerator]]: out_of_range_int = '-1' in_range_int = '0' arguments_cases = [ NameAndValue( 'min', DepthArgs(min_depth=out_of_range_int), ), NameAndValue( 'max', DepthArgs(max_depth=out_of_range_int), ), NameAndValue( 'min & max - min out of range', DepthArgs( min_depth=out_of_range_int, max_depth=in_range_int, ), ), NameAndValue( 'min & max - max out of range', DepthArgs( min_depth=in_range_int, max_depth=out_of_range_int, ), ), ] return [ NameAndValue( arguments_case.name, _ValidationPreSdsShouldFailWhenLimitsAreIntegerOutOfRange( arguments_case.value)) for arguments_case in arguments_cases ]
def cases_for(primitive_expr: str, the_operator: str) -> List[NameAndValue[str]]: sf = StringFormatter({ 'primitive_expr': primitive_expr, 'operator': the_operator, 'non_expr': ast.NOT_A_PRIMITIVE_EXPR_NAME_AND_NOT_A_VALID_SYMBOL_NAME, }) return [ NameAndValue( 'operator not followed by expression', sf.format('{primitive_expr} {operator}'), ), NameAndValue( 'operator followed by non-expression', sf.format('{primitive_expr} {operator} {non_expr}'), ), NameAndValue( 'operator followed by non-expression/two operators', sf.format('{primitive_expr} {operator} {primitive_expr} {operator} {non_expr}'), ), NameAndValue( '( at start of expr: missing )', sf.format('( {primitive_expr} {operator} {primitive_expr} '), ), NameAndValue( '( in middle of expr: missing )', sf.format('( {primitive_expr} {operator} ( {primitive_expr} '), ), ]
def test(self): for must_be_on_current_line in [False, True]: for grammar in GRAMMARS: parser = self.parser_maker.make(grammar.value, must_be_on_current_line) cases = [ NameAndValue( 'source is just space', remaining_source(' '), ), NameAndValue( 'first token quoted/soft', remaining_source( str(surrounded_by_soft_quotes('token'))), ), NameAndValue( 'first token quoted/hard', remaining_source( str(surrounded_by_hard_quotes('token'))), ), NameAndValue( 'missing )', remaining_source('( {primitive} '.format( primitive=ast.PRIMITIVE_SANS_ARG)), ), ] for case in cases: with self.subTest( grammar=grammar.name, must_be_on_current_line=must_be_on_current_line, case_name=case.name): with self.assertRaises( SingleInstructionInvalidArgumentException): parser.parse(case.value)
def test_WHEN_validation_of_every_assertion_part_is_successful_THEN_validation_SHOULD_succeed(self): # ARRANGE # cases = [ NameAndValue( 'empty list of assertion_parts', sut.SequenceOfCooperativeAssertionParts([]) ), NameAndValue( 'validation of every assertion_part is successful', sut.SequenceOfCooperativeAssertionParts([PartForValidation(SdvValidatorThat()), PartForValidation(SdvValidatorThat())]) ), ] validation_environment = self.environment.path_resolving_environment_pre_or_post_sds for case in cases: with self.subTest(case=case.name): sequence_part = case.value with self.subTest(name='pre sds validation'): # ACT # actual = sequence_part.validator.validate_pre_sds_if_applicable(validation_environment) # ASSERT # self.assertIsNone(actual) with self.subTest(name='post setup validation'): # ACT # actual = sequence_part.validator.validate_post_sds_if_applicable(validation_environment) # ASSERT # self.assertIsNone(actual)
def test_fail(self): symbol_name = 'the_symbol_name' for grammar_description, grammar in GRAMMARS: cases = [ NameAndValue( 'symbol name is quoted', remaining_source( str(surrounded_by_hard_quotes(symbol_name))), ), NameAndValue( 'symbol reference syntax with invalid symbol name character: space', remaining_source( symbol_reference_syntax_for_name('the symbol')), ), NameAndValue( 'symbol reference syntax with invalid symbol name character: &', remaining_source( symbol_reference_syntax_for_name('the&symbol')), ), ] for case in cases: with self.subTest(grammar=grammar_description, case_name=case.name): parser = self.parser_maker.make( grammar, must_be_on_current_line=True) with self.assertRaises( SingleInstructionInvalidArgumentException): parser.parse(case.value)
def test_file_exists(self): # ARRANGE # file_name = 'existing-file' instruction_argument_constructor = ArgumentsConstructorWithFileMatcher(file_name) cases_with_existing_file_of_different_types = [ NameAndValue( 'dir', DirContents([Dir.empty(file_name)])), NameAndValue( 'regular file', DirContents([File.empty(file_name)])), NameAndValue( 'sym-link', DirContents( [Dir.empty('directory'), Link(file_name, 'directory')]) ), NameAndValue( 'broken sym-link', DirContents( [Link(file_name, 'non-existing-target-file')]) ), ] self.checker.check_multiple_cases_with_rel_opt_variants_and_expectation_type_variants( cases_with_existing_file_of_different_types, instruction_argument_constructor, main_result_for_positive_expectation=PassOrFail.PASS, )
def test_invalid_formatting(self): cases = [ NameAndValue( 'Missing end quote (soft)', MISSING_END_QUOTE__SOFT ), NameAndValue( 'Missing end quote (hard)', MISSING_END_QUOTE__HARD ), NameAndValue( 'Superfluous arguments', CustomAbsStx.of_str('x superfluous-arg') ), ] for case in cases: syntax = DefineSymbolWMandatoryValue( A_VALID_SYMBOL_NAME, ValueType.STRING, case.value, ) PARSE_CHECKER.check_invalid_syntax__abs_stx( self, syntax, sub_test_identifiers={ 'case': case.name } )
def test_fail_WHEN_invalid_components_of_invalid_type(self): # ARRANGE # assertion = sut.matches_node() cases = [ NameAndValue('invalid type of header', sut.Node(None, None, [], []) ), NameAndValue('invalid type of detail', sut.Node('header', None, ['not a Detail'], []) ), NameAndValue('invalid type of child', sut.Node('header', None, [], ['not a Node']) ), ] for case in cases: with self.subTest(case.name): # ACT & ASSERT # assert_that_assertion_fails(assertion, case.value)
def test_raise_exception_when_syntax_is_invalid(self): source_cases = [ NameAndValue( 'empty - missing all arguments', CustomAbsStx.empty(), ), NameAndValue( 'superfluous arguments', SequenceAbsStx.followed_by_superfluous( InstructionArgumentsAbsStx( DefaultRelPathAbsStx('actual.txt'), StringMatcherSymbolReferenceAbsStx('STRING_MATCHER'), ) ), ), NameAndValue( 'invalid matcher', SequenceAbsStx.followed_by_superfluous( InstructionArgumentsAbsStx( DefaultRelPathAbsStx('actual.txt'), StringMatcherSymbolReferenceAbsStx( NOT_A_PRIMITIVE_EXPR_NAME_AND_NOT_A_VALID_SYMBOL_NAME ), ) ), ) ] for source_case in source_cases: with self.subTest(source_case.name): PARSE_CHECKER.check_invalid_syntax__src_var_consume_last_line_abs_stx( self, source_case.value, )
def test_equals(self): cases = [ NameAndValue('empty tree', sut.Node.empty(HEADER_e, None) ), NameAndValue('tree with data', sut.Node.empty(HEADER_e, 'some data') ), NameAndValue('tree with details', sut.Node.leaf(HEADER_e, None, [STRING_DETAIL_e]) ), NameAndValue('tree with children', sut.Node(HEADER_e, 'root data', [], [sut.Node.empty(HEADER_e, 'child data')]) ), NameAndValue('tree with details and children', sut.Node(HEADER_e, 'root data', [STRING_DETAIL_e], [sut.Node.empty(HEADER_e, 'child data')]) ), ] for case in cases: with self.subTest(case.name): assertion = sut.equals_node(case.value) actual = copy.deepcopy(case.value) assertion.apply_without_message(self, actual)
def runTest(self): # ARRANGE # cases = [ NameAndValue( 'empty', '', ), NameAndValue( 'single line', 'abc', ), NameAndValue( 'multi line', '1st\n2nd\n', ), ] for case in cases: with self.subTest(case.name): source_constructor = _SourceConstructor(case.value) expectation = multi_obj_assertions.ExpectationOnUnFrozenAndFrozen.equals( ''.join(source_constructor.contents), may_depend_on_external_resources=asrt.equals(False), frozen_may_depend_on_external_resources=asrt.equals(False), ) assertion = multi_obj_assertions.assertion_of_sequence_permutations( expectation) # ACT & ASSERT # assertion.apply_without_message( self, multi_obj_assertions.SourceConstructors.of_common( source_constructor), )
def test_success_WHEN_actual_is_valid_node(self): # ARRANGE # assertion = sut.matches_node() cases = [ NameAndValue('no details and no children', sut.Node('header', None, [], []) ), NameAndValue('single valid detail', sut.Node('header', None, [StringDetail('the string')], []) ), NameAndValue('single valid child', sut.Node('header', None, [], [sut.Node('child node', None, [], [])]) ), ] for case in cases: with self.subTest(case.name): # ACT & ASSERT # assertion.apply_without_message(self, case.value)
def runTest(self): cases = [ NameAndValue( 'missing int matcher', InstructionArguments( ProgramOfSymbolReferenceAbsStx('PROGRAM_SYMBOL'), CustomIntegerMatcherAbsStx.empty(), ), ), NameAndValue( 'missing program', InstructionArguments( CustomPgmAndArgsAbsStx.empty(), IntegerMatcherSymbolReferenceAbsStx('INT_MATCHER_SYMBOL'), ), ), NameAndValue( 'superfluous arguments', InstructionArguments( ProgramOfSymbolReferenceAbsStx('PROGRAM_SYMBOL'), im_abs_stx. symbol_reference_followed_by_superfluous_string_on_same_line( 'INT_MATCHER_SYMBOL'), ), ), ] for case in cases: with self.subTest(case.name): PARSE_CHECKER.check_invalid_syntax__abs_stx( self, case.value, )
def test_fail__must_be_on_current_line(self): cases = [ NameAndValue( 'token is not the name of a primitive expression', [ast.NOT_A_PRIMITIVE_EXPR_NAME_AND_NOT_A_VALID_SYMBOL_NAME], ), NameAndValue( 'token is the name of a primitive expression, but it is quoted/soft', [str(surrounded_by_soft_quotes(ast.PRIMITIVE_SANS_ARG))], ), NameAndValue( 'token is the name of a primitive expression, but it is quoted/hard', [str(surrounded_by_hard_quotes(ast.PRIMITIVE_SANS_ARG))], ), NameAndValue( 'token is the name of a primitive expression, but it is on the next line', [ast.PRIMITIVE_SANS_ARG], ), ] # ACT & ASSERT # parse_check.check_fail__must_be_on_current_line( self, self.parser_maker, GRAMMARS, cases, )
def test_failing_parse(self): cases = [ NameAndValue( 'single quoted argument', str(surrounded_by_hard_quotes(NAME_MATCHER_NAME)), ), NameAndValue( 'non-selector name that is not a valid symbol name', NOT_A_VALID_SYMBOL_NAME, ), NameAndValue( 'missing matcher', '', ), ] # ARRANGE # defined_name = 'defined_name' parser = sut.EmbryoParser() for case in cases: with self.subTest(name=case.name): source = single_line_source( src2(ValueType.FILE_MATCHER, defined_name, case.value), ) with self.assertRaises( SingleInstructionInvalidArgumentException): # ACT & ASSERT # parser.parse(ARBITRARY_FS_LOCATION_INFO, source)
def test_fail(self): for grammar_description, grammar in GRAMMARS: for prefix_operator, mk_prefix_expr in self.PREFIX_OPERATORS: cases = [ NameAndValue( 'no source after operator', remaining_source(prefix_operator), ), NameAndValue( 'operator followed by non-expression', remaining_source('{op} {non_expr}'.format( op=prefix_operator, non_expr=str( surrounded_by_soft_quotes( ast.PRIMITIVE_SANS_ARG)))), ), ] for case in cases: with self.subTest(grammar=grammar_description, prefix_operator=prefix_operator, case_name=case.name): parser = self.parser_maker.make( grammar, must_be_on_current_line=True) with self.assertRaises( SingleInstructionInvalidArgumentException): parser.parse(case.value)
class ValueWSymRefsAndVarRefs: REFERENCED_VAR_1 = NameAndValue('existing_var_1', '<val of existing 1>') REFERENCED_VAR_2 = NameAndValue('existing_var_2', '<val of existing 2>') REFERENCED_VAR__ALL = [REFERENCED_VAR_1, REFERENCED_VAR_2] VALUE_PATTERN = '{}between{}' VALUE_W_VAR_REFS = VALUE_PATTERN.format( env_var_ref_syntax(REFERENCED_VAR_1.name), env_var_ref_syntax(REFERENCED_VAR_2.name), ) VALUE_WO_VAR_REFS = VALUE_PATTERN.format( REFERENCED_VAR_1.value, REFERENCED_VAR_2.value, ) POS_OF_END_OF_VAR_REF_1 = end_of_1st_var_ref(VALUE_W_VAR_REFS) SYM_REF_PART_1 = StringConstantSymbolContext( 'VAL_SYM_1', VALUE_W_VAR_REFS[:4], default_restrictions=asrt_w_str_rend_rest.is__w_str_rendering(), ) CONST_STR_PART_2 = VALUE_W_VAR_REFS[4:(POS_OF_END_OF_VAR_REF_1 + 5)] SYM_REF_PART_3 = StringConstantSymbolContext( 'VAL_SYM_3', VALUE_W_VAR_REFS[(POS_OF_END_OF_VAR_REF_1 + 5):], default_restrictions=asrt_w_str_rend_rest.is__w_str_rendering(), ) SYMBOL_CONTEXTS = (SYM_REF_PART_1, SYM_REF_PART_3) STRING_ABS_STX = str_abs_stx.StringConcatAbsStx([ SYM_REF_PART_1.abstract_syntax, str_abs_stx.StringLiteralAbsStx(CONST_STR_PART_2), SYM_REF_PART_3.abstract_syntax, ])
def runTest(self): exe_file = fs.python_executable_file('program-name', py_programs.exit_with(0)) cases = [ NameAndValue( 'pre sds', ( RelOptionType.REL_HDS_CASE, self.conf.expect_failing_validation_pre_sds(), ), ), NameAndValue( 'post sds', ( RelOptionType.REL_ACT, self.conf.expect_hard_error_of_main__any(), ), ), ] for case in cases: with self.subTest(case.name): arguments = sys_cmd.command_line( exe_file.name, program_arguments.existing_file( path_arguments.RelOptPathArgument( 'non-existing', case.value[0])), ) with tmp_dir_in_path_with_files(fs.DirContents([exe_file ])) as env: self.conf.run_single_line_test_with_source_variants_and_source_check( self, arguments.as_str, self.conf.arrangement(environ=env), case.value[1], )