| #!/usr/bin/env python2 |
| # |
| # Copyright (C) 2014 The Android Open Source Project |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| # This is a test file which exercises all feautres supported by the domain- |
| # specific markup language implemented by Checker. |
| |
| import checker |
| import io |
| import unittest |
| |
| # The parent type of exception expected to be thrown by Checker during tests. |
| # It must be specific enough to not cover exceptions thrown due to actual flaws |
| # in Checker. |
| CheckerException = SystemExit |
| |
| |
| class TestCheckFile_PrefixExtraction(unittest.TestCase): |
| def __tryParse(self, string): |
| checkFile = checker.CheckFile(None, []) |
| return checkFile._extractLine("CHECK", string) |
| |
| def test_InvalidFormat(self): |
| self.assertIsNone(self.__tryParse("CHECK")) |
| self.assertIsNone(self.__tryParse(":CHECK")) |
| self.assertIsNone(self.__tryParse("CHECK:")) |
| self.assertIsNone(self.__tryParse("//CHECK")) |
| self.assertIsNone(self.__tryParse("#CHECK")) |
| |
| self.assertIsNotNone(self.__tryParse("//CHECK:foo")) |
| self.assertIsNotNone(self.__tryParse("#CHECK:bar")) |
| |
| def test_InvalidLabel(self): |
| self.assertIsNone(self.__tryParse("//ACHECK:foo")) |
| self.assertIsNone(self.__tryParse("#ACHECK:foo")) |
| |
| def test_NotFirstOnTheLine(self): |
| self.assertIsNone(self.__tryParse("A// CHECK: foo")) |
| self.assertIsNone(self.__tryParse("A # CHECK: foo")) |
| self.assertIsNone(self.__tryParse("// // CHECK: foo")) |
| self.assertIsNone(self.__tryParse("# # CHECK: foo")) |
| |
| def test_WhitespaceAgnostic(self): |
| self.assertIsNotNone(self.__tryParse(" //CHECK: foo")) |
| self.assertIsNotNone(self.__tryParse("// CHECK: foo")) |
| self.assertIsNotNone(self.__tryParse(" //CHECK: foo")) |
| self.assertIsNotNone(self.__tryParse("// CHECK: foo")) |
| |
| |
| class TestCheckLine_Parse(unittest.TestCase): |
| def __getPartPattern(self, linePart): |
| if linePart.variant == checker.CheckElement.Variant.Separator: |
| return "\s+" |
| else: |
| return linePart.pattern |
| |
| def __getRegex(self, checkLine): |
| return "".join(map(lambda x: "(" + self.__getPartPattern(x) + ")", checkLine.lineParts)) |
| |
| def __tryParse(self, string): |
| return checker.CheckLine(string) |
| |
| def __parsesTo(self, string, expected): |
| self.assertEqual(expected, self.__getRegex(self.__tryParse(string))) |
| |
| def __tryParseNot(self, string): |
| return checker.CheckLine(string, checker.CheckLine.Variant.Not) |
| |
| def __parsesPattern(self, string, pattern): |
| line = self.__tryParse(string) |
| self.assertEqual(1, len(line.lineParts)) |
| self.assertEqual(checker.CheckElement.Variant.Pattern, line.lineParts[0].variant) |
| self.assertEqual(pattern, line.lineParts[0].pattern) |
| |
| def __parsesVarRef(self, string, name): |
| line = self.__tryParse(string) |
| self.assertEqual(1, len(line.lineParts)) |
| self.assertEqual(checker.CheckElement.Variant.VarRef, line.lineParts[0].variant) |
| self.assertEqual(name, line.lineParts[0].name) |
| |
| def __parsesVarDef(self, string, name, body): |
| line = self.__tryParse(string) |
| self.assertEqual(1, len(line.lineParts)) |
| self.assertEqual(checker.CheckElement.Variant.VarDef, line.lineParts[0].variant) |
| self.assertEqual(name, line.lineParts[0].name) |
| self.assertEqual(body, line.lineParts[0].pattern) |
| |
| def __doesNotParse(self, string, partType): |
| line = self.__tryParse(string) |
| self.assertEqual(1, len(line.lineParts)) |
| self.assertNotEqual(partType, line.lineParts[0].variant) |
| |
| # Test that individual parts of the line are recognized |
| |
| def test_TextOnly(self): |
| self.__parsesTo("foo", "(foo)") |
| self.__parsesTo(" foo ", "(foo)") |
| self.__parsesTo("f$o^o", "(f\$o\^o)") |
| |
| def test_TextWithWhitespace(self): |
| self.__parsesTo("foo bar", "(foo)(\s+)(bar)") |
| self.__parsesTo("foo bar", "(foo)(\s+)(bar)") |
| |
| def test_RegexOnly(self): |
| self.__parsesPattern("{{a?b.c}}", "a?b.c") |
| |
| def test_VarRefOnly(self): |
| self.__parsesVarRef("[[ABC]]", "ABC") |
| |
| def test_VarDefOnly(self): |
| self.__parsesVarDef("[[ABC:a?b.c]]", "ABC", "a?b.c") |
| |
| def test_TextWithRegex(self): |
| self.__parsesTo("foo{{abc}}bar", "(foo)(abc)(bar)") |
| |
| def test_TextWithVar(self): |
| self.__parsesTo("foo[[ABC:abc]]bar", "(foo)(abc)(bar)") |
| |
| def test_PlainWithRegexAndWhitespaces(self): |
| self.__parsesTo("foo {{abc}}bar", "(foo)(\s+)(abc)(bar)") |
| self.__parsesTo("foo{{abc}} bar", "(foo)(abc)(\s+)(bar)") |
| self.__parsesTo("foo {{abc}} bar", "(foo)(\s+)(abc)(\s+)(bar)") |
| |
| def test_PlainWithVarAndWhitespaces(self): |
| self.__parsesTo("foo [[ABC:abc]]bar", "(foo)(\s+)(abc)(bar)") |
| self.__parsesTo("foo[[ABC:abc]] bar", "(foo)(abc)(\s+)(bar)") |
| self.__parsesTo("foo [[ABC:abc]] bar", "(foo)(\s+)(abc)(\s+)(bar)") |
| |
| def test_AllKinds(self): |
| self.__parsesTo("foo [[ABC:abc]]{{def}}bar", "(foo)(\s+)(abc)(def)(bar)") |
| self.__parsesTo("foo[[ABC:abc]] {{def}}bar", "(foo)(abc)(\s+)(def)(bar)") |
| self.__parsesTo("foo [[ABC:abc]] {{def}} bar", "(foo)(\s+)(abc)(\s+)(def)(\s+)(bar)") |
| |
| # Test that variables and patterns are parsed correctly |
| |
| def test_ValidPattern(self): |
| self.__parsesPattern("{{abc}}", "abc") |
| self.__parsesPattern("{{a[b]c}}", "a[b]c") |
| self.__parsesPattern("{{(a{bc})}}", "(a{bc})") |
| |
| def test_ValidRef(self): |
| self.__parsesVarRef("[[ABC]]", "ABC") |
| self.__parsesVarRef("[[A1BC2]]", "A1BC2") |
| |
| def test_ValidDef(self): |
| self.__parsesVarDef("[[ABC:abc]]", "ABC", "abc") |
| self.__parsesVarDef("[[ABC:ab:c]]", "ABC", "ab:c") |
| self.__parsesVarDef("[[ABC:a[b]c]]", "ABC", "a[b]c") |
| self.__parsesVarDef("[[ABC:(a[bc])]]", "ABC", "(a[bc])") |
| |
| def test_Empty(self): |
| self.__doesNotParse("{{}}", checker.CheckElement.Variant.Pattern) |
| self.__doesNotParse("[[]]", checker.CheckElement.Variant.VarRef) |
| self.__doesNotParse("[[:]]", checker.CheckElement.Variant.VarDef) |
| |
| def test_InvalidVarName(self): |
| self.__doesNotParse("[[0ABC]]", checker.CheckElement.Variant.VarRef) |
| self.__doesNotParse("[[AB=C]]", checker.CheckElement.Variant.VarRef) |
| self.__doesNotParse("[[ABC=]]", checker.CheckElement.Variant.VarRef) |
| self.__doesNotParse("[[0ABC:abc]]", checker.CheckElement.Variant.VarDef) |
| self.__doesNotParse("[[AB=C:abc]]", checker.CheckElement.Variant.VarDef) |
| self.__doesNotParse("[[ABC=:abc]]", checker.CheckElement.Variant.VarDef) |
| |
| def test_BodyMatchNotGreedy(self): |
| self.__parsesTo("{{abc}}{{def}}", "(abc)(def)") |
| self.__parsesTo("[[ABC:abc]][[DEF:def]]", "(abc)(def)") |
| |
| def test_NoVarDefsInNotChecks(self): |
| with self.assertRaises(CheckerException): |
| self.__tryParseNot("[[ABC:abc]]") |
| |
| class TestCheckLine_Match(unittest.TestCase): |
| def __matchSingle(self, checkString, outputString, varState={}): |
| checkLine = checker.CheckLine(checkString) |
| newVarState = checkLine.match(outputString, varState) |
| self.assertIsNotNone(newVarState) |
| return newVarState |
| |
| def __notMatchSingle(self, checkString, outputString, varState={}): |
| checkLine = checker.CheckLine(checkString) |
| self.assertIsNone(checkLine.match(outputString, varState)) |
| |
| def test_TextAndWhitespace(self): |
| self.__matchSingle("foo", "foo") |
| self.__matchSingle("foo", " foo ") |
| self.__matchSingle("foo", "foo bar") |
| self.__notMatchSingle("foo", "XfooX") |
| self.__notMatchSingle("foo", "zoo") |
| |
| self.__matchSingle("foo bar", "foo bar") |
| self.__matchSingle("foo bar", "abc foo bar def") |
| self.__matchSingle("foo bar", "foo foo bar bar") |
| |
| self.__matchSingle("foo bar", "foo X bar") |
| self.__notMatchSingle("foo bar", "foo Xbar") |
| |
| def test_Pattern(self): |
| self.__matchSingle("foo{{A|B}}bar", "fooAbar") |
| self.__matchSingle("foo{{A|B}}bar", "fooBbar") |
| self.__notMatchSingle("foo{{A|B}}bar", "fooCbar") |
| |
| def test_VariableReference(self): |
| self.__matchSingle("foo[[X]]bar", "foobar", {"X": ""}) |
| self.__matchSingle("foo[[X]]bar", "fooAbar", {"X": "A"}) |
| self.__matchSingle("foo[[X]]bar", "fooBbar", {"X": "B"}) |
| self.__notMatchSingle("foo[[X]]bar", "foobar", {"X": "A"}) |
| self.__notMatchSingle("foo[[X]]bar", "foo bar", {"X": "A"}) |
| with self.assertRaises(CheckerException): |
| self.__matchSingle("foo[[X]]bar", "foobar", {}) |
| |
| def test_VariableDefinition(self): |
| self.__matchSingle("foo[[X:A|B]]bar", "fooAbar") |
| self.__matchSingle("foo[[X:A|B]]bar", "fooBbar") |
| self.__notMatchSingle("foo[[X:A|B]]bar", "fooCbar") |
| |
| env = self.__matchSingle("foo[[X:A.*B]]bar", "fooABbar", {}) |
| self.assertEqual(env, {"X": "AB"}) |
| env = self.__matchSingle("foo[[X:A.*B]]bar", "fooAxxBbar", {}) |
| self.assertEqual(env, {"X": "AxxB"}) |
| |
| self.__matchSingle("foo[[X:A|B]]bar[[X]]baz", "fooAbarAbaz") |
| self.__matchSingle("foo[[X:A|B]]bar[[X]]baz", "fooBbarBbaz") |
| self.__notMatchSingle("foo[[X:A|B]]bar[[X]]baz", "fooAbarBbaz") |
| |
| def test_NoVariableRedefinition(self): |
| with self.assertRaises(CheckerException): |
| self.__matchSingle("[[X:...]][[X]][[X:...]][[X]]", "foofoobarbar") |
| |
| def test_EnvNotChangedOnPartialMatch(self): |
| env = {"Y": "foo"} |
| self.__notMatchSingle("[[X:A]]bar", "Abaz", env) |
| self.assertFalse("X" in env.keys()) |
| |
| def test_VariableContentEscaped(self): |
| self.__matchSingle("[[X:..]]foo[[X]]", ".*foo.*") |
| self.__notMatchSingle("[[X:..]]foo[[X]]", ".*fooAAAA") |
| |
| |
| CheckVariant = checker.CheckLine.Variant |
| |
| def prepareSingleCheck(line): |
| if isinstance(line, str): |
| return checker.CheckLine(line) |
| else: |
| return checker.CheckLine(line[0], line[1]) |
| |
| def prepareChecks(lines): |
| if isinstance(lines, str): |
| lines = lines.splitlines() |
| return list(map(lambda line: prepareSingleCheck(line), lines)) |
| |
| |
| class TestCheckGroup_Match(unittest.TestCase): |
| def __matchMulti(self, checkLines, outputString): |
| checkGroup = checker.CheckGroup("MyGroup", prepareChecks(checkLines)) |
| outputGroup = checker.OutputGroup("MyGroup", outputString.splitlines()) |
| return checkGroup.match(outputGroup) |
| |
| def __notMatchMulti(self, checkString, outputString): |
| with self.assertRaises(CheckerException): |
| self.__matchMulti(checkString, outputString) |
| |
| def test_TextAndPattern(self): |
| self.__matchMulti("""foo bar |
| abc {{def}}""", |
| """foo bar |
| abc def"""); |
| self.__matchMulti("""foo bar |
| abc {{de.}}""", |
| """======= |
| foo bar |
| ======= |
| abc de# |
| ======="""); |
| self.__notMatchMulti("""//XYZ: foo bar |
| //XYZ: abc {{def}}""", |
| """======= |
| foo bar |
| ======= |
| abc de# |
| ======="""); |
| |
| def test_Variables(self): |
| self.__matchMulti("""foo[[X:.]]bar |
| abc[[X]]def""", |
| """foo bar |
| abc def"""); |
| self.__matchMulti("""foo[[X:([0-9]+)]]bar |
| abc[[X]]def |
| ### [[X]] ###""", |
| """foo1234bar |
| abc1234def |
| ### 1234 ###"""); |
| |
| def test_Ordering(self): |
| self.__matchMulti([("foo", CheckVariant.InOrder), |
| ("bar", CheckVariant.InOrder)], |
| """foo |
| bar""") |
| self.__notMatchMulti([("foo", CheckVariant.InOrder), |
| ("bar", CheckVariant.InOrder)], |
| """bar |
| foo""") |
| self.__matchMulti([("abc", CheckVariant.DAG), |
| ("def", CheckVariant.DAG)], |
| """abc |
| def""") |
| self.__matchMulti([("abc", CheckVariant.DAG), |
| ("def", CheckVariant.DAG)], |
| """def |
| abc""") |
| self.__matchMulti([("foo", CheckVariant.InOrder), |
| ("abc", CheckVariant.DAG), |
| ("def", CheckVariant.DAG), |
| ("bar", CheckVariant.InOrder)], |
| """foo |
| def |
| abc |
| bar""") |
| self.__notMatchMulti([("foo", CheckVariant.InOrder), |
| ("abc", CheckVariant.DAG), |
| ("def", CheckVariant.DAG), |
| ("bar", CheckVariant.InOrder)], |
| """foo |
| abc |
| bar""") |
| self.__notMatchMulti([("foo", CheckVariant.InOrder), |
| ("abc", CheckVariant.DAG), |
| ("def", CheckVariant.DAG), |
| ("bar", CheckVariant.InOrder)], |
| """foo |
| def |
| bar""") |
| |
| def test_NotAssertions(self): |
| self.__matchMulti([("foo", CheckVariant.Not)], |
| """abc |
| def""") |
| self.__notMatchMulti([("foo", CheckVariant.Not)], |
| """abc foo |
| def""") |
| self.__notMatchMulti([("foo", CheckVariant.Not), |
| ("bar", CheckVariant.Not)], |
| """abc |
| def bar""") |
| |
| def test_LineOnlyMatchesOnce(self): |
| self.__matchMulti([("foo", CheckVariant.DAG), |
| ("foo", CheckVariant.DAG)], |
| """foo |
| foo""") |
| self.__notMatchMulti([("foo", CheckVariant.DAG), |
| ("foo", CheckVariant.DAG)], |
| """foo |
| bar""") |
| |
| class TestOutputFile_Parse(unittest.TestCase): |
| def __parsesTo(self, string, expected): |
| if isinstance(string, str): |
| string = unicode(string) |
| outputStream = io.StringIO(string) |
| return self.assertEqual(checker.OutputFile(outputStream).groups, expected) |
| |
| def test_NoInput(self): |
| self.__parsesTo(None, []) |
| self.__parsesTo("", []) |
| |
| def test_SingleGroup(self): |
| self.__parsesTo("""begin_compilation |
| method "MyMethod" |
| end_compilation |
| begin_cfg |
| name "pass1" |
| foo |
| bar |
| end_cfg""", |
| [ checker.OutputGroup("MyMethod pass1", [ "foo", "bar" ]) ]) |
| |
| def test_MultipleGroups(self): |
| self.__parsesTo("""begin_compilation |
| name "xyz1" |
| method "MyMethod1" |
| date 1234 |
| end_compilation |
| begin_cfg |
| name "pass1" |
| foo |
| bar |
| end_cfg |
| begin_cfg |
| name "pass2" |
| abc |
| def |
| end_cfg""", |
| [ checker.OutputGroup("MyMethod1 pass1", [ "foo", "bar" ]), |
| checker.OutputGroup("MyMethod1 pass2", [ "abc", "def" ]) ]) |
| |
| self.__parsesTo("""begin_compilation |
| name "xyz1" |
| method "MyMethod1" |
| date 1234 |
| end_compilation |
| begin_cfg |
| name "pass1" |
| foo |
| bar |
| end_cfg |
| begin_compilation |
| name "xyz2" |
| method "MyMethod2" |
| date 5678 |
| end_compilation |
| begin_cfg |
| name "pass2" |
| abc |
| def |
| end_cfg""", |
| [ checker.OutputGroup("MyMethod1 pass1", [ "foo", "bar" ]), |
| checker.OutputGroup("MyMethod2 pass2", [ "abc", "def" ]) ]) |
| |
| class TestCheckFile_Parse(unittest.TestCase): |
| def __parsesTo(self, string, expected): |
| if isinstance(string, str): |
| string = unicode(string) |
| checkStream = io.StringIO(string) |
| return self.assertEqual(checker.CheckFile("CHECK", checkStream).groups, expected) |
| |
| def test_NoInput(self): |
| self.__parsesTo(None, []) |
| self.__parsesTo("", []) |
| |
| def test_SingleGroup(self): |
| self.__parsesTo("""// CHECK-START: Example Group |
| // CHECK: foo |
| // CHECK: bar""", |
| [ checker.CheckGroup("Example Group", prepareChecks([ "foo", "bar" ])) ]) |
| |
| def test_MultipleGroups(self): |
| self.__parsesTo("""// CHECK-START: Example Group1 |
| // CHECK: foo |
| // CHECK: bar |
| // CHECK-START: Example Group2 |
| // CHECK: abc |
| // CHECK: def""", |
| [ checker.CheckGroup("Example Group1", prepareChecks([ "foo", "bar" ])), |
| checker.CheckGroup("Example Group2", prepareChecks([ "abc", "def" ])) ]) |
| |
| def test_CheckVariants(self): |
| self.__parsesTo("""// CHECK-START: Example Group |
| // CHECK: foo |
| // CHECK-NOT: bar |
| // CHECK-DAG: abc |
| // CHECK-DAG: def""", |
| [ checker.CheckGroup("Example Group", |
| prepareChecks([ ("foo", CheckVariant.InOrder), |
| ("bar", CheckVariant.Not), |
| ("abc", CheckVariant.DAG), |
| ("def", CheckVariant.DAG) ])) ]) |
| |
| if __name__ == '__main__': |
| checker.Logger.Verbosity = checker.Logger.Level.NoOutput |
| unittest.main() |