Introduce Truth-compatible Subject and Subject.Factory for DetailedExitCode.

The new Subject's first use is in SequencedSkyframeExecutorTest.

PiperOrigin-RevId: 303449763
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/BUILD b/src/test/java/com/google/devtools/build/lib/skyframe/BUILD
index 0ae0950..81ab435 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/BUILD
@@ -137,6 +137,7 @@
         "//src/test/java/com/google/devtools/build/lib/testutil:TestConstants",
         "//src/test/java/com/google/devtools/build/lib/testutil:TestPackageFactoryBuilderFactory",
         "//src/test/java/com/google/devtools/build/lib/testutil:TestUtils",
+        "//src/test/java/com/google/devtools/build/lib/util/subjects",
         "//src/test/java/com/google/devtools/build/lib/vfs/util",
         "//src/test/java/com/google/devtools/build/skyframe:testutil",
         "//third_party:auto_value",
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 f0aa626..60ed4ba 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
@@ -20,6 +20,7 @@
 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 com.google.devtools.build.lib.util.subjects.DetailedExitCodeSubjectFactory.assertThatDetailedExitCode;
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.fail;
@@ -1589,8 +1590,8 @@
                     /* trustRemoteArtifacts= */ false));
     // The catastrophic exception should be propagated into the BuildFailedException whether or not
     // --keep_going is set.
-    assertThat(e.getDetailedExitCode().getExitCode())
-        .isEqualTo(CatastrophicAction.expectedExitCode);
+    assertThatDetailedExitCode(e.getDetailedExitCode())
+        .hasExitCode(CatastrophicAction.expectedExitCode);
     assertThat(builtTargets).isEmpty();
     assertThat(markerRan.get()).isFalse();
   }
@@ -1724,8 +1725,8 @@
                     /* trustRemoteArtifacts= */ false));
     // The catastrophic exception should be propagated into the BuildFailedException whether or not
     // --keep_going is set.
-    assertThat(e.getDetailedExitCode().getExitCode())
-        .isEqualTo(CatastrophicAction.expectedExitCode);
+    assertThatDetailedExitCode(e.getDetailedExitCode())
+        .hasExitCode(CatastrophicAction.expectedExitCode);
     assertThat(builtTargets).isEmpty();
   }
 
@@ -1856,8 +1857,8 @@
                     /* trustRemoteArtifacts= */ false));
     // The catastrophic exception should be propagated into the BuildFailedException whether or not
     // --keep_going is set.
-    assertThat(e.getDetailedExitCode().getExitCode())
-        .isEqualTo(CatastrophicAction.expectedExitCode);
+    assertThatDetailedExitCode(e.getDetailedExitCode())
+        .hasExitCode(CatastrophicAction.expectedExitCode);
     assertThat(builtTargets).isEmpty();
   }
 
@@ -1965,8 +1966,8 @@
                     /* trustRemoteArtifacts= */ false));
     // The catastrophic exception should be propagated into the BuildFailedException whether or not
     // --keep_going is set.
-    assertThat(e.getDetailedExitCode().getExitCode())
-        .isEqualTo(CatastrophicAction.expectedExitCode);
+    assertThatDetailedExitCode(e.getDetailedExitCode())
+        .hasExitCode(CatastrophicAction.expectedExitCode);
     assertThat(builtTargets).isEmpty();
   }
 
@@ -2079,7 +2080,7 @@
                     /* trustRemoteArtifacts= */ false));
     // The exit code should be propagated into the BuildFailedException whether or not --keep_going
     // is set.
-    assertThat(e.getDetailedExitCode().getExitCode()).isEqualTo(USER_EXIT_CODE);
+    assertThatDetailedExitCode(e.getDetailedExitCode()).hasExitCode(USER_EXIT_CODE);
   }
 
   /**
@@ -2177,7 +2178,7 @@
                     /* trustRemoteArtifacts= */ false));
     // The exit code should be propagated into the BuildFailedException whether or not --keep_going
     // is set.
-    assertThat(e.getDetailedExitCode().getExitCode()).isEqualTo(INFRA_EXIT_CODE);
+    assertThatDetailedExitCode(e.getDetailedExitCode()).hasExitCode(INFRA_EXIT_CODE);
   }
 
   /**
diff --git a/src/test/java/com/google/devtools/build/lib/util/BUILD b/src/test/java/com/google/devtools/build/lib/util/BUILD
index 397bcea..8a3262a 100644
--- a/src/test/java/com/google/devtools/build/lib/util/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/util/BUILD
@@ -10,6 +10,7 @@
     testonly = 0,
     srcs = glob(["*"]) + [
         "//src/test/java/com/google/devtools/build/lib/util/io:srcs",
+        "//src/test/java/com/google/devtools/build/lib/util/subjects:srcs",
     ],
     visibility = ["//src:__subpackages__"],
 )
diff --git a/src/test/java/com/google/devtools/build/lib/util/subjects/BUILD b/src/test/java/com/google/devtools/build/lib/util/subjects/BUILD
new file mode 100644
index 0000000..32e06a5
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/util/subjects/BUILD
@@ -0,0 +1,24 @@
+# Test-only package for Truth-compatible Subject and SubjectFactory types.
+load("@rules_java//java:defs.bzl", "java_library")
+
+package(
+    default_testonly = 1,
+    default_visibility = ["//src:__subpackages__"],
+)
+
+filegroup(
+    name = "srcs",
+    testonly = 0,
+    srcs = glob(["*"]),
+    visibility = ["//src:__subpackages__"],
+)
+
+java_library(
+    name = "subjects",
+    srcs = glob(["*.java"]),
+    deps = [
+        "//src/main/java/com/google/devtools/build/lib:detailed_exit_code",
+        "//src/main/java/com/google/devtools/build/lib:exitcode-external",
+        "//third_party:truth",
+    ],
+)
diff --git a/src/test/java/com/google/devtools/build/lib/util/subjects/DetailedExitCodeSubject.java b/src/test/java/com/google/devtools/build/lib/util/subjects/DetailedExitCodeSubject.java
new file mode 100644
index 0000000..9f0639b
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/util/subjects/DetailedExitCodeSubject.java
@@ -0,0 +1,49 @@
+// Copyright 2020 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.util.subjects;
+
+import static com.google.common.truth.Fact.simpleFact;
+
+import com.google.common.truth.FailureMetadata;
+import com.google.common.truth.Subject;
+import com.google.devtools.build.lib.util.DetailedExitCode;
+import com.google.devtools.build.lib.util.ExitCode;
+
+/** A Truth-compatible {@link Subject} for {@link DetailedExitCode}. */
+public class DetailedExitCodeSubject extends Subject {
+
+  private final DetailedExitCode actual;
+
+  public DetailedExitCodeSubject(FailureMetadata failureMetadata, DetailedExitCode exitCode) {
+    super(failureMetadata, exitCode);
+    this.actual = exitCode;
+  }
+
+  public void hasExitCode(ExitCode exitCode) {
+    isNotNull();
+    check("getExitCode()").that(actual.getExitCode()).isEqualTo(exitCode);
+  }
+
+  public void isSuccessful() {
+    if (!actual.isSuccess()) {
+      failWithActual(simpleFact("expected to be SUCCESS"));
+    }
+  }
+
+  public void isNotSuccessful() {
+    if (actual.isSuccess()) {
+      failWithActual(simpleFact("expected *not* to be SUCCESS"));
+    }
+  }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/util/subjects/DetailedExitCodeSubjectFactory.java b/src/test/java/com/google/devtools/build/lib/util/subjects/DetailedExitCodeSubjectFactory.java
new file mode 100644
index 0000000..088eb24
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/util/subjects/DetailedExitCodeSubjectFactory.java
@@ -0,0 +1,36 @@
+// Copyright 2020 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.util.subjects;
+
+import com.google.common.truth.FailureMetadata;
+import com.google.common.truth.Subject;
+import com.google.common.truth.Truth;
+import com.google.devtools.build.lib.util.DetailedExitCode;
+
+/**
+ * {@link Subject.Factory} for {@link DetailedExitCode} objects, providing {@link
+ * DetailedExitCodeSubject}s.
+ */
+public class DetailedExitCodeSubjectFactory
+    implements Subject.Factory<DetailedExitCodeSubject, DetailedExitCode> {
+
+  public static DetailedExitCodeSubject assertThatDetailedExitCode(DetailedExitCode code) {
+    return Truth.assertAbout(new DetailedExitCodeSubjectFactory()).that(code);
+  }
+
+  @Override
+  public DetailedExitCodeSubject createSubject(FailureMetadata metadata, DetailedExitCode actual) {
+    return new DetailedExitCodeSubject(metadata, actual);
+  }
+}