def _compile_grammar(self): # type: () -> ParserElement """ Takes the individual grammars from each registered directive and compiles them into a full test fixture grammar whose callback methods are the bound methods on this class instance. :return: The full PyParsing grammar for test fixture files. """ grammars = [ (LineEnd().suppress()).setParseAction( functools.partial(self._finalize_test_case) ) ] # directives for directive_class in get_all_directives(): grammars.append( LineStart() + directive_class.get_full_grammar().setParseAction( functools.partial(self._ingest_directive, directive_class) ) + LineEnd() ) return StringStart() + OneOrMore(MatchFirst(grammars)) + StringEnd()
def _ingest_directive(self, directive_class, active_string, location, parse_result): """ A callback that ingests a particular matched directive. Called by PyParsing when processing the grammar. :param directive_class: The matched directive class (not an instance) :type directive_class: type :param active_string: The contents of the fixture file :type active_string: union[str, unicode] :param location: The file location of the current parsing activity :param parse_result: The resulting PyParsing parsing object """ self._line_number = get_parse_line_number(location, active_string) source = get_parse_line(location, active_string) self._fixture_source.append(source) if self._working_test_case and not self._working_test_case_line_number: self._working_test_case_line_number = self._line_number - 1 test_case_target = self._working_test_case if parse_result.is_global: # This is actually a global directive, so we're working on those, not on a test case test_case_target = self._global_directives else: self._working_test_case_source.append(source) if parse_result.action: action_path = '{}.{}'.format(parse_result.action, parse_result.action_index or '0') if 'actions' not in test_case_target: test_case_target['actions'] = [] if action_path not in test_case_target: if not parse_result.is_global: # Global directives don't need a list of actions to run; only test cases do test_case_target['actions'].append(action_path) if self._working_action_case: # We're done parsing the previous action case and starting a new one for dc in get_all_directives(): dc().post_parse_test_case_action( self._working_action_case, test_case_target) test_case_target[action_path] = {} action_case_target = test_case_target[action_path] self._working_action_case = action_case_target else: # This branch handles non-action directives, such as comments, test names, test descriptions, non-action # time-freezing, non-action mocking, etc. action_case_target = test_case_target directive_class().ingest_from_parsed_test_fixture( action_case_target, test_case_target, parse_result, self._fixture_file_name, self._line_number, )
def setUpClass(cls): """ This method is invoked one time before the test plan (all the fixtures defined in ``fixture_path``) or any of the normal tests in your test case are run. """ cls._all_directives = get_all_directives() super(ServicePlanTestCase, cls).setUpClass()
def setup_class(cls): # type: () -> None """ This method is invoked one time before the test plan (all the fixtures defined in ``fixture_path``) or any of the normal tests in your test case are run. """ cls._all_directives = get_all_directives() super(ServicePlanTestCase, cls).setup_class() if not hasattr(cls, '_test_fixture_setup_called'): setattr(cls, '_test_fixture_setup_called', {}) if not hasattr(cls, '_test_fixture_setup_succeeded'): setattr(cls, '_test_fixture_setup_succeeded', {})
'action': 'NAME', 'action_index': 'NUM', 'comment': 'PLAIN_LANGUAGE', 'data_type': "'{}'".format("' | '".join(get_all_data_type_names())), 'description': 'PLAIN_LANGUAGE', 'error_code': 'NAME', 'error_message': 'PLAIN_LANGUAGE', 'field_name': "HYPHENATED_NAME (HYPHENATED_NAME | '.')*", 'job_slot': "'context' | 'control'", 'name': 'NAME', 'reason': 'PLAIN_LANGUAGE', 'value': 'PLAIN_LANGUAGE', 'variable_name': 'ALPHANUM (ALPHANUM | [-_.{}])*', } for __directive_class in get_all_directives(): for k, v in six.iteritems( __directive_class.supplies_additional_grammar_types()): if k not in __base_types: __base_types[k] = v for k in sorted(__base_types.keys()): __doc__ += __wrap_documentation_line('{}: {}'.format( k, __base_types[k])) + '\n' __doc__ += '\n' for __directive_class in get_all_directives(): __doc__ += __wrap_documentation_line( __directive_class.name() + ': ' + repr(__directive_class()).strip()) + '\n'
def _finalize_test_case(self, active_string, location, _): # type: (six.text_type, int, Optional[ParseResults]) -> None """ Called by PyParsing at the end of each test case. :param active_string: The contents of the fixture file :param location: The file location of the current parsing activity """ self._fixture_source.append('') if self._working_action_case: # We're done parsing the test case and still need to wrap up the last action in the test case for dc in get_all_directives(): dc().post_parse_test_case_action( self._working_action_case, self._working_test_case or self._global_directives, ) self._working_action_case = {} if not self._working_test_case: # just a blank line before any test cases, probably after globals or an extra blank line between tests return self._working_test_case['line_number'] = self._working_test_case_line_number self._working_test_case_line_number = 0 self._working_test_case['fixture_name'] = self._fixture_name self._working_test_case['fixture_file_name'] = self._fixture_file_name self._working_test_case['source'] = self._working_test_case_source line_number = get_parse_line_number(location, active_string) if not self._working_test_case.get('name'): raise FixtureSyntaxError( '{}:{}: Test case without name'.format(self._fixture_file_name, line_number), file_name=self._fixture_file_name, line_number=line_number - 1, ) if not self._working_test_case.get('description'): raise FixtureSyntaxError( '{}:{}: Test case without description'.format(self._fixture_file_name, line_number), file_name=self._fixture_file_name, line_number=line_number - 1, ) if not self._working_test_case.get('actions') and not self._global_directives: raise FixtureSyntaxError( '{}:{}: Empty test case'.format(self._fixture_file_name, line_number), file_name=self._fixture_file_name, line_number=line_number - 1, ) if self._global_directives: # merge, but make sure current overlays global where there is conflict test_case = {} # type: TestCase for path in get_all_paths(self._global_directives, allow_blank=True): try: value = path_get(self._global_directives, path) path_put(test_case, path, copy.copy(value)) except (KeyError, IndexError): raise FixtureSyntaxError( 'Invalid path: `{}`'.format(path), file_name=self._fixture_file_name, line_number=line_number, ) for path in get_all_paths(self._working_test_case, allow_blank=True): try: path_put(test_case, path, path_get(self._working_test_case, path)) except (KeyError, IndexError): raise FixtureSyntaxError( 'Invalid path: `{}`'.format(path), file_name=self._fixture_file_name, line_number=line_number, ) for directive_class in get_all_directives(): directive_class().post_parse_test_case(test_case) else: for directive_class in get_all_directives(): directive_class().post_parse_test_case(self._working_test_case) test_case = copy.deepcopy(self._working_test_case) test_case['fixture_source'] = self._fixture_source self.test_cases.append(test_case) self._working_test_case.clear() self._working_test_case_source = []