Skip to content

Commit

Permalink
Prohibit constructing ErrorInfo that is directly transient but not …
Browse files Browse the repository at this point in the history
…transitively transient.

PiperOrigin-RevId: 416455536
  • Loading branch information
justinhorvitz authored and copybara-github committed Dec 15, 2021
1 parent 2ff4124 commit 4c6e324
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 20 deletions.
25 changes: 15 additions & 10 deletions src/main/java/com/google/devtools/build/skyframe/ErrorInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -48,7 +47,7 @@ public static ErrorInfo fromCycle(CycleInfo cycleInfo) {
ImmutableList.of(cycleInfo),
/*isDirectlyTransient=*/ false,
/*isTransitivelyTransient=*/ false,
/* isCatastrophic= */ false);
/*isCatastrophic=*/ false);
}

/** Create an ErrorInfo from a collection of existing errors. */
Expand All @@ -64,11 +63,11 @@ public static ErrorInfo fromChildErrors(SkyKey currentValue, Collection<ErrorInf
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(
Expand All @@ -80,9 +79,7 @@ public static ErrorInfo fromChildErrors(SkyKey currentValue, Collection<ErrorInf
}

@Nullable private final Exception exception;

private final ImmutableList<CycleInfo> cycles;

private final boolean isDirectlyTransient;
private final boolean isTransitivelyTransient;
private final boolean isCatastrophic;
Expand All @@ -93,14 +90,19 @@ public ErrorInfo(
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
Expand Down Expand Up @@ -188,6 +190,9 @@ public ImmutableList<CycleInfo> getCycleInfo() {
/**
* 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;
Expand Down
35 changes: 25 additions & 10 deletions src/test/java/com/google/devtools/build/skyframe/ErrorInfoTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -138,14 +137,30 @@ public void testFromChildErrors() {
}

@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");
}
}

0 comments on commit 4c6e324

Please sign in to comment.