Add a method to `BatchResult` to distinguish input crashes from other failures.

Other failures include setup failures, skipped tests, and failures that should
be ignored.

PiperOrigin-RevId: 819875172
diff --git a/centipede/runner_result.cc b/centipede/runner_result.cc
index dac2e83..3b726e7 100644
--- a/centipede/runner_result.cc
+++ b/centipede/runner_result.cc
@@ -173,6 +173,11 @@
                  .substr(0, kSkippedTestPrefix.size()) == kSkippedTestPrefix;
 }
 
+bool BatchResult::IsInputFailure() const {
+  return exit_code_ != EXIT_SUCCESS && !IsIgnoredFailure() &&
+         !IsSetupFailure() && !IsSkippedTest();
+}
+
 bool MutationResult::WriteHasCustomMutator(bool has_custom_mutator,
                                            BlobSequence &blobseq) {
   return blobseq.Write(
diff --git a/centipede/runner_result.h b/centipede/runner_result.h
index ef4459a..1b94f31 100644
--- a/centipede/runner_result.h
+++ b/centipede/runner_result.h
@@ -164,6 +164,10 @@
   // run any inputs at all.
   bool IsSkippedTest() const;
 
+  // Returns true if the batch execution failed due to an input failure,
+  // which is neither an ignored failure, a setup failure, nor a skipped test.
+  bool IsInputFailure() const;
+
   // Accessors.
   std::vector<ExecutionResult>& results() { return results_; }
   const std::vector<ExecutionResult>& results() const { return results_; }
diff --git a/centipede/runner_result_test.cc b/centipede/runner_result_test.cc
index 19f5055..864401a 100644
--- a/centipede/runner_result_test.cc
+++ b/centipede/runner_result_test.cc
@@ -166,6 +166,14 @@
                           ));
 }
 
+TEST(ExecutionResult, IdentifiesIgnoredFailure) {
+  BatchResult batch_result;
+  batch_result.exit_code() = EXIT_FAILURE;
+  batch_result.failure_description() = "IGNORED FAILURE: something went wrong";
+
+  EXPECT_TRUE(batch_result.IsIgnoredFailure());
+}
+
 TEST(ExecutionResult, IdentifiesSetupFailure) {
   BatchResult batch_result;
   batch_result.exit_code() = EXIT_FAILURE;
@@ -174,6 +182,31 @@
   EXPECT_TRUE(batch_result.IsSetupFailure());
 }
 
+TEST(ExecutionResult, IdentifiesSkippedTest) {
+  BatchResult batch_result;
+  batch_result.exit_code() = EXIT_FAILURE;
+  batch_result.failure_description() = "SKIPPED TEST: test is skipped";
+
+  EXPECT_TRUE(batch_result.IsSkippedTest());
+}
+
+TEST(ExecutionResult, IdentifiesInputFailure) {
+  BatchResult batch_result;
+  batch_result.exit_code() = EXIT_FAILURE;
+
+  batch_result.failure_description() = "IGNORED FAILURE: something went wrong";
+  EXPECT_FALSE(batch_result.IsInputFailure());
+
+  batch_result.failure_description() = "SETUP FAILURE: something went wrong";
+  EXPECT_FALSE(batch_result.IsInputFailure());
+
+  batch_result.failure_description() = "SKIPPED TEST: test is skipped";
+  EXPECT_FALSE(batch_result.IsInputFailure());
+
+  batch_result.failure_description() = "something went wrong";
+  EXPECT_TRUE(batch_result.IsInputFailure());
+}
+
 TEST(MutationResult, WriteThenRead) {
   std::array<uint8_t, 1000> buffer;
   BlobSequence blobseq(buffer.data(), buffer.size());