Skip errored runs when computing repetition statistics (#2199)
diff --git a/src/statistics.cc b/src/statistics.cc
index 2c6c858..619bb66 100644
--- a/src/statistics.cc
+++ b/src/statistics.cc
@@ -112,10 +112,15 @@
   typedef BenchmarkReporter::Run Run;
   std::vector<Run> results;
 
-  auto error_count = std::count_if(reports.begin(), reports.end(),
-                                   [](Run const& run) { return run.skipped; });
+  const auto is_successful = [](Run const& run) {
+    return run.skipped == internal::NotSkipped;
+  };
+  auto successful_run =
+      std::find_if(reports.begin(), reports.end(), is_successful);
+  const auto successful_count = static_cast<size_t>(
+      std::count_if(reports.begin(), reports.end(), is_successful));
 
-  if (reports.size() - static_cast<size_t>(error_count) < 2) {
+  if (successful_count < 2) {
     // We don't report aggregated data if there was a single run.
     return results;
   }
@@ -124,12 +129,12 @@
   std::vector<double> real_accumulated_time_stat;
   std::vector<double> cpu_accumulated_time_stat;
 
-  real_accumulated_time_stat.reserve(reports.size());
-  cpu_accumulated_time_stat.reserve(reports.size());
+  real_accumulated_time_stat.reserve(successful_count);
+  cpu_accumulated_time_stat.reserve(successful_count);
 
   // All repetitions should be run with the same number of iterations so we
   // can take this information from the first benchmark.
-  const IterationCount run_iterations = reports.front().iterations;
+  const IterationCount run_iterations = successful_run->iterations;
   // create stats for user counters
   struct CounterStat {
     Counter c;
@@ -137,6 +142,9 @@
   };
   std::map<std::string, CounterStat> counter_stats;
   for (Run const& r : reports) {
+    if (!is_successful(r)) {
+      continue;
+    }
     for (auto const& cnt : r.counters) {
       auto it = counter_stats.find(cnt.first);
       if (it == counter_stats.end()) {
@@ -144,7 +152,7 @@
                  .emplace(cnt.first,
                           CounterStat{cnt.second, std::vector<double>{}})
                  .first;
-        it->second.s.reserve(reports.size());
+        it->second.s.reserve(successful_count);
       } else {
         BM_CHECK_EQ(it->second.c.flags, cnt.second.flags);
       }
@@ -153,11 +161,11 @@
 
   // Populate the accumulators.
   for (Run const& run : reports) {
-    BM_CHECK_EQ(reports[0].benchmark_name(), run.benchmark_name());
-    BM_CHECK_EQ(run_iterations, run.iterations);
-    if (run.skipped != 0u) {
+    BM_CHECK_EQ(successful_run->benchmark_name(), run.benchmark_name());
+    if (!is_successful(run)) {
       continue;
     }
+    BM_CHECK_EQ(run_iterations, run.iterations);
     real_accumulated_time_stat.emplace_back(run.real_accumulated_time);
     cpu_accumulated_time_stat.emplace_back(run.cpu_accumulated_time);
     // user counters
@@ -169,26 +177,30 @@
   }
 
   // Only add label if it is same for all runs
-  std::string report_label = reports[0].report_label;
-  for (std::size_t i = 1; i < reports.size(); i++) {
-    if (reports[i].report_label != report_label) {
+  std::string report_label = successful_run->report_label;
+  for (const Run& run : reports) {
+    if (!is_successful(run)) {
+      continue;
+    }
+    if (run.report_label != report_label) {
       report_label = "";
       break;
     }
   }
 
   const double iteration_rescale_factor =
-      static_cast<double>(reports.size()) / static_cast<double>(run_iterations);
+      static_cast<double>(successful_count) /
+      static_cast<double>(run_iterations);
 
-  for (const auto& Stat : *reports[0].statistics) {
+  for (const auto& Stat : *successful_run->statistics) {
     // Get the data from the accumulator to BenchmarkReporter::Run's.
     Run data;
-    data.run_name = reports[0].run_name;
-    data.family_index = reports[0].family_index;
-    data.per_family_instance_index = reports[0].per_family_instance_index;
+    data.run_name = successful_run->run_name;
+    data.family_index = successful_run->family_index;
+    data.per_family_instance_index = successful_run->per_family_instance_index;
     data.run_type = BenchmarkReporter::Run::RT_Aggregate;
-    data.threads = reports[0].threads;
-    data.repetitions = reports[0].repetitions;
+    data.threads = successful_run->threads;
+    data.repetitions = successful_run->repetitions;
     data.repetition_index = Run::no_repetition_index;
     data.aggregate_name = Stat.name_;
     data.aggregate_unit = Stat.unit_;
@@ -199,7 +211,7 @@
     // Similarly, if there are N repetitions with 1 iterations each,
     // an aggregate will be computed over N measurements, not 1.
     // Thus it is best to simply use the count of separate reports.
-    data.iterations = static_cast<IterationCount>(reports.size());
+    data.iterations = static_cast<IterationCount>(successful_count);
 
     data.real_accumulated_time = Stat.compute_(real_accumulated_time_stat);
     data.cpu_accumulated_time = Stat.compute_(cpu_accumulated_time_stat);
@@ -214,7 +226,7 @@
       data.cpu_accumulated_time *= iteration_rescale_factor;
     }
 
-    data.time_unit = reports[0].time_unit;
+    data.time_unit = successful_run->time_unit;
 
     // user counters
     for (auto const& kv : counter_stats) {
diff --git a/test/repetitions_test.cc b/test/repetitions_test.cc
index 80216ab..0673692 100644
--- a/test/repetitions_test.cc
+++ b/test/repetitions_test.cc
@@ -209,6 +209,43 @@
 ADD_CASES(TC_CSVOut, {{"^\"BM_ImplicitRepetitions_mean\",%csv_report$"}});
 ADD_CASES(TC_CSVOut, {{"^\"BM_ImplicitRepetitions_median\",%csv_report$"}});
 ADD_CASES(TC_CSVOut, {{"^\"BM_ImplicitRepetitions_stddev\",%csv_report$"}});
+
+// ========================================================================= //
+// --------------------- Testing Skipped Repetitions ------------------------ //
+// ========================================================================= //
+
+void BM_SkippedFirstRepetition(benchmark::State& state) {
+  static int repetition_index = 0;
+  if (repetition_index++ == 0) {
+    state.SkipWithError("skipped first repetition");
+    return;
+  }
+  for (auto _ : state) {
+  }
+}
+BENCHMARK(BM_SkippedFirstRepetition)->Repetitions(3);
+
+ADD_CASES(
+    TC_ConsoleOut,
+    {{"^BM_SkippedFirstRepetition/repeats:3[ ]+ERROR OCCURRED: "
+      "'skipped first repetition'$"},
+     {"^BM_SkippedFirstRepetition/repeats:3 %console_report$", MR_Next},
+     {"^BM_SkippedFirstRepetition/repeats:3 %console_report$", MR_Next},
+     {"^BM_SkippedFirstRepetition/repeats:3_mean %console_report$", MR_Next},
+     {"^BM_SkippedFirstRepetition/repeats:3_median %console_report$", MR_Next},
+     {"^BM_SkippedFirstRepetition/repeats:3_stddev %console_report$",
+      MR_Next}});
+ADD_CASES(TC_JSONOut,
+          {{"\"name\": \"BM_SkippedFirstRepetition/repeats:3_mean\",$"},
+           {"\"run_type\": \"aggregate\",$"},
+           {"\"repetitions\": 3,$", MR_Next},
+           {"\"threads\": 1,$", MR_Next},
+           {"\"aggregate_name\": \"mean\",$", MR_Next},
+           {"\"aggregate_unit\": \"time\",$", MR_Next},
+           {"\"iterations\": 2,$", MR_Next}});
+ADD_CASES(TC_CSVOut,
+          {{"^\"BM_SkippedFirstRepetition/repeats:3_mean\",2,%float,%float,"
+            "ns,,,,,$"}});
 }  // end namespace
 
 // ========================================================================= //