예제 #1
0
def _compare_values(expected_val, actual_val, full_path=None, subset_lists=False):
    missing_keys = []
    extra_keys = []
    mismatching_keys = []
    mismatching_values = {}

    if isinstance(expected_val, dict) and isinstance(actual_val, dict):
        # Expected value is a dict, iterate recursively
        if expected_val:
            missing_sub_keys, mismatching_sub_keys, mismatching_sub_values, extra_sub_keys = _check_subset_dict(
                expected=expected_val,
                actual=actual_val,
                subset_lists=subset_lists,
                prefix=full_path,
            )
            missing_keys.extend(missing_sub_keys)
            mismatching_keys.extend(mismatching_sub_keys)
            mismatching_values.update(mismatching_sub_values)
            extra_keys.extend(extra_sub_keys)

        elif actual_val:
            # expected empty dict but got a populated one
            mismatching_keys.append(full_path)
            mismatching_values[full_path] = {
                'expected': expected_val,
                'actual': actual_val,
            }
            extra_keys.extend(get_all_paths(actual_val, current_path=full_path))

    elif isinstance(actual_val, list) and (subset_lists or isinstance(expected_val, list)):
        # Expected value is a list, iterate recursively
        if expected_val:
            missing_sub_keys, mismatching_sub_keys, mismatching_sub_values, extra_sub_keys = _check_subset_list(
                expected=expected_val,
                actual=actual_val,
                subset_lists=subset_lists,
                prefix=full_path,
            )
            missing_keys.extend(missing_sub_keys)
            mismatching_keys.extend(mismatching_sub_keys)
            mismatching_values.update(mismatching_sub_values)
            extra_keys.extend(extra_sub_keys)

        elif actual_val:
            # expected empty list but got a populated one
            mismatching_keys.append(full_path)
            mismatching_values[full_path] = {
                'expected': expected_val,
                'actual': actual_val,
            }
            extra_keys.extend(get_all_paths(actual_val, current_path=full_path))

    elif expected_val != actual_val:
        mismatching_keys.append(full_path)
        mismatching_values[full_path] = {
            'expected': expected_val,
            'actual': actual_val,
        }

    return sorted(missing_keys), sorted(mismatching_keys), mismatching_values, sorted(extra_keys)
예제 #2
0
def assert_not_expected(not_expected, actual, msg=None):
    """
    Assert that the given not_expected values are not in actual

    Note that this allows the keys to exist (be present) so long as they have different values.
    """
    (missing, mismatch, mismatch_values, extra) = _check_subset_dict(not_expected, actual, True)
    count = len(get_all_paths(not_expected))

    # For cases where we are sub-setting lists but the list actual is None, call mismatches good and count
    # them among the missing.
    for field, details in six.iteritems(mismatch_values):
        if details['actual'] is None and details['expected'] is not None:
            missing.append(field)

    if len(missing) != count:
        msg = make_error_msg_header(msg)
        msg = make_not_expected_error_message(
            set(get_all_paths(not_expected)) - set(missing),
            msg
        )

        msg += make_failed_comparision_error_message(
            'NOT expected',
            pprint.pformat(not_expected),
            pprint.pformat(actual),
        )
        raise AssertionError(msg)
예제 #3
0
def _check_subset_dict(expected, actual, subset_lists=False, prefix=None):
    """
    Contrasts `expected` and `actual` dicts and annotates missing/mismatching values.
    """
    missing_keys = []
    extra_keys = []
    mismatching_keys = []
    mismatching_values = {}

    if not isinstance(expected, dict):
        raise AssertionError('expected value is not a dict')

    if not isinstance(actual, dict):
        raise AssertionError('actual value is not a dict')

    for expected_key, expected_val in six.iteritems(expected):
        full_path = expected_key
        if '.' in full_path and '{' not in full_path:
            full_path = '{{{}}}'.format(full_path)
        if prefix is not None:
            full_path = '{}.{}'.format(prefix, full_path)

        if expected_key not in actual:
            for missing_sub_path in get_all_paths(expected_val):
                if missing_sub_path:
                    missing_keys.append('{}.{}'.format(full_path, missing_sub_path))
                else:
                    missing_keys.append(full_path)
            continue

        actual_val = actual[expected_key]
        missing_sub_keys, mismatching_sub_keys, mismatching_sub_values, extra_sub_keys = _compare_values(
            expected_val,
            actual_val,
            full_path,
            subset_lists=subset_lists
        )
        missing_keys.extend(missing_sub_keys)
        mismatching_keys.extend(mismatching_sub_keys)
        mismatching_values.update(mismatching_sub_values)
        extra_keys.extend(extra_sub_keys)
        continue

    # check for extra unexpected paths
    for actual_key, actual_val in six.iteritems(actual):
        full_path = actual_key
        if '.' in full_path and '{' not in full_path:
            full_path = '{{{}}}'.format(full_path)
        if prefix is not None:
            full_path = '{}.{}'.format(prefix, full_path)

        if actual_key not in expected:
            extra_keys.extend(get_all_paths(actual_val, current_path=full_path))
            continue

    return sorted(missing_keys), sorted(mismatching_keys), mismatching_values, sorted(extra_keys)
예제 #4
0
    def test_018_get_all_paths(self):
        path_list = [
            'foo.bar',
            'foo.{bar.baz}',
            'foo.{yea_bar.baz}.gar',
            'foo.aba_bar.0',
            'foo.sba_bar.0.baz',
            'foo.nu_bar.0.0.baz',
            'foo.ba_bar.2.baz',
            'foo.re_bar.{2}.baz',
            '{record_transaction.0}.inputs.foo',
            'transaction.metadata.references.0.reference_type',
            'transaction.metadata.references.0.reference_ids.0',
        ]

        out = {}  # type: Dict[six.text_type, Any]
        for path in path_list:
            path_put(out, path, 'blah_blah')

        self.assertEqual(sorted(path_list), sorted(get_all_paths(out)))
예제 #5
0
def assert_not_present(not_present, actual, msg=None):
    """
    Assert that none of the keys in not_present exist in actual
    """
    present = []
    for path in get_all_paths(not_present):
        try:
            path_get(actual, path)
            present.append(path)
        except (KeyError, IndexError):
            pass

    if present:
        msg = make_error_msg_header(msg, 'Not present')
        msg += make_failed_comparision_error_message(
            'SHOULD NOT be present',
            pprint.pformat(present),
            pprint.pformat(actual),
        )
        raise AssertionError(msg)
예제 #6
0
def assert_not_present(
    not_present,  # type: Union[Mapping, List, Tuple, AbstractSet]
    actual,  # type: Union[Mapping, List, Tuple, AbstractSet]
    msg=None,  # type: Optional[six.text_type]
):  # type: (...) -> None
    """
    Assert that none of the keys in not_present exist in actual
    """
    present = []  # type: List[six.text_type]
    for path in get_all_paths(not_present):
        try:
            path_get(actual, path)
            present.append(path)
        except (KeyError, IndexError):
            pass

    if present:
        msg = make_error_msg_header(msg, 'Not present')
        msg += make_failed_comparision_error_message(
            'SHOULD NOT be present',
            pprint.pformat(present),
            pprint.pformat(actual),
        )
        raise AssertionError(msg)
예제 #7
0
 def test_021_path_listing_for_empty_structures(self):
     self.assertEqual(get_all_paths({}), [])
     self.assertEqual(get_all_paths([]), [])
예제 #8
0
    def test_020_path_listing_for_complex_dict(self):
        data = {
            'foo': {
                'aba_bar': ['test'],
                'ba_bar': [{}, {
                    'baz': 'test'
                }, {}],
                'bar': 'test',
                'bar.baz': 'test',
                'nu_bar': [[{
                    'baz': 'test'
                }]],
                're_bar': {
                    '2': {
                        'baz': 'test'
                    }
                },
                'sba_bar': [{
                    'baz': 'test'
                }, [], {}],
                'yea_bar.baz': {
                    'gar': 'test'
                },
            },
            'record_transaction.0': {
                'inputs': {
                    'bar': [],
                    'foo': 'test',
                    're_bar': {},
                },
            },
            'transaction': {
                'metadata': {
                    'references': [
                        {
                            'reference_ids': ['blah_blah'],
                            'reference_type': 'blah_blah'
                        },
                        {},
                    ],
                },
            },
        }

        actual = get_all_paths(data, allow_blank=True)

        expected = [
            'foo.aba_bar.0',
            'foo.ba_bar.0',
            'foo.ba_bar.1.baz',
            'foo.ba_bar.2',
            'foo.bar',
            'foo.nu_bar.0.0.baz',
            'foo.re_bar.{2}.baz',
            'foo.sba_bar.0.baz',
            'foo.sba_bar.1',
            'foo.sba_bar.2',
            'foo.{bar.baz}',
            'foo.{yea_bar.baz}.gar',
            'transaction.metadata.references.0.reference_ids.0',
            'transaction.metadata.references.0.reference_type',
            'transaction.metadata.references.1',
            '{record_transaction.0}.inputs.bar',
            '{record_transaction.0}.inputs.foo',
            '{record_transaction.0}.inputs.re_bar',
        ]
        self.assertEqual(sorted(actual), sorted(expected))
예제 #9
0
    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 = []
예제 #10
0
def _compare_values(
    expected_val,  # type: Any
    actual_val,  # type: Any
    full_path=None,  # type: Optional[six.text_type]
    subset_lists=False,  # type: bool
):  # type: (...) -> CompareReturn
    missing_keys = []  # type: List[six.text_type]
    extra_keys = []  # type: List[six.text_type]
    mismatching_keys = []  # type: List[six.text_type]
    mismatching_values = {}  # type: Dict[six.text_type, Dict[six.text_type, Any]]

    if isinstance(expected_val, dict) and isinstance(actual_val, dict):
        # Expected value is a dict, iterate recursively
        if expected_val:
            missing_sub_keys, mismatching_sub_keys, mismatching_sub_values, extra_sub_keys = _check_subset_dict(
                expected=expected_val,
                actual=actual_val,
                subset_lists=subset_lists,
                prefix=full_path,
            )
            missing_keys.extend(missing_sub_keys)
            mismatching_keys.extend(mismatching_sub_keys)
            mismatching_values.update(mismatching_sub_values)
            extra_keys.extend(extra_sub_keys)

        elif actual_val:
            # expected empty dict but got a populated one
            mismatching_keys.append(full_path or '')
            mismatching_values[full_path or ''] = {
                'expected': expected_val,
                'actual': actual_val,
            }
            extra_keys.extend(get_all_paths(actual_val, current_path=full_path or ''))

    elif isinstance(actual_val, list) and (subset_lists or isinstance(expected_val, list)):
        # Expected value is a list, iterate recursively
        if expected_val:
            missing_sub_keys, mismatching_sub_keys, mismatching_sub_values, extra_sub_keys = _check_subset_list(
                expected=expected_val,
                actual=actual_val,
                subset_lists=subset_lists,
                prefix=full_path,
            )
            missing_keys.extend(missing_sub_keys)
            mismatching_keys.extend(mismatching_sub_keys)
            mismatching_values.update(mismatching_sub_values)
            extra_keys.extend(extra_sub_keys)

        elif actual_val:
            # expected empty list but got a populated one
            mismatching_keys.append(full_path or '')
            mismatching_values[full_path or ''] = {
                'expected': expected_val,
                'actual': actual_val,
            }
            extra_keys.extend(get_all_paths(actual_val, current_path=full_path or ''))

    elif expected_val != actual_val:
        mismatching_keys.append(full_path or '')
        mismatching_values[full_path or ''] = {
            'expected': expected_val,
            'actual': actual_val,
        }

    return sorted(missing_keys), sorted(mismatching_keys), mismatching_values, sorted(extra_keys)