diff --git a/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java b/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java index 9728117437897b..c9098a007a801a 100644 --- a/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java +++ b/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java @@ -195,6 +195,7 @@ import java.util.TreeMap; import java.util.UUID; import java.util.function.Predicate; +import java.util.regex.Pattern; import javax.annotation.Nullable; import net.starlark.java.eval.StarlarkSemantics; import org.junit.Before; @@ -1211,6 +1212,31 @@ protected Event checkError(String packageName, return assertContainsEvent(expectedErrorMessage); } + /** + * Check that configuration of the target named 'ruleName' in the specified BUILD file fails with + * an error message matching 'expectedErrorPattern'. + * + * @param packageName the package name of the generated BUILD file + * @param ruleName the rule name for the rule in the generated BUILD file + * @param expectedErrorPattern a regex that matches the expected error. + * @param lines the text of the rule. + * @return the found error. + */ + protected Event checkError( + String packageName, String ruleName, Pattern expectedErrorPattern, String... lines) + throws Exception { + eventCollector.clear(); + reporter.removeHandler(failFastHandler); // expect errors + ConfiguredTarget target = scratchConfiguredTarget(packageName, ruleName, lines); + if (target != null) { + assertWithMessage( + "Rule '" + "//" + packageName + ":" + ruleName + "' did not contain an error") + .that(view.hasErrors(target)) + .isTrue(); + } + return assertContainsEvent(expectedErrorPattern); + } + /** * Check that configuration of the target named 'label' fails with an error message containing * 'expectedErrorMessage'. diff --git a/src/test/java/com/google/devtools/build/lib/buildtool/CustomRealFilesystemBuildIntegrationTest.java b/src/test/java/com/google/devtools/build/lib/buildtool/CustomRealFilesystemBuildIntegrationTest.java index 84d31cbc9d54dd..f13093b66f6cb2 100644 --- a/src/test/java/com/google/devtools/build/lib/buildtool/CustomRealFilesystemBuildIntegrationTest.java +++ b/src/test/java/com/google/devtools/build/lib/buildtool/CustomRealFilesystemBuildIntegrationTest.java @@ -50,6 +50,7 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.regex.Pattern; import org.junit.Test; import org.junit.runner.RunWith; @@ -353,11 +354,12 @@ public void ioExceptionReadingBuildFileForDiscoveredInput() throws Exception { buildTarget("//hello:hello"); BuildFailedException e = assertThrows(BuildFailedException.class, () -> buildTarget("//hello:hello")); - MoreAsserts.assertContainsEventRegex( + MoreAsserts.assertContainsEvent( events.collector(), - "^ERROR.*Compiling hello/hello.cc failed: Unable to resolve hello/subdir/undeclared.h as an" - + " artifact: no such package 'hello/subdir': IO errors while looking for BUILD file" - + " reading .*hello/subdir/BUILD: nope"); + Pattern.compile( + "^ERROR.*Compiling hello/hello.cc failed: Unable to resolve hello/subdir/undeclared.h" + + " as an artifact: no such package 'hello/subdir': IO errors while looking for" + + " BUILD file reading .*hello/subdir/BUILD: nope")); assertThat(e.getDetailedExitCode().getFailureDetail()) .comparingExpectedFieldsOnly() .isEqualTo( @@ -382,12 +384,14 @@ public void inconsistentExceptionReadingBuildFileForDiscoveredInput() throws Exc customFileSystem.errorInsideStat(buildFile, 0); BuildFailedException e = assertThrows(BuildFailedException.class, () -> buildTarget("//hello:hello")); - MoreAsserts.assertContainsEventRegex( + MoreAsserts.assertContainsEvent( events.collector(), - ".*Compiling hello/hello.cc failed: Unable to resolve hello/subdir/undeclared.h as an" - + " artifact: Inconsistent filesystem operations. 'stat' said .*/hello/subdir/BUILD is" - + " a file but then we later encountered error 'nope for .*/hello/subdir/BUILD' which" - + " indicates that .*/hello/subdir/BUILD is no longer a file.*"); + Pattern.compile( + ".*Compiling hello/hello.cc failed: Unable to resolve hello/subdir/undeclared.h as an" + + " artifact: Inconsistent filesystem operations. 'stat' said" + + " .*/hello/subdir/BUILD is a file but then we later encountered error 'nope for" + + " .*/hello/subdir/BUILD' which indicates that .*/hello/subdir/BUILD is no longer" + + " a file.*")); events.assertContainsError("hello/subdir/BUILD "); assertThat(e.getDetailedExitCode().getFailureDetail()) .comparingExpectedFieldsOnly() @@ -529,14 +533,16 @@ public void ioExceptionInTopLevelSource_keepGoing() throws Exception { @Test public void ioExceptionInTopLevelSource_noKeepGoing() throws Exception { runIoExceptionInTopLevelSource(); - MoreAsserts.assertContainsEventRegex( + MoreAsserts.assertContainsEvent( events.collector(), - ".*foo/BUILD:2:11: //foo:foo: (error reading file '//foo:error.in': nope|missing input file" - + " '//foo:missing.in')"); - MoreAsserts.assertContainsEventRegex( + Pattern.compile( + ".*foo/BUILD:2:11: //foo:foo: (error reading file '//foo:error.in': nope|missing input" + + " file '//foo:missing.in')")); + MoreAsserts.assertContainsEvent( events.collector(), - ".*(1 input file\\(s\\) (are in error|do not exist)|2 input file\\(s\\) are in error or do" - + " not exist)"); + Pattern.compile( + ".*(1 input file\\(s\\) (are in error|do not exist)|2 input file\\(s\\) are in error" + + " or do not exist)")); } private void runMissingFileAndIoException() throws Exception { @@ -565,14 +571,16 @@ public void missingFileAndIoException_keepGoing() throws Exception { @Test public void missingFileAndIoException_noKeepGoing() throws Exception { runMissingFileAndIoException(); - MoreAsserts.assertContainsEventRegex( + MoreAsserts.assertContainsEvent( events.collector(), - ".*foo/BUILD:1:8: Executing genrule //foo:foo failed: (error reading file '//foo:error.in':" - + " nope|missing input file '//foo:missing.in')"); - MoreAsserts.assertContainsEventRegex( + Pattern.compile( + ".*foo/BUILD:1:8: Executing genrule //foo:foo failed: (error reading file" + + " '//foo:error.in': nope|missing input file '//foo:missing.in')")); + MoreAsserts.assertContainsEvent( events.collector(), - ".*(1 input file\\(s\\) (are in error|do not exist)|2 input file\\(s\\) are in error or do" - + " not exist)"); + Pattern.compile( + ".*(1 input file\\(s\\) (are in error|do not exist)|2 input file\\(s\\) are in error" + + " or do not exist)")); } private static class CustomRealFilesystem extends UnixFileSystem { diff --git a/src/test/java/com/google/devtools/build/lib/events/util/EventCollectionApparatus.java b/src/test/java/com/google/devtools/build/lib/events/util/EventCollectionApparatus.java index 6e036e6b199e37..888153751d1490 100644 --- a/src/test/java/com/google/devtools/build/lib/events/util/EventCollectionApparatus.java +++ b/src/test/java/com/google/devtools/build/lib/events/util/EventCollectionApparatus.java @@ -25,6 +25,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Set; +import java.util.regex.Pattern; /** * An apparatus for reporting / collecting events. @@ -175,8 +176,16 @@ public Event assertContainsError(String expectedMessage) { } /** - * Utility method: Assert that the {@link #collector()} has received a - * warning with the {@code expectedMessage}. + * Utility method: Assert that the {@link #collector()} has received an error that matches {@code + * expectedPattern}. + */ + public Event assertContainsError(Pattern expectedPattern) { + return MoreAsserts.assertContainsEvent(eventCollector, expectedPattern, EventKind.ERROR); + } + + /** + * Utility method: Assert that the {@link #collector()} has received a warning with the {@code + * expectedMessage}. */ public Event assertContainsWarning(String expectedMessage) { return MoreAsserts.assertContainsEvent(eventCollector, expectedMessage, EventKind.WARNING); diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/SequencedSkyframeExecutorTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/SequencedSkyframeExecutorTest.java index c8b91d69703e1c..e3a8fed4440f76 100644 --- a/src/test/java/com/google/devtools/build/lib/skyframe/SequencedSkyframeExecutorTest.java +++ b/src/test/java/com/google/devtools/build/lib/skyframe/SequencedSkyframeExecutorTest.java @@ -18,9 +18,7 @@ import static com.google.common.truth.Truth.assertWithMessage; import static com.google.devtools.build.lib.actions.util.ActionCacheTestHelper.AMNESIAC_CACHE; import static com.google.devtools.build.lib.actions.util.ActionsTestUtil.NULL_ACTION_OWNER; -import static com.google.devtools.build.lib.testutil.MoreAsserts.assertContainsEventRegex; import static com.google.devtools.build.lib.testutil.MoreAsserts.assertEventCount; -import static com.google.devtools.build.lib.testutil.MoreAsserts.assertNotContainsEventRegex; import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertThrows; import static org.junit.Assert.fail; @@ -160,6 +158,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; +import java.util.regex.Pattern; import javax.annotation.Nullable; import net.starlark.java.eval.EvalException; import net.starlark.java.eval.Printer; @@ -2274,9 +2273,10 @@ public NestedSet discoverInputs(ActionExecutionContext actionExecution null, null, /* trustRemoteArtifacts= */ false); - assertContainsEventRegex(eventCollector, ".*during scanning.*\n.*Scanning.*\n.*Test dir/top.*"); - assertNotContainsEventRegex( - eventCollector, ".*after scanning.*\n.*Scanning.*\n.*Test dir/top.*"); + MoreAsserts.assertContainsEvent( + eventCollector, Pattern.compile(".*during scanning.*\n.*Scanning.*\n.*Test dir/top.*")); + MoreAsserts.assertNotContainsEvent( + eventCollector, Pattern.compile(".*after scanning.*\n.*Scanning.*\n.*Test dir/top.*")); } private static AnalysisProtos.Artifact getArtifact( diff --git a/src/test/java/com/google/devtools/build/lib/testutil/FoundationTestCase.java b/src/test/java/com/google/devtools/build/lib/testutil/FoundationTestCase.java index 114e923455794a..e6baee9d8a35cd 100644 --- a/src/test/java/com/google/devtools/build/lib/testutil/FoundationTestCase.java +++ b/src/test/java/com/google/devtools/build/lib/testutil/FoundationTestCase.java @@ -28,6 +28,7 @@ import com.google.devtools.build.lib.vfs.Root; import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem; import java.util.Set; +import java.util.regex.Pattern; import org.junit.After; import org.junit.Before; @@ -113,6 +114,10 @@ protected Event assertContainsEvent(String expectedMessage) { expectedMessage); } + protected Event assertContainsEvent(Pattern expectedMessagePattern) { + return MoreAsserts.assertContainsEvent(eventCollector, expectedMessagePattern); + } + protected Event assertContainsEvent(String expectedMessage, Set kinds) { return MoreAsserts.assertContainsEvent(eventCollector, expectedMessage, diff --git a/src/test/java/com/google/devtools/build/lib/testutil/MoreAsserts.java b/src/test/java/com/google/devtools/build/lib/testutil/MoreAsserts.java index 7e1161096b49c5..fcbb4a7759da35 100644 --- a/src/test/java/com/google/devtools/build/lib/testutil/MoreAsserts.java +++ b/src/test/java/com/google/devtools/build/lib/testutil/MoreAsserts.java @@ -35,9 +35,9 @@ import java.util.Arrays; import java.util.Collections; import java.util.Comparator; +import java.util.IdentityHashMap; import java.util.Iterator; import java.util.List; -import java.util.Map; import java.util.Queue; import java.util.Set; import java.util.regex.Pattern; @@ -84,7 +84,7 @@ public static void assertInstanceOfNotReachable( static final Predicate ALL_STRONG_REFS = Predicates.equalTo(NON_STRONG_REF); private static boolean isRetained(Predicate predicate, Object start) { - Map visited = Maps.newIdentityHashMap(); + IdentityHashMap visited = Maps.newIdentityHashMap(); visited.put(start, start); Queue toScan = new ArrayDeque<>(); toScan.add(start); @@ -301,28 +301,45 @@ public static Event assertContainsEvent(Iterable eventCollector, } /** - * If {@code eventCollector} does not contain an event which matches {@code expectedEventRegex}, + * If {@code eventCollector} does not contain an event which matches {@code expectedEventPattern}, * fails with an informative assertion. */ - public static void assertContainsEventRegex( - Iterable eventCollector, String expectedEventRegex) { + public static Event assertContainsEvent( + Iterable eventCollector, Pattern expectedEventPattern, EventKind... kinds) { + return assertContainsEvent(eventCollector, expectedEventPattern, ImmutableSet.copyOf(kinds)); + } + + /** + * If {@code eventCollector} does not contain an event which matches {@code expectedEventPattern}, + * fails with an informative assertion. + */ + public static Event assertContainsEvent( + Iterable eventCollector, Pattern expectedEventPattern, Set kinds) { for (Event event : eventCollector) { - if (event.toString().matches(expectedEventRegex)) { - return; + // Does the event message match the expected regex? + if (!expectedEventPattern.matcher(event.toString()).find()) { + continue; + } + // Was an expected kind given, and does the event match? + if (!kinds.isEmpty() && !kinds.contains(event.getKind())) { + continue; } + // Return the event, assertion successful + return event; } String eventsString = eventsToString(eventCollector); - String failureMessage = "Event matching '" + expectedEventRegex + "' not found"; + String failureMessage = "Event matching '" + expectedEventPattern + "' not found"; if (!eventsString.isEmpty()) { failureMessage += "; found these though: " + eventsString; } fail(failureMessage); + return null; // unreachable } - public static void assertNotContainsEventRegex( - Iterable eventCollector, String unexpectedEventRegex) { + public static void assertNotContainsEvent( + Iterable eventCollector, Pattern unexpectedEventPattern) { for (Event event : eventCollector) { - assertThat(event.toString()).doesNotMatch(unexpectedEventRegex); + assertThat(event.toString()).doesNotMatch(unexpectedEventPattern); } } @@ -384,12 +401,12 @@ private static String eventsToString(Iterable eventCollector) { } /** - * If "expectedSublist" is not a sublist of "arguments", an informative - * assertion is failed in the context of the specified TestCase. + * If "expectedSublist" is not a sublist of "arguments", an informative assertion is failed in the + * context of the specified TestCase. * *

Argument order mnemonic: assert(X)ContainsSublist(Y). */ - @SuppressWarnings({"unchecked", "varargs"}) + @SuppressWarnings("varargs") public static void assertContainsSublist(List arguments, T... expectedSublist) { List sublist = Arrays.asList(expectedSublist); try { @@ -400,12 +417,12 @@ public static void assertContainsSublist(List arguments, T... expectedSub } /** - * If "expectedSublist" is a sublist of "arguments", an informative - * assertion is failed in the context of the specified TestCase. + * If "expectedSublist" is a sublist of "arguments", an informative assertion is failed in the + * context of the specified TestCase. * *

Argument order mnemonic: assert(X)DoesNotContainSublist(Y). */ - @SuppressWarnings({"unchecked", "varargs"}) + @SuppressWarnings("varargs") public static void assertDoesNotContainSublist(List arguments, T... expectedSublist) { List sublist = Arrays.asList(expectedSublist); try { @@ -442,7 +459,6 @@ public static void assertContainsEventsInOrder( * if the elements of the pair are equal by its lights. * @return first element not in arguments in order, or null if success. */ - @SuppressWarnings({"unchecked"}) protected static T containsSublistWithGapsAndEqualityChecker(List arguments, Function, Boolean> equalityChecker, T... expectedSublist) { Iterator iter = arguments.iterator();