From 711411957a433555eda4bcf8d1f05aabf04425e8 Mon Sep 17 00:00:00 2001 From: David Brazdil Date: Tue, 19 May 2015 18:29:40 +0100 Subject: [PATCH] ART: Implement next-line assertions in Checker Some tests require verifying an exact sequence of lines in the graph dump. This was already possible by inserting 'CHECK-NOT: {{.*}}' between the individual lines, but hardly a convenient way of doing so. This patch introduces a new 'CHECK-NEXT' kind of assertions that replaces the old method and will become useful for testing assembly. Change-Id: I1bb951707bda44320166dc7ef828866a6957a113 --- test/476-checker-ctor-memory-barrier/src/Main.java | 93 ++++++++++------------ tools/checker/file_format/checker/parser.py | 5 ++ tools/checker/file_format/checker/struct.py | 11 ++- tools/checker/file_format/checker/test.py | 43 ++++++++-- tools/checker/match/file.py | 5 ++ tools/checker/match/test.py | 48 +++++++++++ 6 files changed, 145 insertions(+), 60 deletions(-) diff --git a/test/476-checker-ctor-memory-barrier/src/Main.java b/test/476-checker-ctor-memory-barrier/src/Main.java index 75cb1d7b9..f24dc4aba 100644 --- a/test/476-checker-ctor-memory-barrier/src/Main.java +++ b/test/476-checker-ctor-memory-barrier/src/Main.java @@ -27,9 +27,8 @@ class ClassWithFinals { public ClassWithFinals obj; // CHECK-START: void ClassWithFinals.(boolean) register (after) - // CHECK: MemoryBarrier kind:StoreStore - // CHECK-NOT: {{.*}} - // CHECK: ReturnVoid + // CHECK: MemoryBarrier kind:StoreStore + // CHECK-NEXT: ReturnVoid public ClassWithFinals(boolean cond) { x = 0; if (cond) { @@ -39,18 +38,16 @@ class ClassWithFinals { } // CHECK-START: void ClassWithFinals.() register (after) - // CHECK: MemoryBarrier kind:StoreStore - // CHECK-NOT: {{.*}} - // CHECK: ReturnVoid + // CHECK: MemoryBarrier kind:StoreStore + // CHECK-NEXT: ReturnVoid public ClassWithFinals() { x = 0; } // CHECK-START: void ClassWithFinals.(int) register (after) - // CHECK: MemoryBarrier kind:StoreStore - // CHECK: MemoryBarrier kind:StoreStore - // CHECK-NOT: {{.*}} - // CHECK: ReturnVoid + // CHECK: MemoryBarrier kind:StoreStore + // CHECK: MemoryBarrier kind:StoreStore + // CHECK-NEXT: ReturnVoid public ClassWithFinals(int x) { // This should have two barriers: // - one for the constructor @@ -62,33 +59,32 @@ class ClassWithFinals { class InheritFromClassWithFinals extends ClassWithFinals { // CHECK-START: void InheritFromClassWithFinals.() register (after) - // CHECK: MemoryBarrier kind:StoreStore - // CHECK-NOT: {{.*}} - // CHECK: ReturnVoid + // CHECK: MemoryBarrier kind:StoreStore + // CHECK-NEXT: ReturnVoid // CHECK-START: void InheritFromClassWithFinals.() register (after) - // CHECK-NOT: InvokeStaticOrDirect + // CHECK-NOT: InvokeStaticOrDirect public InheritFromClassWithFinals() { // Should inline the super constructor. } // CHECK-START: void InheritFromClassWithFinals.(boolean) register (after) - // CHECK: InvokeStaticOrDirect + // CHECK: InvokeStaticOrDirect // CHECK-START: void InheritFromClassWithFinals.(boolean) register (after) - // CHECK-NOT: MemoryBarrier kind:StoreStore + // CHECK-NOT: MemoryBarrier kind:StoreStore public InheritFromClassWithFinals(boolean cond) { super(cond); // should not inline the super constructor } // CHECK-START: void InheritFromClassWithFinals.(int) register (after) - // CHECK: MemoryBarrier kind:StoreStore - // CHECK: MemoryBarrier kind:StoreStore - // CHECK: ReturnVoid + // CHECK: MemoryBarrier kind:StoreStore + // CHECK: MemoryBarrier kind:StoreStore + // CHECK: ReturnVoid // CHECK-START: void InheritFromClassWithFinals.(int) register (after) - // CHECK-NOT: InvokeStaticOrDirect + // CHECK-NOT: InvokeStaticOrDirect public InheritFromClassWithFinals(int unused) { // Should inline the super constructor and insert a memory barrier. @@ -101,9 +97,8 @@ class HaveFinalsAndInheritFromClassWithFinals extends ClassWithFinals { final int y; // CHECK-START: void HaveFinalsAndInheritFromClassWithFinals.() register (after) - // CHECK: MemoryBarrier kind:StoreStore - // CHECK-NOT: {{.*}} - // CHECK: ReturnVoid + // CHECK: MemoryBarrier kind:StoreStore + // CHECK-NEXT: ReturnVoid // CHECK-START: void HaveFinalsAndInheritFromClassWithFinals.() register (after) // CHECK-NOT: InvokeStaticOrDirect @@ -113,10 +108,9 @@ class HaveFinalsAndInheritFromClassWithFinals extends ClassWithFinals { } // CHECK-START: void HaveFinalsAndInheritFromClassWithFinals.(boolean) register (after) - // CHECK: InvokeStaticOrDirect - // CHECK: MemoryBarrier kind:StoreStore - // CHECK-NOT: {{.*}} - // CHECK: ReturnVoid + // CHECK: InvokeStaticOrDirect + // CHECK: MemoryBarrier kind:StoreStore + // CHECK-NEXT: ReturnVoid public HaveFinalsAndInheritFromClassWithFinals(boolean cond) { super(cond); // should not inline the super constructor @@ -124,14 +118,13 @@ class HaveFinalsAndInheritFromClassWithFinals extends ClassWithFinals { } // CHECK-START: void HaveFinalsAndInheritFromClassWithFinals.(int) register (after) - // CHECK: MemoryBarrier kind:StoreStore - // CHECK: MemoryBarrier kind:StoreStore - // CHECK: MemoryBarrier kind:StoreStore - // CHECK-NOT: {{.*}} - // CHECK: ReturnVoid + // CHECK: MemoryBarrier kind:StoreStore + // CHECK: MemoryBarrier kind:StoreStore + // CHECK: MemoryBarrier kind:StoreStore + // CHECK-NEXT: ReturnVoid // CHECK-START: void HaveFinalsAndInheritFromClassWithFinals.(int) register (after) - // CHECK-NOT: InvokeStaticOrDirect + // CHECK-NOT: InvokeStaticOrDirect public HaveFinalsAndInheritFromClassWithFinals(int unused) { // Should inline the super constructor and keep just one memory barrier. y = 0; @@ -146,55 +139,51 @@ class HaveFinalsAndInheritFromClassWithFinals extends ClassWithFinals { public class Main { // CHECK-START: ClassWithFinals Main.noInlineNoConstructorBarrier() register (after) - // CHECK: InvokeStaticOrDirect + // CHECK: InvokeStaticOrDirect // CHECK-START: ClassWithFinals Main.noInlineNoConstructorBarrier() register (after) - // CHECK-NOT: MemoryBarrier kind:StoreStore + // CHECK-NOT: MemoryBarrier kind:StoreStore public static ClassWithFinals noInlineNoConstructorBarrier() { return new ClassWithFinals(false); } // CHECK-START: void Main.inlineNew() register (after) - // CHECK: MemoryBarrier kind:StoreStore - // CHECK-NOT: {{.*}} - // CHECK: ReturnVoid + // CHECK: MemoryBarrier kind:StoreStore + // CHECK-NEXT: ReturnVoid // CHECK-START: void Main.inlineNew() register (after) - // CHECK-NOT: InvokeStaticOrDirect + // CHECK-NOT: InvokeStaticOrDirect public static void inlineNew() { new ClassWithFinals(); } // CHECK-START: void Main.inlineNew1() register (after) - // CHECK: MemoryBarrier kind:StoreStore - // CHECK-NOT: {{.*}} - // CHECK: ReturnVoid + // CHECK: MemoryBarrier kind:StoreStore + // CHECK-NEXT: ReturnVoid // CHECK-START: void Main.inlineNew1() register (after) - // CHECK-NOT: InvokeStaticOrDirect + // CHECK-NOT: InvokeStaticOrDirect public static void inlineNew1() { new InheritFromClassWithFinals(); } // CHECK-START: void Main.inlineNew2() register (after) - // CHECK: MemoryBarrier kind:StoreStore - // CHECK-NOT: {{.*}} - // CHECK: ReturnVoid + // CHECK: MemoryBarrier kind:StoreStore + // CHECK-NEXT: ReturnVoid // CHECK-START: void Main.inlineNew2() register (after) - // CHECK-NOT: InvokeStaticOrDirect + // CHECK-NOT: InvokeStaticOrDirect public static void inlineNew2() { new HaveFinalsAndInheritFromClassWithFinals(); } // CHECK-START: void Main.inlineNew3() register (after) - // CHECK: MemoryBarrier kind:StoreStore - // CHECK: MemoryBarrier kind:StoreStore - // CHECK-NOT: {{.*}} - // CHECK: ReturnVoid + // CHECK: MemoryBarrier kind:StoreStore + // CHECK: MemoryBarrier kind:StoreStore + // CHECK-NEXT: ReturnVoid // CHECK-START: void Main.inlineNew3() register (after) - // CHECK-NOT: InvokeStaticOrDirect + // CHECK-NOT: InvokeStaticOrDirect public static void inlineNew3() { new HaveFinalsAndInheritFromClassWithFinals(); new HaveFinalsAndInheritFromClassWithFinals(); diff --git a/tools/checker/file_format/checker/parser.py b/tools/checker/file_format/checker/parser.py index d7a38dab4..4eed39129 100644 --- a/tools/checker/file_format/checker/parser.py +++ b/tools/checker/file_format/checker/parser.py @@ -54,6 +54,11 @@ def __processLine(line, lineNo, prefix): if plainLine is not None: return (plainLine, TestAssertion.Variant.InOrder, lineNo), None + # 'CHECK-NEXT' lines are in-order but must match the very next line. + nextLine = __extractLine(prefix + "-NEXT", line) + if nextLine is not None: + return (nextLine, TestAssertion.Variant.NextLine, lineNo), None + # 'CHECK-DAG' lines are no-order assertions. dagLine = __extractLine(prefix + "-DAG", line) if dagLine is not None: diff --git a/tools/checker/file_format/checker/struct.py b/tools/checker/file_format/checker/struct.py index 381c92bb2..6a541428d 100644 --- a/tools/checker/file_format/checker/struct.py +++ b/tools/checker/file_format/checker/struct.py @@ -42,7 +42,7 @@ class TestCase(PrintableMixin): self.startLineNo = startLineNo if not self.name: - Logger.fail("Test case does not have a name", self.parent.fileName, self.startLineNo) + Logger.fail("Test case does not have a name", self.fileName, self.startLineNo) self.parent.addTestCase(self) @@ -51,6 +51,13 @@ class TestCase(PrintableMixin): return self.parent.fileName def addAssertion(self, new_assertion): + if new_assertion.variant == TestAssertion.Variant.NextLine: + if not self.assertions or \ + (self.assertions[-1].variant != TestAssertion.Variant.InOrder and \ + self.assertions[-1].variant != TestAssertion.Variant.NextLine): + Logger.fail("A next-line assertion can only be placed after an " + "in-order assertion or another next-line assertion.", + new_assertion.fileName, new_assertion.lineNo) self.assertions.append(new_assertion) def __eq__(self, other): @@ -63,7 +70,7 @@ class TestAssertion(PrintableMixin): class Variant(object): """Supported types of assertions.""" - InOrder, DAG, Not = range(3) + InOrder, NextLine, DAG, Not = range(4) def __init__(self, parent, variant, originalText, lineNo): assert isinstance(parent, TestCase) diff --git a/tools/checker/file_format/checker/test.py b/tools/checker/file_format/checker/test.py index 475e8c3d0..453deedf4 100644 --- a/tools/checker/file_format/checker/test.py +++ b/tools/checker/file_format/checker/test.py @@ -192,9 +192,12 @@ class CheckerParser_FileLayoutTest(unittest.TestCase): def assertParsesTo(self, checkerText, expectedData): expectedFile = self.createFile(expectedData) - actualFile = ParseCheckerStream("", "CHECK", io.StringIO(ToUnicode(checkerText))) + actualFile = self.parse(checkerText) return self.assertEqual(expectedFile, actualFile) + def parse(self, checkerText): + return ParseCheckerStream("", "CHECK", io.StringIO(ToUnicode(checkerText))) + def test_EmptyFile(self): self.assertParsesTo("", []) @@ -227,12 +230,40 @@ class CheckerParser_FileLayoutTest(unittest.TestCase): self.assertParsesTo( """ // CHECK-START: Example Group - // CHECK: foo - // CHECK-NOT: bar - // CHECK-DAG: abc - // CHECK-DAG: def + // CHECK: foo1 + // CHECK: foo2 + // CHECK-NEXT: foo3 + // CHECK-NEXT: foo4 + // CHECK-NOT: bar + // CHECK-DAG: abc + // CHECK-DAG: def """, - [ ( "Example Group", [ ("foo", TestAssertion.Variant.InOrder), + [ ( "Example Group", [ ("foo1", TestAssertion.Variant.InOrder), + ("foo2", TestAssertion.Variant.InOrder), + ("foo3", TestAssertion.Variant.NextLine), + ("foo4", TestAssertion.Variant.NextLine), ("bar", TestAssertion.Variant.Not), ("abc", TestAssertion.Variant.DAG), ("def", TestAssertion.Variant.DAG) ] ) ]) + + def test_MisplacedNext(self): + with self.assertRaises(CheckerException): + self.parse( + """ + // CHECK-START: Example Group + // CHECK-DAG: foo + // CHECK-NEXT: bar + """) + with self.assertRaises(CheckerException): + self.parse( + """ + // CHECK-START: Example Group + // CHECK-NOT: foo + // CHECK-NEXT: bar + """) + with self.assertRaises(CheckerException): + self.parse( + """ + // CHECK-START: Example Group + // CHECK-NEXT: bar + """) diff --git a/tools/checker/match/file.py b/tools/checker/match/file.py index 6cff2bf53..b22211ab5 100644 --- a/tools/checker/match/file.py +++ b/tools/checker/match/file.py @@ -127,6 +127,11 @@ def MatchTestCase(testCase, c1Pass): assert len(assertionGroup) == 1 scope = MatchScope(matchFrom, c1Length) match = findMatchingLine(assertionGroup[0], c1Pass, scope, variables) + elif assertionGroup[0].variant == TestAssertion.Variant.NextLine: + # Single next-line assertion. Test if the current line matches. + assert len(assertionGroup) == 1 + scope = MatchScope(matchFrom, matchFrom + 1) + match = findMatchingLine(assertionGroup[0], c1Pass, scope, variables) else: # A group of DAG assertions. Match them all starting from the same point. assert assertionGroup[0].variant == TestAssertion.Variant.DAG diff --git a/tools/checker/match/test.py b/tools/checker/match/test.py index e4dd78447..348c1d2b3 100644 --- a/tools/checker/match/test.py +++ b/tools/checker/match/test.py @@ -195,6 +195,54 @@ class MatchFiles_Test(unittest.TestCase): foo """) + def test_NextLineAssertions(self): + self.assertMatches( + """ + // CHECK: foo + // CHECK-NEXT: bar + // CHECK-NEXT: abc + // CHECK: def + """, + """ + foo + bar + abc + def + """) + self.assertMatches( + """ + // CHECK: foo + // CHECK-NEXT: bar + // CHECK: def + """, + """ + foo + bar + abc + def + """) + self.assertDoesNotMatch( + """ + // CHECK: foo + // CHECK-NEXT: bar + """, + """ + foo + abc + bar + """) + + self.assertDoesNotMatch( + """ + // CHECK: foo + // CHECK-NEXT: bar + """, + """ + bar + foo + abc + """) + def test_DagAssertions(self): self.assertMatches( """ -- 2.11.0