diff options
-rw-r--r-- | src/test/java/com/google/devtools/build/lib/syntax/MethodLibraryTest.java | 251 | ||||
-rw-r--r-- | src/test/skylark/skylark_test.py | 22 | ||||
-rw-r--r-- | src/test/skylark/testdata/int_function.sky | 95 | ||||
-rw-r--r-- | src/test/skylark/testdata/list_mutation.sky | 87 | ||||
-rw-r--r-- | src/test/skylark/testdata/list_slices.sky | 22 | ||||
-rw-r--r-- | src/test/skylark/testdata/range.sky | 18 | ||||
-rw-r--r-- | src/test/skylark/testdata/reversed.sky | 30 |
7 files changed, 263 insertions, 262 deletions
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/MethodLibraryTest.java b/src/test/java/com/google/devtools/build/lib/syntax/MethodLibraryTest.java index 7c63e18feb..2c353d6fc9 100644 --- a/src/test/java/com/google/devtools/build/lib/syntax/MethodLibraryTest.java +++ b/src/test/java/com/google/devtools/build/lib/syntax/MethodLibraryTest.java @@ -17,7 +17,6 @@ package com.google.devtools.build.lib.syntax; import static com.google.common.truth.Truth.assertThat; import com.google.common.collect.Iterables; -import com.google.devtools.build.lib.syntax.SkylarkList.MutableList; import com.google.devtools.build.lib.syntax.util.EvaluationTestCase; import org.junit.Before; import org.junit.Test; @@ -206,51 +205,6 @@ public class MethodLibraryTest extends EvaluationTestCase { } @Test - public void testReversedWithInvalidTypes() throws Exception { - new BothModesTest() - .testIfExactError("type 'NoneType' is not iterable", "reversed(None)") - .testIfExactError("type 'int' is not iterable", "reversed(1)") - .testIfExactError( - "Argument to reversed() must be a sequence, not a dictionary.", "reversed({1: 3})"); - new SkylarkTest() - .testIfExactError( - "Argument to reversed() must be a sequence, not a depset.", "reversed(depset([1]))"); - } - - @Test - public void testReversedWithLists() throws Exception { - new BothModesTest() - .testEval("reversed([])", "[]") - .testEval("reversed([1])", "[1]") - .testEval("reversed([1, 2, 3, 4, 5])", "[5, 4, 3, 2, 1]") - .testEval("reversed([[1, 2], 3, 4, [5]])", "[[5], 4, 3, [1, 2]]") - .testEval("reversed([1, 1, 1, 1, 2])", "[2, 1, 1, 1, 1]"); - } - - @Test - public void testReversedNoSideEffects() throws Exception { - new SkylarkTest() - .testEval( - "def foo():\n" - + " x = ['a', 'b']\n" - + " y = reversed(x)\n" - + " y += ['c']\n" - + " return x\n" - + "foo()", - "['a', 'b']"); - } - - @Test - public void testEquivalenceOfReversedAndSlice() throws Exception { - String[] data = new String[] {"[]", "[1]", "[1, 2, 3]"}; - for (String toBeReversed : data) { - new BothModesTest() - .testEval( - String.format("reversed(%s)", toBeReversed), String.format("%s[::-1]", toBeReversed)); - } - } - - @Test public void testListSort() throws Exception { new BothModesTest() .testEval("sorted([0,1,2,3])", "[0, 1, 2, 3]") @@ -286,12 +240,6 @@ public class MethodLibraryTest extends EvaluationTestCase { } @Test - public void testListAccessBadIndex() throws Exception { - new BothModesTest() - .testIfErrorContains("indices must be integers, not string", "[[1], [2]]['a']"); - } - - @Test public void testDictionaryAccess() throws Exception { new BothModesTest() .testEval("{1: ['foo']}[1]", "['foo']") @@ -475,34 +423,6 @@ public class MethodLibraryTest extends EvaluationTestCase { } @Test - public void testListIndex() throws Exception { - new BothModesTest() - .testStatement("['a', 'b', 'c', 'd'][0]", "a") - .testStatement("['a', 'b', 'c', 'd'][1]", "b") - .testStatement("['a', 'b', 'c', 'd'][-1]", "d") - .testStatement("['a', 'b', 'c', 'd'][-2]", "c") - .testStatement("[0, 1, 2][-3]", 0) - .testStatement("[0, 1, 2][-2]", 1) - .testStatement("[0, 1, 2][-1]", 2) - .testStatement("[0, 1, 2][0]", 0); - } - - @Test - public void testListIndexOutOfRange() throws Exception { - new BothModesTest() - .testIfErrorContains( - "index out of range (index is 3, but sequence has 3 elements)", "[0, 1, 2][3]") - .testIfErrorContains( - "index out of range (index is -4, but sequence has 3 elements)", "[0, 1, 2][-4]") - .testIfErrorContains( - "index out of range (index is -2, but sequence has 1 elements)", "[0][-2]") - .testIfErrorContains( - "index out of range (index is 1, but sequence has 1 elements)", "[0][1]") - .testIfErrorContains( - "index out of range (index is 1, but sequence has 0 elements)", "[][1]"); - } - - @Test public void testHash() throws Exception { // We specify the same string hashing algorithm as String.hashCode(). new SkylarkTest() @@ -554,80 +474,6 @@ public class MethodLibraryTest extends EvaluationTestCase { } @Test - public void testPyListAppend() throws Exception { - new BuildTest() - .setUp("FOO = ['a', 'b']", "FOO.insert(0, 'c')") - .testLookup("FOO", MutableList.of(env, "c", "a", "b")) - .setUp("FOO.insert(1, 'd')") - .testLookup("FOO", MutableList.of(env, "c", "d", "a", "b")) - .setUp("FOO.insert(4, 'e')") - .testLookup("FOO", MutableList.of(env, "c", "d", "a", "b", "e")) - .setUp("FOO.insert(-10, 'f')") - .testLookup("FOO", MutableList.of(env, "f", "c", "d", "a", "b", "e")) - .setUp("FOO.insert(10, 'g')") - .testLookup("FOO", MutableList.of(env, "f", "c", "d", "a", "b", "e", "g")) - .testIfErrorContains("type 'tuple' has no method insert(int)", "(1, 2).insert(3)"); - } - - @Test - public void testPyListInsert() throws Exception { - new BuildTest() - .setUp("FOO = ['a', 'b']", "FOO.append('c')") - .testLookup("FOO", MutableList.of(env, "a", "b", "c")) - .testIfErrorContains("type 'tuple' has no method append(int)", "(1, 2).append(3)"); - } - - @Test - public void testPyListExtend() throws Exception { - new BuildTest() - .setUp("FOO = ['a', 'b']", "FOO.extend(['c', 'd'])", "FOO.extend(('e', 'f'))") - .testLookup("FOO", MutableList.of(env, "a", "b", "c", "d", "e", "f")) - .testIfErrorContains("type 'tuple' has no method extend(list)", "(1, 2).extend([3, 4])") - .testIfErrorContains( - "argument 'items' has type 'int', but should be 'sequence'\n" - + "in call to builtin method list.extend(items)", - "[1, 2].extend(3)"); - } - - @Test - public void testListRemove() throws Exception { - new BothModesTest() - .setUp("foo = ['a', 'b', 'c', 'b']", "foo.remove('b')") - .testLookup("foo", MutableList.of(env, "a", "c", "b")) - .setUp("foo.remove('c')") - .testLookup("foo", MutableList.of(env, "a", "b")) - .setUp("foo.remove('a')") - .testLookup("foo", MutableList.of(env, "b")) - .setUp("foo.remove('b')") - .testLookup("foo", MutableList.of(env)) - .testIfErrorContains("item 3 not found in list", "[1, 2].remove(3)"); - - new BothModesTest() - .testIfErrorContains("type 'tuple' has no method remove(int)", "(1, 2).remove(3)"); - } - - @Test - public void testListPop() throws Exception { - new BothModesTest() - .setUp("li = [2, 3, 4]; ret = li.pop()") - .testLookup("li", MutableList.of(env, 2, 3)) - .testLookup("ret", 4); - new BothModesTest() - .setUp("li = [2, 3, 4]; ret = li.pop(-2)") - .testLookup("li", MutableList.of(env, 2, 4)) - .testLookup("ret", 3); - new BothModesTest() - .setUp("li = [2, 3, 4]; ret = li.pop(1)") - .testLookup("li", MutableList.of(env, 2, 4)) - .testLookup("ret", 3); - new BothModesTest() - .testIfErrorContains( - "index out of range (index is 3, but sequence has 2 elements)", "[1, 2].pop(3)"); - - new BothModesTest().testIfErrorContains("type 'tuple' has no method pop()", "(1, 2).pop()"); - } - - @Test public void testReassignmentOfPrimitivesNotForbiddenByCoreLanguage() throws Exception { new BuildTest() .setUp("cc_binary = (['hello.cc'])") @@ -686,103 +532,6 @@ public class MethodLibraryTest extends EvaluationTestCase { } @Test - public void testIntNonstring() throws Exception { - new BothModesTest() - .testStatement("int(0)", 0) - .testStatement("int(42)", 42) - .testStatement("int(-1)", -1) - .testStatement("int(2147483647)", 2147483647) - // TODO(bazel-team): -2147483648 is not actually a valid int literal even though it's a - // valid int value, hence the -1 expression. - .testStatement("int(-2147483647 - 1)", -2147483648) - .testStatement("int(True)", 1) - .testStatement("int(False)", 0) - .testIfErrorContains("None is not of type string or int or bool", "int(None)") - // This case is allowed in Python but not Skylark. - .testIfErrorContains("insufficient arguments received", "int()"); - } - - @Test - public void testIntStringNoBase_Simple() throws Exception { - // Includes same numbers as integer test cases above. - new BothModesTest() - .testStatement("int('0')", 0) - .testStatement("int('42')", 42) - .testStatement("int('-1')", -1) - .testStatement("int('2147483647')", 2147483647) - .testStatement("int('-2147483648')", -2147483648) - // Leading zero allowed when not using base = 0. - .testStatement("int('016')", 16) - // Leading plus sign allowed for strings. - .testStatement("int('+42')", 42); - } - - @Test - public void testIntStringNoBase_BadStrings() throws Exception { - new BothModesTest() - .testIfErrorContains("invalid base-10 integer constant: 2147483648", "int(2147483648)") - // .testIfErrorContains("invalid base-10 integer constant: -2147483649", "int(-2147483649)") - .testIfErrorContains("cannot be empty", "int('')") - // Surrounding whitespace is not allowed. - .testIfErrorContains("invalid literal for int() with base 10: \" 42 \"", "int(' 42 ')") - .testIfErrorContains("invalid literal for int() with base 10: \"-\"", "int('-')") - .testIfErrorContains("invalid literal for int() with base 10: \"0x\"", "int('0x')") - .testIfErrorContains("invalid literal for int() with base 10: \"1.5\"", "int('1.5')") - .testIfErrorContains("invalid literal for int() with base 10: \"ab\"", "int('ab')"); - } - - @Test - public void testIntStringWithBase() throws Exception { - new BothModesTest() - .testStatement("int('11', 2)", 3) - .testStatement("int('-11', 2)", -3) - .testStatement("int('11', 9)", 10) - .testStatement("int('AF', 16)", 175) - .testStatement("int('11', 36)", 37) - .testStatement("int('az', 36)", 395) - .testStatement("int('11', 10)", 11) - .testStatement("int('11', 0)", 11) - .testStatement("int('016', 8)", 14) - .testStatement("int('016', 16)", 22); - } - - @Test - public void testIntStringWithBase_InvalidBase() throws Exception { - new BothModesTest() - .testIfErrorContains( - "cannot infer base for int() when value begins with a 0: \"016\"", - "int('016', 0)") - .testIfExactError("invalid literal for int() with base 3: \"123\"", "int('123', 3)") - .testIfExactError("invalid literal for int() with base 15: \"FF\"", "int('FF', 15)") - .testIfExactError("int() base must be >= 2 and <= 36", "int('123', -1)") - .testIfExactError("int() base must be >= 2 and <= 36", "int('123', 1)") - .testIfExactError("int() base must be >= 2 and <= 36", "int('123', 37)"); - } - - @Test - public void testIntStringWithBase_Prefix() throws Exception { - new BothModesTest() - .testStatement("int('0b11', 0)", 3) - .testStatement("int('-0b11', 0)", -3) - .testStatement("int('+0b11', 0)", 3) - .testStatement("int('0B11', 2)", 3) - .testStatement("int('0o11', 0)", 9) - .testStatement("int('0O11', 8)", 9) - .testStatement("int('0XFF', 0)", 255) - .testStatement("int('0xFF', 16)", 255) - .testIfExactError("invalid literal for int() with base 8: \"0xFF\"", "int('0xFF', 8)"); - } - - @Test - public void testIntNonstringWithBase() throws Exception { - new BothModesTest() - .testIfExactError("int() can't convert non-string with explicit base", "int(True, 2)") - .testIfExactError("int() can't convert non-string with explicit base", "int(1, 2)") - .testIfExactError("int() can't convert non-string with explicit base", "int(True, 10)") - ; - } - - @Test public void testStrFunction() throws Exception { new SkylarkTest().testStatement("def foo(x): return x\nstr(foo)", "<function foo>"); } diff --git a/src/test/skylark/skylark_test.py b/src/test/skylark/skylark_test.py index 55bf92b7ec..0e7bcf5e48 100644 --- a/src/test/skylark/skylark_test.py +++ b/src/test/skylark/skylark_test.py @@ -34,6 +34,7 @@ class SkylarkTest(unittest.TestCase): CHUNK_SEP = "---" ERR_SEP = "###" + seen_error = False def chunks(self, path): code = [] @@ -61,21 +62,18 @@ class SkylarkTest(unittest.TestCase): def check_output(self, output, expected): if expected and not output: - raise Exception("Expected error:", expected) + self.seen_error = True + print("Expected error:", expected) if output and not expected: - raise Exception("Unexpected error:", output) + self.seen_error = True + print("Unexpected error:", output) for exp in expected: - if not re.search(exp, output): - raise Exception("Error `{}` not found, got: {}".format(exp, output)) - - TESTS = [ - "int.sky", - "equality.sky", - "and_or_not.sky", - "min_max.sky", - ] + # Try both substring and regex matching. + if exp not in output and not re.search(exp, output): + self.seen_error = True + print("Error `{}` not found, got: `{}`".format(exp, output)) PRELUDE = """ def assert_eq(x, y): @@ -100,6 +98,8 @@ def assert_(cond, msg="assertion failed"): output = self.evaluate(tmp.name).decode("utf-8") os.unlink(tmp.name) self.check_output(output, expected) + if self.seen_error: + raise Exception("Test failed") if __name__ == "__main__": diff --git a/src/test/skylark/testdata/int_function.sky b/src/test/skylark/testdata/int_function.sky new file mode 100644 index 0000000000..7d6a382a65 --- /dev/null +++ b/src/test/skylark/testdata/int_function.sky @@ -0,0 +1,95 @@ +# int +assert_eq(int(0), 0) +assert_eq(int(42), 42) +assert_eq(int(-1), -1) +assert_eq(int(2147483647), 2147483647) +# -2147483648 is not actually a valid int literal even though it's a +# valid int value, hence the -1 expression. +assert_eq(int(-2147483647 - 1), -2147483647 - 1) +assert_eq(int(True), 1) +assert_eq(int(False), 0) + +--- +int(None) ### None is not of type string or int or bool +--- +# This case is allowed in Python but not Skylark +int() ### insufficient arguments received +--- + +# string, no base +# Includes same numbers as integer test cases above. +assert_eq(int('0'), 0) +assert_eq(int('42'), 42) +assert_eq(int('-1'), -1) +assert_eq(int('2147483647'), 2147483647) +assert_eq(int('-2147483648'), -2147483647 - 1) +# Leading zero allowed when not using base = 0. +assert_eq(int('016'), 16) +# Leading plus sign allowed for strings. +assert_eq(int('+42'), 42) + +--- +int(2147483648) ### invalid base-10 integer constant: 2147483648 +--- +int(-2147483649) ### invalid base-10 integer constant: 2147483649 +--- +int('') ### cannot be empty +--- +# Surrounding whitespace is not allowed +int(' 42 ') ### invalid literal for int() with base 10: " 42 " +--- +int('-') ### invalid literal for int() with base 10: "-" +--- +int('0x') ### invalid literal for int() with base 10: "0x" +--- +int('1.5') ### invalid literal for int() with base 10: "1.5" +--- +int('ab') ### invalid literal for int() with base 10: "ab" +--- + +assert_eq(int('11', 2), 3) +assert_eq(int('-11', 2), -3) +assert_eq(int('11', 9), 10) +assert_eq(int('AF', 16), 175) +assert_eq(int('11', 36), 37) +assert_eq(int('az', 36), 395) +assert_eq(int('11', 10), 11) +assert_eq(int('11', 0), 11) +assert_eq(int('016', 8), 14) +assert_eq(int('016', 16), 22) + +--- +# invalid base +int('016', 0) ### cannot infer base for int() when value begins with a 0: "016" +--- +int('123', 3) ### invalid literal for int() with base 3: "123" +--- +int('FF', 15) ### invalid literal for int() with base 15: "FF" +--- +int('123', -1) ### int() base must be >= 2 and <= 36 +--- +int('123', 1) ### int() base must be >= 2 and <= 36 +--- +int('123', 37) ### int() base must be >= 2 and <= 36 +--- +int('123', 'x') ### base must be an integer (got 'string') +--- + +# base with prefix +assert_eq(int('0b11', 0), 3) +assert_eq(int('-0b11', 0), -3) +assert_eq(int('+0b11', 0), 3) +assert_eq(int('0B11', 2), 3) +assert_eq(int('0o11', 0), 9) +assert_eq(int('0O11', 8), 9) +assert_eq(int('0XFF', 0), 255) +assert_eq(int('0xFF', 16), 255) + +--- +int('0xFF', 8) ### invalid literal for int() with base 8: "0xFF" +--- +int(True, 2) ### int() can't convert non-string with explicit base +--- +int(1, 2) ### int() can't convert non-string with explicit base +--- +int(True, 10) ### int() can't convert non-string with explicit base diff --git a/src/test/skylark/testdata/list_mutation.sky b/src/test/skylark/testdata/list_mutation.sky new file mode 100644 index 0000000000..24d5425952 --- /dev/null +++ b/src/test/skylark/testdata/list_mutation.sky @@ -0,0 +1,87 @@ +# insert + +foo = ['a', 'b'] + +foo.insert(0, 'c') +assert_eq(foo, ['c', 'a', 'b']) + +foo.insert(1, 'd') +assert_eq(foo, ['c', 'd', 'a', 'b']) + +foo.insert(4, 'e') +assert_eq(foo, ['c', 'd', 'a', 'b', 'e']) + +foo.insert(-10, 'f') +assert_eq(foo, ['f', 'c', 'd', 'a', 'b', 'e']) + +foo.insert(10, 'g') +assert_eq(foo, ['f', 'c', 'd', 'a', 'b', 'e', 'g']) + +--- +(1, 2).insert(3) ### type 'tuple' has no method insert\(int\) +--- + +# append + +foo = ['a', 'b'] +foo.append('c') +assert_eq(foo, ['a', 'b', 'c']) +foo.append('d') +assert_eq(foo, ['a', 'b', 'c', 'd']) + +--- +(1, 2).append(3) ### type 'tuple' has no method append\(int\) +--- + +# extend + +foo = ['a', 'b'] +foo.extend(['c', 'd']) +foo.extend(('e', 'f')) +assert_eq(foo, ['a', 'b', 'c', 'd', 'e', 'f']) + +--- +(1, 2).extend([3, 4]) ### type 'tuple' has no method extend(list) +--- +[1, 2].extend(3) ### argument 'items' has type 'int', but should be 'sequence' + +# remove + +foo = ['a', 'b', 'c', 'b'] + +foo.remove('b') +assert_eq(foo, ['a', 'c', 'b']) + +foo.remove('c') +assert_eq(foo, ['a', 'b']) + +foo.remove('a') +assert_eq(foo, ['b']) + +foo.remove('b') +assert_eq(foo, []) + +--- +(1, 2).remove(3) ### type 'tuple' has no method remove\(int\) +--- +[1, 2].remove(3) ### item 3 not found in list +--- + +# pop + +li1 = [2, 3, 4] +assert_eq(li1.pop(), 4) +assert_eq(li1, [2, 3]) + +li2 = [2, 3, 4] +assert_eq(li2.pop(-2), 3) +assert_eq(li2, [2, 4]) + +li3 = [2, 3, 4] +assert_eq(li3.pop(1), 3) +assert_eq(li3, [2, 4]) + +--- +[1, 2].pop(3) ### index out of range (index is 3, but sequence has 2 elements) +--- +(1, 2).pop() ### type 'tuple' has no method pop() diff --git a/src/test/skylark/testdata/list_slices.sky b/src/test/skylark/testdata/list_slices.sky index 3ec2999cba..b79661e23c 100644 --- a/src/test/skylark/testdata/list_slices.sky +++ b/src/test/skylark/testdata/list_slices.sky @@ -46,6 +46,16 @@ assert_eq((1, 2, 3, 4, 5)[3:1:-1], (4, 3)) assert_eq((1, 2, 3, 4, 5)[::-2], (5, 3, 1)) assert_eq((1, 2, 3, 4, 5)[::-10], (5,)) +# index +assert_eq(['a', 'b', 'c', 'd'][0], 'a') +assert_eq(['a', 'b', 'c', 'd'][1], 'b') +assert_eq(['a', 'b', 'c', 'd'][-1], 'd') +assert_eq(['a', 'b', 'c', 'd'][-2], 'c') +assert_eq([0, 1, 2][-3], 0) +assert_eq([0, 1, 2][-2], 1) +assert_eq([0, 1, 2][-1], 2) +assert_eq([0, 1, 2][0], 0) + --- '123'['a'::] ### slice start must be an integer, not 'a' --- @@ -60,3 +70,15 @@ assert_eq((1, 2, 3, 4, 5)[::-10], (5,)) [1, 2, 3][:3:0] ### slice step cannot be zero --- [1, 2, 3][1:3:0] ### slice step cannot be zero +--- +[[1], [2]]['a'] ### indices must be integers, not string +--- +[0, 1, 2][3] ### index out of range (index is 3, but sequence has 3 elements) +--- +[0, 1, 2][-4] ### index out of range (index is -4, but sequence has 3 elements) +--- +[0][-2] ### index out of range (index is -2, but sequence has 1 elements) +--- +[0][1] ### index out of range (index is 1, but sequence has 1 elements) +--- +[][1] ### index out of range (index is 1, but sequence has 0 elements) diff --git a/src/test/skylark/testdata/range.sky b/src/test/skylark/testdata/range.sky new file mode 100644 index 0000000000..75d9be441b --- /dev/null +++ b/src/test/skylark/testdata/range.sky @@ -0,0 +1,18 @@ +assert_eq(range(5), [0, 1, 2, 3, 4]) +assert_eq(range(0), []) +assert_eq(range(1), [0]) +assert_eq(range(-2), []) +assert_eq(range(-3, 2), [-3, -2, -1, 0, 1]) +assert_eq(range(3, 2), []) +assert_eq(range(3, 3), []) +assert_eq(range(3, 4), [3]) +assert_eq(range(3, 5), [3, 4]) +assert_eq(range(-3, 5, 2), [-3, -1, 1, 3]) +assert_eq(range(-3, 6, 2), [-3, -1, 1, 3, 5]) +assert_eq(range(5, 0, -1), [5, 4, 3, 2, 1]) +assert_eq(range(5, 0, -10), [5]) +assert_eq(range(0, -3, -2), [0, -2]) + +--- +range(2, 3, 0) ### step cannot be 0 + diff --git a/src/test/skylark/testdata/reversed.sky b/src/test/skylark/testdata/reversed.sky new file mode 100644 index 0000000000..91ce565687 --- /dev/null +++ b/src/test/skylark/testdata/reversed.sky @@ -0,0 +1,30 @@ +# lists + +assert_eq(reversed(''), []) +assert_eq(reversed('a'), ['a']) +assert_eq(reversed('abc'), ['c', 'b', 'a']) +assert_eq(reversed('__test '), [' ', ' ', 't', 's', 'e', 't', '_', '_']) +assert_eq(reversed('bbb'), ['b', 'b', 'b']) + +--- +reversed(None) ### type 'NoneType' is not iterable +--- +reversed(1) ### type 'int' is not iterable +--- +reversed({1: 3}) ### Argument to reversed() must be a sequence, not a dictionary +--- + +x = ['a', 'b'] +y = reversed(x) +y.append('c') +assert_eq(y, ['b', 'a', 'c']) +assert_eq(x, ['a', 'b']) + +def reverse_equivalence(inp): + assert_eq(reversed(inp), inp[::-1]) + assert_eq(reversed(reversed(inp)), inp) + +reverse_equivalence([]) +reverse_equivalence([1]) +reverse_equivalence(["a", "b"]) + |