1#!/usr/bin/env python2
2#
3# Copyright (C) 2014 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#   http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17# This is a test file which exercises all feautres supported by the domain-
18# specific markup language implemented by Checker.
19
20import checker
21import io
22import unittest
23
24# The parent type of exception expected to be thrown by Checker during tests.
25# It must be specific enough to not cover exceptions thrown due to actual flaws
26# in Checker.
27CheckerException = SystemExit
28
29
30class TestCheckFile_PrefixExtraction(unittest.TestCase):
31  def __tryParse(self, string):
32    checkFile = checker.CheckFile(None, [])
33    return checkFile._extractLine("CHECK", string)
34
35  def test_InvalidFormat(self):
36    self.assertIsNone(self.__tryParse("CHECK"))
37    self.assertIsNone(self.__tryParse(":CHECK"))
38    self.assertIsNone(self.__tryParse("CHECK:"))
39    self.assertIsNone(self.__tryParse("//CHECK"))
40    self.assertIsNone(self.__tryParse("#CHECK"))
41
42    self.assertIsNotNone(self.__tryParse("//CHECK:foo"))
43    self.assertIsNotNone(self.__tryParse("#CHECK:bar"))
44
45  def test_InvalidLabel(self):
46    self.assertIsNone(self.__tryParse("//ACHECK:foo"))
47    self.assertIsNone(self.__tryParse("#ACHECK:foo"))
48
49  def test_NotFirstOnTheLine(self):
50    self.assertIsNone(self.__tryParse("A// CHECK: foo"))
51    self.assertIsNone(self.__tryParse("A # CHECK: foo"))
52    self.assertIsNone(self.__tryParse("// // CHECK: foo"))
53    self.assertIsNone(self.__tryParse("# # CHECK: foo"))
54
55  def test_WhitespaceAgnostic(self):
56    self.assertIsNotNone(self.__tryParse("  //CHECK: foo"))
57    self.assertIsNotNone(self.__tryParse("//  CHECK: foo"))
58    self.assertIsNotNone(self.__tryParse("    //CHECK: foo"))
59    self.assertIsNotNone(self.__tryParse("//    CHECK: foo"))
60
61
62class TestCheckLine_Parse(unittest.TestCase):
63  def __getPartPattern(self, linePart):
64    if linePart.variant == checker.CheckElement.Variant.Separator:
65      return "\s+"
66    else:
67      return linePart.pattern
68
69  def __getRegex(self, checkLine):
70    return "".join(map(lambda x: "(" + self.__getPartPattern(x) + ")", checkLine.lineParts))
71
72  def __tryParse(self, string):
73    return checker.CheckLine(string)
74
75  def __parsesTo(self, string, expected):
76    self.assertEqual(expected, self.__getRegex(self.__tryParse(string)))
77
78  def __tryParseNot(self, string):
79    return checker.CheckLine(string, checker.CheckLine.Variant.Not)
80
81  def __parsesPattern(self, string, pattern):
82    line = self.__tryParse(string)
83    self.assertEqual(1, len(line.lineParts))
84    self.assertEqual(checker.CheckElement.Variant.Pattern, line.lineParts[0].variant)
85    self.assertEqual(pattern, line.lineParts[0].pattern)
86
87  def __parsesVarRef(self, string, name):
88    line = self.__tryParse(string)
89    self.assertEqual(1, len(line.lineParts))
90    self.assertEqual(checker.CheckElement.Variant.VarRef, line.lineParts[0].variant)
91    self.assertEqual(name, line.lineParts[0].name)
92
93  def __parsesVarDef(self, string, name, body):
94    line = self.__tryParse(string)
95    self.assertEqual(1, len(line.lineParts))
96    self.assertEqual(checker.CheckElement.Variant.VarDef, line.lineParts[0].variant)
97    self.assertEqual(name, line.lineParts[0].name)
98    self.assertEqual(body, line.lineParts[0].pattern)
99
100  def __doesNotParse(self, string, partType):
101    line = self.__tryParse(string)
102    self.assertEqual(1, len(line.lineParts))
103    self.assertNotEqual(partType, line.lineParts[0].variant)
104
105  # Test that individual parts of the line are recognized
106
107  def test_TextOnly(self):
108    self.__parsesTo("foo", "(foo)")
109    self.__parsesTo("  foo  ", "(foo)")
110    self.__parsesTo("f$o^o", "(f\$o\^o)")
111
112  def test_TextWithWhitespace(self):
113    self.__parsesTo("foo bar", "(foo)(\s+)(bar)")
114    self.__parsesTo("foo   bar", "(foo)(\s+)(bar)")
115
116  def test_RegexOnly(self):
117    self.__parsesPattern("{{a?b.c}}", "a?b.c")
118
119  def test_VarRefOnly(self):
120    self.__parsesVarRef("[[ABC]]", "ABC")
121
122  def test_VarDefOnly(self):
123    self.__parsesVarDef("[[ABC:a?b.c]]", "ABC", "a?b.c")
124
125  def test_TextWithRegex(self):
126    self.__parsesTo("foo{{abc}}bar", "(foo)(abc)(bar)")
127
128  def test_TextWithVar(self):
129    self.__parsesTo("foo[[ABC:abc]]bar", "(foo)(abc)(bar)")
130
131  def test_PlainWithRegexAndWhitespaces(self):
132    self.__parsesTo("foo {{abc}}bar", "(foo)(\s+)(abc)(bar)")
133    self.__parsesTo("foo{{abc}} bar", "(foo)(abc)(\s+)(bar)")
134    self.__parsesTo("foo {{abc}} bar", "(foo)(\s+)(abc)(\s+)(bar)")
135
136  def test_PlainWithVarAndWhitespaces(self):
137    self.__parsesTo("foo [[ABC:abc]]bar", "(foo)(\s+)(abc)(bar)")
138    self.__parsesTo("foo[[ABC:abc]] bar", "(foo)(abc)(\s+)(bar)")
139    self.__parsesTo("foo [[ABC:abc]] bar", "(foo)(\s+)(abc)(\s+)(bar)")
140
141  def test_AllKinds(self):
142    self.__parsesTo("foo [[ABC:abc]]{{def}}bar", "(foo)(\s+)(abc)(def)(bar)")
143    self.__parsesTo("foo[[ABC:abc]] {{def}}bar", "(foo)(abc)(\s+)(def)(bar)")
144    self.__parsesTo("foo [[ABC:abc]] {{def}} bar", "(foo)(\s+)(abc)(\s+)(def)(\s+)(bar)")
145
146  # Test that variables and patterns are parsed correctly
147
148  def test_ValidPattern(self):
149    self.__parsesPattern("{{abc}}", "abc")
150    self.__parsesPattern("{{a[b]c}}", "a[b]c")
151    self.__parsesPattern("{{(a{bc})}}", "(a{bc})")
152
153  def test_ValidRef(self):
154    self.__parsesVarRef("[[ABC]]", "ABC")
155    self.__parsesVarRef("[[A1BC2]]", "A1BC2")
156
157  def test_ValidDef(self):
158    self.__parsesVarDef("[[ABC:abc]]", "ABC", "abc")
159    self.__parsesVarDef("[[ABC:ab:c]]", "ABC", "ab:c")
160    self.__parsesVarDef("[[ABC:a[b]c]]", "ABC", "a[b]c")
161    self.__parsesVarDef("[[ABC:(a[bc])]]", "ABC", "(a[bc])")
162
163  def test_Empty(self):
164    self.__doesNotParse("{{}}", checker.CheckElement.Variant.Pattern)
165    self.__doesNotParse("[[]]", checker.CheckElement.Variant.VarRef)
166    self.__doesNotParse("[[:]]", checker.CheckElement.Variant.VarDef)
167
168  def test_InvalidVarName(self):
169    self.__doesNotParse("[[0ABC]]", checker.CheckElement.Variant.VarRef)
170    self.__doesNotParse("[[AB=C]]", checker.CheckElement.Variant.VarRef)
171    self.__doesNotParse("[[ABC=]]", checker.CheckElement.Variant.VarRef)
172    self.__doesNotParse("[[0ABC:abc]]", checker.CheckElement.Variant.VarDef)
173    self.__doesNotParse("[[AB=C:abc]]", checker.CheckElement.Variant.VarDef)
174    self.__doesNotParse("[[ABC=:abc]]", checker.CheckElement.Variant.VarDef)
175
176  def test_BodyMatchNotGreedy(self):
177    self.__parsesTo("{{abc}}{{def}}", "(abc)(def)")
178    self.__parsesTo("[[ABC:abc]][[DEF:def]]", "(abc)(def)")
179
180  def test_NoVarDefsInNotChecks(self):
181    with self.assertRaises(CheckerException):
182      self.__tryParseNot("[[ABC:abc]]")
183
184class TestCheckLine_Match(unittest.TestCase):
185  def __matchSingle(self, checkString, outputString, varState={}):
186    checkLine = checker.CheckLine(checkString)
187    newVarState = checkLine.match(outputString, varState)
188    self.assertIsNotNone(newVarState)
189    return newVarState
190
191  def __notMatchSingle(self, checkString, outputString, varState={}):
192    checkLine = checker.CheckLine(checkString)
193    self.assertIsNone(checkLine.match(outputString, varState))
194
195  def test_TextAndWhitespace(self):
196    self.__matchSingle("foo", "foo")
197    self.__matchSingle("foo", "  foo  ")
198    self.__matchSingle("foo", "foo bar")
199    self.__notMatchSingle("foo", "XfooX")
200    self.__notMatchSingle("foo", "zoo")
201
202    self.__matchSingle("foo bar", "foo   bar")
203    self.__matchSingle("foo bar", "abc foo bar def")
204    self.__matchSingle("foo bar", "foo foo bar bar")
205
206    self.__matchSingle("foo bar", "foo X bar")
207    self.__notMatchSingle("foo bar", "foo Xbar")
208
209  def test_Pattern(self):
210    self.__matchSingle("foo{{A|B}}bar", "fooAbar")
211    self.__matchSingle("foo{{A|B}}bar", "fooBbar")
212    self.__notMatchSingle("foo{{A|B}}bar", "fooCbar")
213
214  def test_VariableReference(self):
215    self.__matchSingle("foo[[X]]bar", "foobar", {"X": ""})
216    self.__matchSingle("foo[[X]]bar", "fooAbar", {"X": "A"})
217    self.__matchSingle("foo[[X]]bar", "fooBbar", {"X": "B"})
218    self.__notMatchSingle("foo[[X]]bar", "foobar", {"X": "A"})
219    self.__notMatchSingle("foo[[X]]bar", "foo bar", {"X": "A"})
220    with self.assertRaises(CheckerException):
221      self.__matchSingle("foo[[X]]bar", "foobar", {})
222
223  def test_VariableDefinition(self):
224    self.__matchSingle("foo[[X:A|B]]bar", "fooAbar")
225    self.__matchSingle("foo[[X:A|B]]bar", "fooBbar")
226    self.__notMatchSingle("foo[[X:A|B]]bar", "fooCbar")
227
228    env = self.__matchSingle("foo[[X:A.*B]]bar", "fooABbar", {})
229    self.assertEqual(env, {"X": "AB"})
230    env = self.__matchSingle("foo[[X:A.*B]]bar", "fooAxxBbar", {})
231    self.assertEqual(env, {"X": "AxxB"})
232
233    self.__matchSingle("foo[[X:A|B]]bar[[X]]baz", "fooAbarAbaz")
234    self.__matchSingle("foo[[X:A|B]]bar[[X]]baz", "fooBbarBbaz")
235    self.__notMatchSingle("foo[[X:A|B]]bar[[X]]baz", "fooAbarBbaz")
236
237  def test_NoVariableRedefinition(self):
238    with self.assertRaises(CheckerException):
239      self.__matchSingle("[[X:...]][[X]][[X:...]][[X]]", "foofoobarbar")
240
241  def test_EnvNotChangedOnPartialMatch(self):
242    env = {"Y": "foo"}
243    self.__notMatchSingle("[[X:A]]bar", "Abaz", env)
244    self.assertFalse("X" in env.keys())
245
246  def test_VariableContentEscaped(self):
247    self.__matchSingle("[[X:..]]foo[[X]]", ".*foo.*")
248    self.__notMatchSingle("[[X:..]]foo[[X]]", ".*fooAAAA")
249
250
251CheckVariant = checker.CheckLine.Variant
252
253def prepareSingleCheck(line):
254  if isinstance(line, str):
255    return checker.CheckLine(line)
256  else:
257    return checker.CheckLine(line[0], line[1])
258
259def prepareChecks(lines):
260  if isinstance(lines, str):
261    lines = lines.splitlines()
262  return list(map(lambda line: prepareSingleCheck(line), lines))
263
264
265class TestCheckGroup_Match(unittest.TestCase):
266  def __matchMulti(self, checkLines, outputString):
267    checkGroup = checker.CheckGroup("MyGroup", prepareChecks(checkLines))
268    outputGroup = checker.OutputGroup("MyGroup", outputString.splitlines())
269    return checkGroup.match(outputGroup)
270
271  def __notMatchMulti(self, checkString, outputString):
272    with self.assertRaises(CheckerException):
273      self.__matchMulti(checkString, outputString)
274
275  def test_TextAndPattern(self):
276    self.__matchMulti("""foo bar
277                         abc {{def}}""",
278                      """foo bar
279                         abc def""");
280    self.__matchMulti("""foo bar
281                         abc {{de.}}""",
282                      """=======
283                         foo bar
284                         =======
285                         abc de#
286                         =======""");
287    self.__notMatchMulti("""//XYZ: foo bar
288                            //XYZ: abc {{def}}""",
289                         """=======
290                            foo bar
291                            =======
292                            abc de#
293                            =======""");
294
295  def test_Variables(self):
296    self.__matchMulti("""foo[[X:.]]bar
297                         abc[[X]]def""",
298                      """foo bar
299                         abc def""");
300    self.__matchMulti("""foo[[X:([0-9]+)]]bar
301                         abc[[X]]def
302                         ### [[X]] ###""",
303                      """foo1234bar
304                         abc1234def
305                         ### 1234 ###""");
306
307  def test_Ordering(self):
308    self.__matchMulti([("foo", CheckVariant.InOrder),
309                       ("bar", CheckVariant.InOrder)],
310                      """foo
311                         bar""")
312    self.__notMatchMulti([("foo", CheckVariant.InOrder),
313                          ("bar", CheckVariant.InOrder)],
314                         """bar
315                            foo""")
316    self.__matchMulti([("abc", CheckVariant.DAG),
317                       ("def", CheckVariant.DAG)],
318                      """abc
319                         def""")
320    self.__matchMulti([("abc", CheckVariant.DAG),
321                       ("def", CheckVariant.DAG)],
322                      """def
323                         abc""")
324    self.__matchMulti([("foo", CheckVariant.InOrder),
325                       ("abc", CheckVariant.DAG),
326                       ("def", CheckVariant.DAG),
327                       ("bar", CheckVariant.InOrder)],
328                      """foo
329                         def
330                         abc
331                         bar""")
332    self.__notMatchMulti([("foo", CheckVariant.InOrder),
333                          ("abc", CheckVariant.DAG),
334                          ("def", CheckVariant.DAG),
335                          ("bar", CheckVariant.InOrder)],
336                         """foo
337                            abc
338                            bar""")
339    self.__notMatchMulti([("foo", CheckVariant.InOrder),
340                          ("abc", CheckVariant.DAG),
341                          ("def", CheckVariant.DAG),
342                          ("bar", CheckVariant.InOrder)],
343                         """foo
344                            def
345                            bar""")
346
347  def test_NotAssertions(self):
348    self.__matchMulti([("foo", CheckVariant.Not)],
349                      """abc
350                         def""")
351    self.__notMatchMulti([("foo", CheckVariant.Not)],
352                         """abc foo
353                            def""")
354    self.__notMatchMulti([("foo", CheckVariant.Not),
355                          ("bar", CheckVariant.Not)],
356                         """abc
357                            def bar""")
358
359  def test_LineOnlyMatchesOnce(self):
360    self.__matchMulti([("foo", CheckVariant.DAG),
361                       ("foo", CheckVariant.DAG)],
362                       """foo
363                          foo""")
364    self.__notMatchMulti([("foo", CheckVariant.DAG),
365                          ("foo", CheckVariant.DAG)],
366                          """foo
367                             bar""")
368
369class TestOutputFile_Parse(unittest.TestCase):
370  def __parsesTo(self, string, expected):
371    if isinstance(string, str):
372      string = unicode(string)
373    outputStream = io.StringIO(string)
374    return self.assertEqual(checker.OutputFile(outputStream).groups, expected)
375
376  def test_NoInput(self):
377    self.__parsesTo(None, [])
378    self.__parsesTo("", [])
379
380  def test_SingleGroup(self):
381    self.__parsesTo("""begin_compilation
382                         method "MyMethod"
383                       end_compilation
384                       begin_cfg
385                         name "pass1"
386                         foo
387                         bar
388                       end_cfg""",
389                    [ checker.OutputGroup("MyMethod pass1", [ "foo", "bar" ]) ])
390
391  def test_MultipleGroups(self):
392    self.__parsesTo("""begin_compilation
393                         name "xyz1"
394                         method "MyMethod1"
395                         date 1234
396                       end_compilation
397                       begin_cfg
398                         name "pass1"
399                         foo
400                         bar
401                       end_cfg
402                       begin_cfg
403                         name "pass2"
404                         abc
405                         def
406                       end_cfg""",
407                    [ checker.OutputGroup("MyMethod1 pass1", [ "foo", "bar" ]),
408                      checker.OutputGroup("MyMethod1 pass2", [ "abc", "def" ]) ])
409
410    self.__parsesTo("""begin_compilation
411                         name "xyz1"
412                         method "MyMethod1"
413                         date 1234
414                       end_compilation
415                       begin_cfg
416                         name "pass1"
417                         foo
418                         bar
419                       end_cfg
420                       begin_compilation
421                         name "xyz2"
422                         method "MyMethod2"
423                         date 5678
424                       end_compilation
425                       begin_cfg
426                         name "pass2"
427                         abc
428                         def
429                       end_cfg""",
430                    [ checker.OutputGroup("MyMethod1 pass1", [ "foo", "bar" ]),
431                      checker.OutputGroup("MyMethod2 pass2", [ "abc", "def" ]) ])
432
433class TestCheckFile_Parse(unittest.TestCase):
434  def __parsesTo(self, string, expected):
435    if isinstance(string, str):
436      string = unicode(string)
437    checkStream = io.StringIO(string)
438    return self.assertEqual(checker.CheckFile("CHECK", checkStream).groups, expected)
439
440  def test_NoInput(self):
441    self.__parsesTo(None, [])
442    self.__parsesTo("", [])
443
444  def test_SingleGroup(self):
445    self.__parsesTo("""// CHECK-START: Example Group
446                       // CHECK:  foo
447                       // CHECK:    bar""",
448                    [ checker.CheckGroup("Example Group", prepareChecks([ "foo", "bar" ])) ])
449
450  def test_MultipleGroups(self):
451    self.__parsesTo("""// CHECK-START: Example Group1
452                       // CHECK: foo
453                       // CHECK: bar
454                       // CHECK-START: Example Group2
455                       // CHECK: abc
456                       // CHECK: def""",
457                    [ checker.CheckGroup("Example Group1", prepareChecks([ "foo", "bar" ])),
458                      checker.CheckGroup("Example Group2", prepareChecks([ "abc", "def" ])) ])
459
460  def test_CheckVariants(self):
461    self.__parsesTo("""// CHECK-START: Example Group
462                       // CHECK:     foo
463                       // CHECK-NOT: bar
464                       // CHECK-DAG: abc
465                       // CHECK-DAG: def""",
466                    [ checker.CheckGroup("Example Group",
467                                         prepareChecks([ ("foo", CheckVariant.InOrder),
468                                                         ("bar", CheckVariant.Not),
469                                                         ("abc", CheckVariant.DAG),
470                                                         ("def", CheckVariant.DAG) ])) ])
471
472if __name__ == '__main__':
473  checker.Logger.Verbosity = checker.Logger.Level.NoOutput
474  unittest.main()
475