Skip to content

Commit

Permalink
Fix regex event matching
Browse files Browse the repository at this point in the history
This is the prerequisite to bazelbuild#13371.

Closes bazelbuild#13375.

PiperOrigin-RevId: 369286187
  • Loading branch information
katre authored and copybara-github committed Apr 19, 2021
1 parent e5c832a commit ad90293
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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'.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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(
Expand All @@ -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()
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -2274,9 +2273,10 @@ public NestedSet<Artifact> 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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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<EventKind> kinds) {
return MoreAsserts.assertContainsEvent(eventCollector,
expectedMessage,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -84,7 +84,7 @@ public static void assertInstanceOfNotReachable(
static final Predicate<Field> ALL_STRONG_REFS = Predicates.equalTo(NON_STRONG_REF);

private static boolean isRetained(Predicate<Object> predicate, Object start) {
Map<Object, Object> visited = Maps.newIdentityHashMap();
IdentityHashMap<Object, Object> visited = Maps.newIdentityHashMap();
visited.put(start, start);
Queue<Object> toScan = new ArrayDeque<>();
toScan.add(start);
Expand Down Expand Up @@ -301,28 +301,45 @@ public static Event assertContainsEvent(Iterable<Event> 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<Event> eventCollector, String expectedEventRegex) {
public static Event assertContainsEvent(
Iterable<Event> 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<Event> eventCollector, Pattern expectedEventPattern, Set<EventKind> 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<Event> eventCollector, String unexpectedEventRegex) {
public static void assertNotContainsEvent(
Iterable<Event> eventCollector, Pattern unexpectedEventPattern) {
for (Event event : eventCollector) {
assertThat(event.toString()).doesNotMatch(unexpectedEventRegex);
assertThat(event.toString()).doesNotMatch(unexpectedEventPattern);
}
}

Expand Down Expand Up @@ -384,12 +401,12 @@ private static String eventsToString(Iterable<Event> 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.
*
* <p>Argument order mnemonic: assert(X)ContainsSublist(Y).
*/
@SuppressWarnings({"unchecked", "varargs"})
@SuppressWarnings("varargs")
public static <T> void assertContainsSublist(List<T> arguments, T... expectedSublist) {
List<T> sublist = Arrays.asList(expectedSublist);
try {
Expand All @@ -400,12 +417,12 @@ public static <T> void assertContainsSublist(List<T> 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.
*
* <p>Argument order mnemonic: assert(X)DoesNotContainSublist(Y).
*/
@SuppressWarnings({"unchecked", "varargs"})
@SuppressWarnings("varargs")
public static <T> void assertDoesNotContainSublist(List<T> arguments, T... expectedSublist) {
List<T> sublist = Arrays.asList(expectedSublist);
try {
Expand Down Expand Up @@ -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 <S, T> T containsSublistWithGapsAndEqualityChecker(List<S> arguments,
Function<Pair<S, T>, Boolean> equalityChecker, T... expectedSublist) {
Iterator<S> iter = arguments.iterator();
Expand Down

0 comments on commit ad90293

Please sign in to comment.