Prohibit constructing `ErrorInfo` that is directly transient but not transitively transient.
PiperOrigin-RevId: 416455536
diff --git a/src/main/java/com/google/devtools/build/skyframe/ErrorInfo.java b/src/main/java/com/google/devtools/build/skyframe/ErrorInfo.java
index 141c47e..c2a754b 100644
--- a/src/main/java/com/google/devtools/build/skyframe/ErrorInfo.java
+++ b/src/main/java/com/google/devtools/build/skyframe/ErrorInfo.java
@@ -16,7 +16,6 @@
import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
import com.google.devtools.build.skyframe.SkyFunctionException.ReifiedSkyFunctionException;
import java.util.Collection;
import java.util.Objects;
@@ -48,7 +47,7 @@
ImmutableList.of(cycleInfo),
/*isDirectlyTransient=*/ false,
/*isTransitivelyTransient=*/ false,
- /* isCatastrophic= */ false);
+ /*isCatastrophic=*/ false);
}
/** Create an ErrorInfo from a collection of existing errors. */
@@ -64,11 +63,11 @@
for (ErrorInfo child : childErrors) {
if (firstException == null) {
// Arbitrarily pick the first error.
- firstException = child.getException();
+ firstException = child.exception;
}
cycleBuilder.addAll(CycleInfo.prepareCycles(currentValue, child.cycles));
- isTransitivelyTransient |= child.isTransitivelyTransient();
- isCatastrophic |= child.isCatastrophic();
+ isTransitivelyTransient |= child.isTransitivelyTransient;
+ isCatastrophic |= child.isCatastrophic;
}
return new ErrorInfo(
@@ -80,9 +79,7 @@
}
@Nullable private final Exception exception;
-
private final ImmutableList<CycleInfo> cycles;
-
private final boolean isDirectlyTransient;
private final boolean isTransitivelyTransient;
private final boolean isCatastrophic;
@@ -93,14 +90,19 @@
boolean isDirectlyTransient,
boolean isTransitivelyTransient,
boolean isCatastrophic) {
- Preconditions.checkState(exception != null || !Iterables.isEmpty(cycles),
- "At least one of exception and cycles must be non-null/empty, respectively");
-
this.exception = exception;
this.cycles = cycles;
this.isDirectlyTransient = isDirectlyTransient;
this.isTransitivelyTransient = isTransitivelyTransient;
this.isCatastrophic = isCatastrophic;
+ Preconditions.checkArgument(
+ exception != null || !cycles.isEmpty(),
+ "At least one of exception and cycles must be present",
+ this);
+ Preconditions.checkArgument(
+ !isDirectlyTransient || isTransitivelyTransient,
+ "Cannot be directly transient but not transitively transient",
+ this);
}
@Override
@@ -188,6 +190,9 @@
/**
* Returns true iff the error is directly transient, i.e. if there was a transient error
* encountered during the computation itself.
+ *
+ * <p>A return of {@code true} implies that {@link #isTransitivelyTransient()} is also {@code
+ * true}.
*/
public boolean isDirectlyTransient() {
return isDirectlyTransient;
diff --git a/src/test/java/com/google/devtools/build/skyframe/ErrorInfoTest.java b/src/test/java/com/google/devtools/build/skyframe/ErrorInfoTest.java
index b2e58f5..1537d8b 100644
--- a/src/test/java/com/google/devtools/build/skyframe/ErrorInfoTest.java
+++ b/src/test/java/com/google/devtools/build/skyframe/ErrorInfoTest.java
@@ -26,14 +26,13 @@
/** Tests for the non-trivial creation logic of {@link ErrorInfo}. */
@RunWith(JUnit4.class)
-public class ErrorInfoTest {
+public final class ErrorInfoTest {
/** Dummy SkyFunctionException implementation for the sake of testing. */
- private static class DummySkyFunctionException extends SkyFunctionException {
+ private static final class DummySkyFunctionException extends SkyFunctionException {
private final boolean isCatastrophic;
- public DummySkyFunctionException(Exception cause, boolean isTransient,
- boolean isCatastrophic) {
+ DummySkyFunctionException(Exception cause, boolean isTransient, boolean isCatastrophic) {
super(cause, isTransient ? Transience.TRANSIENT : Transience.PERSISTENT);
this.isCatastrophic = isCatastrophic;
}
@@ -138,14 +137,30 @@
}
@Test
- public void testCannotCreateErrorInfoWithoutExceptionOrCycle() {
- // Brittle, but confirms we failed for the right reason.
- IllegalStateException e =
+ public void cannotCreateErrorInfoWithoutExceptionOrCycle() {
+ Exception e =
assertThrows(
- IllegalStateException.class,
- () -> new ErrorInfo(/*exception=*/ null, ImmutableList.of(), false, false, false));
+ RuntimeException.class,
+ () ->
+ new ErrorInfo(
+ /*exception=*/ null, /*cycles=*/ ImmutableList.of(), false, false, false));
+ assertThat(e).hasMessageThat().contains("At least one of exception and cycles must be present");
+ }
+
+ @Test
+ public void cannotCreateErrorInfoWithDirectTransienceButNotTransitiveTransience() {
+ Exception e =
+ assertThrows(
+ RuntimeException.class,
+ () ->
+ new ErrorInfo(
+ new Exception(),
+ /*cycles=*/ ImmutableList.of(),
+ /*isDirectlyTransient=*/ true,
+ /*isTransitivelyTransient=*/ false,
+ /*isCatastrophic=*/ false));
assertThat(e)
.hasMessageThat()
- .isEqualTo("At least one of exception and cycles must be non-null/empty, respectively");
+ .contains("Cannot be directly transient but not transitively transient");
}
}