def test_string_eq(self): context = ExecutionContext() eq_abc = jp.STR_EQ('abc') self.assertGoodResult(PathValue('', 'abc'), eq_abc, eq_abc(context, 'abc')) self.assertBadResult(PathValue('', 'abcd'), eq_abc, eq_abc(context, 'abcd'))
def test_string_ne(self): context = ExecutionContext() ne_abc = jp.STR_NE('abc') self.assertGoodResult(PathValue('', 'abcd'), ne_abc, ne_abc(context, 'abcd')) self.assertBadResult(PathValue('', 'abc'), ne_abc, ne_abc(context, 'abc'))
def test_collect_with_dict_subset(self): # See test_dict_subset_with_array_nodes for comparision. context = ExecutionContext() letters = {'a':'A', 'b':'B', 'c':'C'} numbers = {'a':1, 'b':2} both = [letters, numbers] source = {'items': both} letter_subset_pred = jp.DICT_SUBSET({'a':'A'}) path_pred = jp.PathPredicate('items', pred=letter_subset_pred) result = path_pred(context, source) mismatch_error = jp.TypeMismatchError( expect_type=(int, long, float), got_type=str, source=source, target_path=jp.PATH_SEP.join(['items', 'a']), path_value=PathValue('items[1]', numbers)) valid_result = letter_subset_pred(context, letters).clone_with_source( source=source, base_target_path='items', base_value_path='items[0]') self.assertEqual( # pylint: disable=bad-continuation jp.PathPredicateResultBuilder(source, path_pred) .add_result_candidate(PathValue('items[0]', letters), valid_result) .add_result_candidate(PathValue('items[1]', numbers), mismatch_error) .build(True), result)
def test_indirect_string(self): context = ExecutionContext(TEST='abc') eq_abc = jp.STR_EQ(lambda x: x['TEST']) self.assertGoodResult(PathValue('', 'abc'), eq_abc, eq_abc(context, 'abc')) self.assertBadResult(PathValue('', 'abcd'), eq_abc, eq_abc(context, 'abcd'))
def test_string_substr(self): substr_q = jp.STR_SUBSTR('q') substr_pqr = jp.STR_SUBSTR('pqr') self.assertGoodResult(PathValue('', 'pqr'), substr_q, substr_q('pqr')) self.assertGoodResult(PathValue('', 'rqp'), substr_q, substr_q('rqp')) self.assertGoodResult(PathValue('', 'pqr'), substr_pqr, substr_pqr('pqr')) self.assertBadResult(PathValue('', 'abc'), substr_q, substr_q('abc')) self.assertBadResult(PathValue('', 'xyz'), substr_q, substr_q('xyz'))
def test_path_found_multiple(self): source = {'outer': [_LETTER_DICT, _NUMBER_DICT]} pred = jp.PathPredicate(PATH_SEP.join(['outer', 'a'])) result = pred(source) self.assertEqual( _make_result(pred, None, source, [ PathValue(PATH_SEP.join(['outer[0]', 'a']), 'A'), PathValue(PATH_SEP.join(['outer[1]', 'a']), 1) ], []), result)
def test_dict_ne(self): letters = {'a': 'A', 'b': 'B', 'c': 'C'} operand = {'a': 'A', 'b': 'B'} ne_pred = jp.DICT_NE(operand) self.assertGoodResult(PathValue('', letters), ne_pred, ne_pred(letters)) self.assertBadResult(PathValue('', operand), ne_pred, ne_pred(operand)) self.assertGoodResult(PathValue('', {'a': 'A'}), ne_pred, ne_pred({'a': 'A'}))
def test_dict_ne(self): context = ExecutionContext() letters = {'a':'A', 'b':'B', 'c':'C'} operand = {'a': 'A', 'b': 'B'} ne_pred = jp.DICT_NE(operand) self.assertGoodResult(PathValue('', letters), ne_pred, ne_pred(context, letters)) self.assertBadResult(PathValue('', operand), ne_pred, ne_pred(context, operand)) self.assertGoodResult(PathValue('', {'a': 'A'}), ne_pred, ne_pred(context, {'a': 'A'}))
def test_collect_from_list_found(self): # """Ambiguous path passes through a list element.""" source = [_LETTER_DICT] pred = PathPredicate('a') values = pred(source) self.assertEqual([PathValue(PATH_SEP.join(['[0]', 'a']), 'A')], values.path_values) pred = PathPredicate('b') values = pred(source) self.assertEqual([PathValue(PATH_SEP.join(['[0]', 'b']), 'B')], values.path_values) self.assertEqual([], values.path_failures)
def test_collect_from_nested_dict_found(self): # """Nested dictionary attribute lookup.""" source = {'outer': {'inner': _LETTER_DICT}} pred = PathPredicate(PATH_SEP.join(['outer', 'inner', 'a'])) values = pred(source) self.assertEqual([PathValue(pred.path, 'A')], values.path_values) self.assertEqual([], values.path_failures) pred = PathPredicate(PATH_SEP.join(['outer', 'inner', 'b'])) values = pred(source) self.assertEqual([PathValue(pred.path, 'B')], values.path_values) self.assertEqual([], values.path_failures)
def test_collect_from_dict_found(self): # """Normal dictionary attribute lookup.""" source = _LETTER_DICT pred = PathPredicate('a') values = pred(source) self.assertEqual([PathValue('a', 'A')], values.path_values) self.assertEqual([], values.path_failures) pred = PathPredicate('b') values = pred(source) self.assertEqual([PathValue('b', 'B')], values.path_values) self.assertEqual([], values.path_failures)
def test_collect_plain_terminal_list(self): # """Path to a value that is a list.""" source = {'a': [_LETTER_DICT]} pred = PathPredicate('a' + DONT_ENUMERATE_TERMINAL) values = pred(source) self.assertEqual([PathValue('a', [_LETTER_DICT])], values.path_values) self.assertEqual([], values.path_failures) pred = PathPredicate(PATH_SEP.join(['a', 'a'])) values = pred(source) self.assertEqual([PathValue(PATH_SEP.join(['a[0]', 'a']), 'A')], values.path_values) self.assertEqual([], values.path_failures)
def test_dict_eq_indirect(self): context = ExecutionContext(testA='A', testKey='b') letters = {'a':'A', 'b':'B', 'c':'C'} operand = {'a': lambda x: x['testA'], 'b': 'B'} actual_operand = {'a': 'A', 'b': 'B'} eq_pred = jp.DICT_EQ(operand) self.assertBadResult(PathValue('', letters), eq_pred, eq_pred(context, letters)) self.assertGoodResult(PathValue('', actual_operand), eq_pred, eq_pred(context, actual_operand)) self.assertBadResult(PathValue('', {'a': 'A'}), eq_pred, eq_pred(context, {'a': 'A'}))
def test_collect_from_nested_list_not_found(self): # """Path through nested lists that cannot be resolved.""" source = {'outer': [_LETTER_DICT, _NUMBER_DICT]} pred = PathPredicate(PATH_SEP.join(['outer', 'X'])) values = pred(source) self.assertEqual([], values.path_values) self.assertEqual( [MissingPathError( _LETTER_DICT, 'X', path_value=PathValue('outer[0]', _LETTER_DICT)), MissingPathError( _NUMBER_DICT, 'X', path_value=PathValue('outer[1]', _NUMBER_DICT))], values.path_failures)
def test_string_substr(self): context = ExecutionContext() substr_q = jp.STR_SUBSTR('q') substr_pqr = jp.STR_SUBSTR('pqr') self.assertGoodResult(PathValue('', 'pqr'), substr_q, substr_q(context, 'pqr')) self.assertGoodResult(PathValue('', 'rqp'), substr_q, substr_q(context, 'rqp')) self.assertGoodResult(PathValue('', 'pqr'), substr_pqr, substr_pqr(context, 'pqr')) self.assertBadResult(PathValue('', 'abc'), substr_q, substr_q(context, 'abc')) self.assertBadResult(PathValue('', 'xyz'), substr_q, substr_q(context, 'xyz'))
def test_path_found_in_array(self): pred = jp.PathPredicate(PATH_SEP.join(['outer', 'inner', 'a'])) simple = {'a': 'A', 'b': 'B'} source = {'outer': [{'middle': simple}, {'inner': simple}]} found = [PathValue(PATH_SEP.join(['outer[1]', 'inner', 'a']), 'A')] pruned = [ jp.MissingPathError(source['outer'][0], 'inner', path_value=PathValue('outer[0]', source['outer'][0])) ] expect = _make_result(pred, None, source, found, [], pruned) self.assertEqual(expect, pred(source))
def test_dict_subset_with_array_values(self): small = {'a': ['A'], 'b': [1, 2]} big = {'a': ['A', 'B', 'C'], 'b': [1, 2, 3], 'c': ['red', 'yellow']} small_nested = {'first': small} big_nested = {'first': big, 'second': big} small_subset_pred = jp.DICT_SUBSET(small) nested_subset_pred = jp.DICT_SUBSET(small_nested) # These are matching the outer source objects because they contain # the subset we are looking for. self.assertGoodResult(PathValue('', big), small_subset_pred, small_subset_pred(big)) self.assertGoodResult(PathValue('', big_nested), nested_subset_pred, nested_subset_pred(big_nested))
def test_dict_simple_subset(self): context = ExecutionContext() letters = {'a':'A', 'b':'B', 'c':'C'} operand = {'a': 'A', 'b': 'B'} subset_pred = jp.DICT_SUBSET(operand) self.assertGoodResult(PathValue('', letters), subset_pred, subset_pred(context, letters)) self.assertGoodResult(PathValue('', operand), subset_pred, subset_pred(context, operand)) source = {'a':'A'} self.assertEqual( jp.MissingPathError(source=source, target_path='b', path_value=PathValue('', source)), subset_pred(context, source))
def test_dict_nested_subset(self): small = {'first': 'Apple', 'second': 'Banana'} small_subset_pred = jp.DICT_SUBSET(small) big = {'first': 'Apple', 'second': 'Banana', 'third': 'Cherry'} self.assertGoodResult(PathValue('', big), small_subset_pred, small_subset_pred(big)) small_nested = {'outer': small} small_nested_subset_pred = jp.DICT_SUBSET(small_nested) big_nested = {'outer': big, 'another': big} self.assertGoodResult(PathValue('', big_nested), small_nested_subset_pred, small_nested_subset_pred(big_nested))
def test_collect_from_dict_transform(self): context = ExecutionContext() source = {'a': 7, 'b': 4} pred = PathPredicate('', transform=lambda ctxt, val: val['a'] - val['b']) expect = 7 - 4 values = pred(context, source) builder = PathPredicateResultBuilder(source, pred) builder.add_result_candidate( PathValue('', source), PathValueResult(source=source, target_path='', path_value=PathValue('', expect), valid=True)) self.assertEqual(builder.build(True), values)
def __call__(self, context, value): valid = value == context.eval(self.__operand) return PathValueResult(pred=self, source=value, target_path='', path_value=PathValue('', value), valid=valid)
def test_collect_from_list_with_index(self): # """Path with explicit list indexes to traverse.""" source = [_LETTER_DICT, _NUMBER_DICT] pred = PathPredicate('[0]') values = pred(source) self.assertEqual([PathValue('[0]', _LETTER_DICT)], values.path_values) self.assertEqual([], values.path_failures) pred = PathPredicate('[1]') values = pred(source) self.assertEqual([PathValue('[1]', _NUMBER_DICT)], values.path_values) self.assertEqual([], values.path_failures) pred = PathPredicate(PATH_SEP.join(['[1]', 'a'])) values = pred(source) self.assertEqual([PathValue('[1]/a', 1)], values.path_values) self.assertEqual([], values.path_failures)
def test_path_value_found_top(self): source = _COMPOSITE_DICT pred = jp.PathEqPredicate('letters', _LETTER_DICT) result = pred(source) expect = _make_result(pred, jp.DICT_EQ(_LETTER_DICT), source, [PathValue('letters', _LETTER_DICT)], []) self.assertTrue(result) self.assertEqual(expect, result)
def test_list_subset_strict_bad(self): letters = {'a': 'A', 'b': 'B', 'c': 'C'} numbers = {'a': 1, 'b': 2, 'c': 'C'} source = [letters, numbers] common_subset_pred = jp.LIST_SUBSET([{'c': 'C'}], strict=True) self.assertBadResult(PathValue('', source), common_subset_pred, common_subset_pred(source))
def test_collect_from_list_of_list_with_index(self): # """Path with explicit list indexes to traverse through nested lists.""" context = ExecutionContext() upper = ['A', 'B', 'C'] lower = ['a', 'b', 'c'] letters = [upper, lower] arabic = [1, 2, 3] roman = ['i', 'ii', 'iii'] numbers = [arabic, roman] source = [letters, numbers] # By default, values that are lists get expanded (one level) pred = PathPredicate('[1]') values = pred(context, source) self.assertEquals( [PathValue('[1][0]', arabic), PathValue('[1][1]', roman)], values.path_values) self.assertEqual([], values.path_failures) # If we dont want to expand, then decorate with DONT_ENUMERATE_TERMINAL. pred = PathPredicate('[0]' + DONT_ENUMERATE_TERMINAL) values = pred(context, source) self.assertEqual([PathValue('[0]', letters)], values.path_values) self.assertEqual([], values.path_failures) pred = PathPredicate('[1]' + DONT_ENUMERATE_TERMINAL) values = pred(context, source) self.assertEqual([PathValue('[1]', numbers)], values.path_values) self.assertEqual([], values.path_failures) # Go out one more level pred = PathPredicate('[1][0]' + DONT_ENUMERATE_TERMINAL) values = pred(context, source) self.assertEqual([PathValue('[1][0]', arabic)], values.path_values) self.assertEqual([], values.path_failures) pred = PathPredicate('[1][0]') values = pred(context, source) self.assertEqual([ PathValue('[1][0][0]', 1), PathValue('[1][0][1]', 2), PathValue('[1][0][2]', 3) ], values.path_values) self.assertEqual([], values.path_failures) # Go all the way down. pred = PathPredicate('[1][0][2]') values = pred(context, source) self.assertEqual([PathValue('[1][0][2]', 3)], values.path_values) self.assertEqual([], values.path_failures)
def test_collect_from_list_identity(self): context = ExecutionContext() letters = ['A', 'B', 'C'] pred = PathPredicate('') values = pred(context, letters) self.assertEqual([ PathValue('[0]', 'A'), PathValue('[1]', 'B'), PathValue('[2]', 'C') ], values.path_values) self.assertEqual([], values.path_failures) pred = PathPredicate(DONT_ENUMERATE_TERMINAL) values = pred(context, letters) self.assertEqual([PathValue('', letters)], values.path_values) self.assertEqual([], values.path_failures) pred = PathPredicate(PATH_SEP) values = pred(context, letters) self.assertEqual([ PathValue('[0]', 'A'), PathValue('[1]', 'B'), PathValue('[2]', 'C') ], values.path_values) self.assertEqual([], values.path_failures)
def test_collect_filter_good(self): source = {'outer': [_LETTER_DICT, _NUMBER_DICT]} filter_pred = TestEqualsPredicate(2) pred = PathPredicate(PATH_SEP.join(['outer', 'b']), pred=filter_pred) # This is a precise test against the exact result returned. builder = PathPredicateResultBuilder(source, pred) path_0 = PATH_SEP.join(['outer[0]', 'b']) path_1 = PATH_SEP.join(['outer[1]', 'b']) bad_result = PathValueResult(source=source, target_path=pred.path, path_value=PathValue(path_0, 'B'), valid=False, pred=filter_pred) good_result = PathValueResult(source=source, target_path=pred.path, path_value=PathValue(path_1, 2), valid=True, pred=filter_pred) builder.add_result_candidate(PathValue(path_0, 'B'), bad_result) builder.add_result_candidate(PathValue(path_1, 2), good_result) expect = builder.build(True) pred_result = pred(source) self.assertEqual([PathValue(PATH_SEP.join(['outer[1]', 'b']), 2)], pred_result.path_values) self.assertEqual(expect, pred_result) self.assertEqual( [jp.PathPredicateResultCandidate(PathValue(path_0, 'B'), bad_result)], pred_result.invalid_candidates) self.assertEqual( [jp.PathPredicateResultCandidate(PathValue(path_1, 2), good_result)], pred_result.valid_candidates) self.assertEqual([], pred_result.path_failures)
def test_list_subset_nested_bad(self): context = ExecutionContext() letters = {'a': 'A', 'b': 'B', 'c': 'C'} numbers = {'a': 1, 'b': 2, 'c': 'C'} source = [numbers, [letters]] pred = jp.LIST_SUBSET([[{'b': 2}]]) self.assertBadResult(PathValue('', source), pred, pred(context, source))
def test_collect_from_nested_dict_not_found(self): # """Nested dictionary attribute lookup with missing element.""" source = _LETTER_DICT pred = PathPredicate(PATH_SEP.join(['a', 'b'])) values = pred(source) self.assertEqual([], values.path_values) self.assertEqual( [MissingPathError('A', 'b', path_value=PathValue('a', 'A'))], values.path_failures)
def test_list_subset_nonstrict_good(self): letters = {'a': 'A', 'b': 'B', 'c': 'C'} numbers = {'a': 1, 'b': 2, 'c': 'C'} source = [letters, numbers] common_subset_pred = jp.LIST_SUBSET([{'c': 'C'}]) self.assertFalse(common_subset_pred.strict) self.assertGoodResult(PathValue('', source), common_subset_pred, common_subset_pred(source))