text: // Copyright 2018 The Fuchsia Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "src/cobalt/bin/testapp/tests.h"
  
  #include <ctime>
  
  #include "src/cobalt/bin/testapp/prober_metrics_registry.cb.h"
  #include "src/cobalt/bin/testapp/test_constants.h"
  #include "src/cobalt/bin/testapp/testapp_metrics_registry.cb.h"
  #include "src/cobalt/bin/utils/base64.h"
  #include "src/lib/cobalt/cpp/cobalt_event_builder.h"
  #include "third_party/cobalt/src/lib/util/datetime_util.h"
  
  namespace cobalt {
  
  using util::SystemClockInterface;
  using util::TimeToDayIndex;
  
  namespace testapp {
  
  using fidl::VectorPtr;
  using fuchsia::cobalt::Status;
  
  namespace {
  uint32_t CurrentDayIndex(SystemClockInterface* clock) {
    return TimeToDayIndex(std::chrono::system_clock::to_time_t(clock->now()), MetricDefinition::UTC);
  }
  
  bool SendAndCheckSuccess(const std::string& test_name, CobaltTestAppLogger* logger) {
    if (!logger->CheckForSuccessfulSend()) {
      FX_LOGS(INFO) << "CheckForSuccessfulSend() returned false";
      FX_LOGS(INFO) << test_name << ": FAIL";
      return false;
    }
    FX_LOGS(INFO) << test_name << ": PASS";
    return true;
  }
  }  // namespace
  
  bool TestLogEvent(CobaltTestAppLogger* logger) {
    FX_LOGS(INFO) << "========================";
    FX_LOGS(INFO) << "TestLogEvent";
    for (uint32_t index : kErrorOccurredIndicesToUse) {
      if (!logger->LogEvent(cobalt_registry::kErrorOccurredMetricId, index)) {
        FX_LOGS(INFO) << "TestLogEvent: FAIL";
        return false;
      }
    }
    if (logger->LogEvent(cobalt_registry::kErrorOccurredMetricId, kErrorOccurredInvalidIndex)) {
      FX_LOGS(INFO) << "TestLogEvent: FAIL";
      return false;
    }
  
    return SendAndCheckSuccess("TestLogEvent", logger);
  }
  
  // file_system_cache_misses using EVENT_COUNT metric.
  //
  // For each |event_code| and each |component_name|, log one observation with
  // a value of kFileSystemCacheMissesCountMax - event_code index.
  bool TestLogEventCount(CobaltTestAppLogger* logger) {
    FX_LOGS(INFO) << "========================";
    FX_LOGS(INFO) << "TestLogEventCount";
    for (uint32_t index : kFileSystemCacheMissesIndices) {
      for (std::string name : kFileSystemCacheMissesComponentNames) {
        if (!logger->LogEventCount(cobalt_registry::kFileSystemCacheMissesMetricId, index, name,
                                   kFileSystemCacheMissesCountMax - index)) {
          FX_LOGS(INFO) << "LogEventCount(" << cobalt_registry::kFileSystemCacheMissesMetricId << ", "
                        << index << ", " << name << ", " << kFileSystemCacheMissesCountMax - index
                        << ")";
          FX_LOGS(INFO) << "TestLogEventCount: FAIL";
          return false;
        }
      }
    }
  
    return SendAndCheckSuccess("TestLogEventCount", logger);
  }
  
  // update_duration using ELAPSED_TIME metric.
  //
  // For each |event_code| and each |component_name|, log one observation in each
  // exponential histogram bucket.
  bool TestLogElapsedTime(CobaltTestAppLogger* logger) {
    FX_LOGS(INFO) << "========================";
    FX_LOGS(INFO) << "TestLogElapsedTime";
    for (uint32_t index : kUpdateDurationIndices) {
      for (std::string name : kUpdateDurationComponentNames) {
        for (int64_t value : kUpdateDurationValues) {
          if (!logger->LogElapsedTime(cobalt_registry::kUpdateDurationMetricId, index, name, value)) {
            FX_LOGS(INFO) << "LogElapsedTime(" << cobalt_registry::kUpdateDurationMetricId << ", "
                          << index << ", " << name << ", " << value << ")";
            FX_LOGS(INFO) << "TestLogElapsedTime: FAIL";
            return false;
          }
        }
      }
    }
  
    return SendAndCheckSuccess("TestLogElapsedTime", logger);
  }
  
  // game_frame_rate using FRAME_RATE metric.
  //
  // For each |event_code| and each |component_name|, log one observation in each
  // exponential histogram bucket.
  bool TestLogFrameRate(CobaltTestAppLogger* logger) {
    FX_LOGS(INFO) << "========================";
    FX_LOGS(INFO) << "TestLogFrameRate";
    for (uint32_t index : kGameFrameRateIndices) {
      for (std::string name : kGameFrameRateComponentNames) {
        for (float value : kGameFrameRateValues) {
          if (!logger->LogFrameRate(cobalt_registry::kGameFrameRateMetricId, index, name, value)) {
            FX_LOGS(INFO) << "LogFrameRate(" << cobalt_registry::kGameFrameRateMetricId << ", "
                          << index << ", " << name << ", " << value << ")";
            FX_LOGS(INFO) << "TestLogFrameRate: FAIL";
            return false;
          }
        }
      }
    }
  
    return SendAndCheckSuccess("TestLogFrameRate", logger);
  }
  
  // application_memory
  //
  // For each |event_code| and each |component_name|, log one observation in each
  // exponential histogram bucket.
  bool TestLogMemoryUsage(CobaltTestAppLogger* logger) {
    FX_LOGS(INFO) << "========================";
    FX_LOGS(INFO) << "TestLogMemoryUsage";
    for (uint32_t index : kApplicationMemoryIndices) {
      for (std::string name : kApplicationComponentNames) {
        for (int64_t value : kApplicationMemoryValues) {
          if (!logger->LogMemoryUsage(cobalt_registry::kApplicationMemoryMetricId, index, name,
                                      value)) {
            FX_LOGS(INFO) << "LogMemoryUsage(" << cobalt_registry::kApplicationMemoryMetricId << ", "
                          << index << ", " << name << ", " << value << ")";
            FX_LOGS(INFO) << "TestLogMemoryUsage: FAIL";
            return false;
          }
        }
      }
    }
  
    return SendAndCheckSuccess("TestLogMemoryUsage", logger);
  }
  
  // power_usage and bandwidth_usage
  //
  // For each |event_code| and each |component_name|, log one observation in each
  // histogram bucket, using decreasing values per bucket.
  bool TestLogIntHistogram(CobaltTestAppLogger* logger) {
    FX_LOGS(INFO) << "========================";
    FX_LOGS(INFO) << "TestLogIntHistogram";
    std::map<uint32_t, uint64_t> histogram;
  
    // Set up and send power_usage histogram.
    for (uint32_t bucket = 0; bucket < kPowerUsageBuckets; bucket++) {
      histogram[bucket] = kPowerUsageBuckets - bucket + 1;
    }
    for (uint32_t index : kPowerUsageIndices) {
      for (std::string name : kApplicationComponentNames) {
        if (!logger->LogIntHistogram(cobalt_registry::kPowerUsageMetricId, index, name, histogram)) {
          FX_LOGS(INFO) << "TestLogIntHistogram : FAIL";
          return false;
        }
      }
    }
  
    histogram.clear();
  
    // Set up and send bandwidth_usage histogram.
    for (uint32_t bucket = 0; bucket < kBandwidthUsageBuckets; bucket++) {
      histogram[bucket] = kBandwidthUsageBuckets - bucket + 1;
    }
    for (uint32_t index : kBandwidthUsageIndices) {
      for (std::string name : kApplicationComponentNames) {
        if (!logger->LogIntHistogram(cobalt_registry::kBandwidthUsageMetricId, index, name,
                                     histogram)) {
          FX_LOGS(INFO) << "TestLogIntHistogram : FAIL";
          return false;
        }
      }
    }
  
    return SendAndCheckSuccess("TestLogIntHistogram", logger);
  }
  
  bool TestLogCustomEvent(CobaltTestAppLogger* logger) {
    FX_LOGS(INFO) << "========================";
    FX_LOGS(INFO) << "TestLogCustomEvent";
    bool success =
        logger->LogCustomMetricsTestProto(cobalt_registry::kQueryResponseMetricId,
                                          "test " + std::to_string(std::time(0)),
                                          100, 1);
  
    FX_LOGS(INFO) << "TestLogCustomEvent : " << (success ? "PASS" : "FAIL");
  
    return SendAndCheckSuccess("TestLogCustomEvent", logger);
  }
  
  bool TestLogCobaltEvent(CobaltTestAppLogger* logger) {
    FX_LOGS(INFO) << "========================";
    FX_LOGS(INFO) << "TestLogCobaltEvent";
  
    if (logger->LogCobaltEvent(
            CobaltEventBuilder(cobalt_registry::kErrorOccurredMetricId).as_event())) {
      // A LogEvent with no event codes is invalid.
      FX_LOGS(INFO) << "TestLogCobaltEvent: FAIL";
      return false;
    }
  
    if (logger->LogCobaltEvent(CobaltEventBuilder(cobalt_registry::kErrorOccurredMetricId)
                                   .with_event_code(0)
                                   .with_event_code(0)
                                   .as_event())) {
      // A LogEvent with more than 1 event code is invalid.
      FX_LOGS(INFO) << "TestLogCobaltEvent: FAIL";
      return false;
    }
  
    for (uint32_t index : kErrorOccurredIndicesToUse) {
      if (!logger->LogCobaltEvent(CobaltEventBuilder(cobalt_registry::kErrorOccurredMetricId)
                                      .with_event_code(index)
                                      .as_event())) {
        FX_LOGS(INFO) << "TestLogCobaltEvent: FAIL";
        return false;
      }
    }
  
    if (!SendAndCheckSuccess("TestLogCobaltEvent", logger)) {
      return false;
    }
  
    for (uint32_t index : kFileSystemCacheMissesIndices) {
      for (std::string name : kFileSystemCacheMissesComponentNames) {
        if (!logger->LogCobaltEvent(
                CobaltEventBuilder(cobalt_registry::kFileSystemCacheMissesMetricId)
                    .with_event_code(index)
                    .with_component(name)
                    .as_count_event(0, kFileSystemCacheMissesCountMax - index))) {
          FX_LOGS(INFO) << "TestLogCobaltEvent: FAIL";
          return false;
        }
      }
    }
  
    if (!SendAndCheckSuccess("TestLogCobaltEvent", logger)) {
      return false;
    }
  
    for (uint32_t index : kUpdateDurationIndices) {
      for (std::string name : kUpdateDurationComponentNames) {
        for (int64_t value : kUpdateDurationValues) {
          if (!logger->LogCobaltEvent(CobaltEventBuilder(cobalt_registry::kUpdateDurationMetricId)
                                          .with_event_code(index)
                                          .with_component(name)
                                          .as_elapsed_time(value))) {
            FX_LOGS(INFO) << "LogElapsedTime(" << cobalt_registry::kUpdateDurationMetricId << ", "
                          << index << ", " << name << ", " << value << ")";
            FX_LOGS(INFO) << "TestLogCobaltEvent: FAIL";
            return false;
          }
        }
      }
    }
  
    return SendAndCheckSuccess("TestLogCobaltEvent", logger);
  }
  
  ////////////////////// Tests using local aggregation ///////////////////////
  
  // A helper function which generates locally aggregated observations for
  // |day_index| and checks that the number of generated observations is equal to
  // the expected number for each locally aggregated report ID. The keys of
  // |expected_num_obs| should be the elements of |kAggregatedReportIds|.
  bool GenerateObsAndCheckCount(uint32_t day_index,
                                fuchsia::cobalt::ControllerSyncPtr* cobalt_controller,
                                uint32_t project_id,
                                std::map<std::pair<uint32_t, uint32_t>, uint64_t> expected_num_obs) {
    FX_LOGS(INFO) << "Generating locally aggregated observations for day index " << day_index;
    std::vector<fuchsia::cobalt::ReportSpec> aggregatedReportSpecs;
    std::vector<std::pair<uint32_t, uint32_t>> aggregatedReportIds;
    for (const auto& [ids, expected] : expected_num_obs) {
      const auto& [metric_id, report_id] = ids;
      fuchsia::cobalt::ReportSpec report_spec = std::move(fuchsia::cobalt::ReportSpec()
                                                              .set_customer_id(1)
                                                              .set_project_id(project_id)
                                                              .set_metric_id(metric_id)
                                                              .set_report_id(report_id));
      aggregatedReportSpecs.push_back(std::move(report_spec));
      aggregatedReportIds.push_back(ids);
    }
    std::vector<uint64_t> num_obs;
    (*cobalt_controller)
        ->GenerateAggregatedObservations(day_index, std::move(aggregatedReportSpecs), &num_obs);
    for (size_t i = 0; i < aggregatedReportIds.size(); i++) {
      const auto& [metric_id, report_id] = aggregatedReportIds[i];
      uint64_t expected = expected_num_obs[{metric_id, report_id}];
      uint64_t found = num_obs[i];
      if (found != expected) {
        FX_LOGS(INFO) << "Expected " << expected << " observations for report MetricID " << metric_id
                      << " ReportId " << report_id << ", found " << found;
        return false;
      }
    }
    return true;
  }
  
  bool TestLogEventWithAggregation(CobaltTestAppLogger* logger, SystemClockInterface* clock,
                                   fuchsia::cobalt::ControllerSyncPtr* cobalt_controller,
                                   const size_t backfill_days, uint32_t project_id) {
    FX_LOGS(INFO) << "========================";
    FX_LOGS(INFO) << "TestLogEventWithAggregation";
    uint32_t day_index = CurrentDayIndex(clock);
    for (uint32_t index : kFeaturesActiveIndices) {
      if (!logger->LogEvent(cobalt_registry::kFeaturesActiveMetricId, index)) {
        FX_LOGS(INFO) << "Failed to log event with index " << index << ".";
        FX_LOGS(INFO) << "TestLogEventWithAggregation : FAIL";
        return false;
      }
    }
    if (logger->LogEvent(cobalt_registry::kFeaturesActiveMetricId, kFeaturesActiveInvalidIndex)) {
      FX_LOGS(INFO) << "Failed to reject event with invalid index " << kFeaturesActiveInvalidIndex
                    << ".";
      FX_LOGS(INFO) << "TestLogEventWithAggregation : FAIL";
      return false;
    }
  
    if (CurrentDayIndex(clock) != day_index) {
      // TODO(fxbug.dev/52750): The date has changed mid-test. We are currently unable to
      // deal with this so we fail this test and our caller may try again.
      FX_LOGS(INFO) << "Quitting test because the date has changed mid-test.";
      return false;
    }
  
    // Expect to generate |kNumAggregatedObservations| for each day in the
    // backfill period, plus the current day. Expect to generate no observations
    // when GenerateObservations is called for the second time on the same day.
    std::map<std::pair<uint32_t, uint32_t>, uint64_t> expected_num_obs;
    std::map<std::pair<uint32_t, uint32_t>, uint64_t> expect_no_obs;
    for (const auto& [ids, expected] : kNumAggregatedObservations) {
      expected_num_obs[ids] = (1 + backfill_days) * expected;
      expect_no_obs[ids] = 0;
    }
    if (!GenerateObsAndCheckCount(day_index, cobalt_controller, project_id, expected_num_obs)) {
      FX_LOGS(INFO) << "TestLogEventWithAggregation : FAIL";
      return false;
    }
    if (!GenerateObsAndCheckCount(day_index, cobalt_controller, project_id, expect_no_obs)) {
      FX_LOGS(INFO) << "TestLogEventWithAggregation : FAIL";
      return false;
    }
    return SendAndCheckSuccess("TestLogEventWithAggregation", logger);
  }
  
  bool TestLogEventCountWithAggregation(CobaltTestAppLogger* logger, SystemClockInterface* clock,
                                        fuchsia::cobalt::ControllerSyncPtr* cobalt_controller,
                                        const size_t backfill_days, uint32_t project_id) {
    FX_LOGS(INFO) << "========================";
    FX_LOGS(INFO) << "TestLogEventCountWithAggregation";
    std::map<std::pair<uint32_t, uint32_t>, uint64_t> expected_num_obs;
    std::map<std::pair<uint32_t, uint32_t>, uint64_t> expect_no_obs;
    for (const auto& [ids, expected] : kNumAggregatedObservations) {
      expected_num_obs[ids] = (1 + backfill_days) * expected;
      expect_no_obs[ids] = 0;
    }
    uint32_t day_index = CurrentDayIndex(clock);
    for (uint32_t index : kConnectionAttemptsIndices) {
      for (std::string component : kConnectionAttemptsComponentNames) {
        if (index != 0) {
          // Log a count depending on the index.
          int64_t count = index * 5;
          if (!logger->LogEventCount(cobalt_registry::kConnectionAttemptsMetricId, index, component,
                                     count)) {
            FX_LOGS(INFO) << "Failed to log event count for index " << index << " and component "
                          << component << ".";
            FX_LOGS(INFO) << "TestLogEventCountWithAggregation : FAIL";
            return false;
          }
          expected_num_obs[{
              cobalt_registry::kConnectionAttemptsMetricId,
              cobalt_registry::kConnectionAttemptsConnectionAttemptsPerDeviceCountReportId}] +=
              kConnectionAttemptsNumWindowSizes;
        }
      }
    }
  
    if (CurrentDayIndex(clock) != day_index) {
      // TODO(fxbug.dev/52750): The date has changed mid-test. We are currently unable to
      // deal with this so we fail this test and our caller may try again.
      FX_LOGS(INFO) << "Quitting test because the date has changed mid-test.";
      return false;
    }
  
    if (!GenerateObsAndCheckCount(day_index, cobalt_controller, project_id, expected_num_obs)) {
      FX_LOGS(INFO) << "TestLogEventCountWithAggregation : FAIL";
      return false;
    }
    if (!GenerateObsAndCheckCount(day_index, cobalt_controller, project_id, expect_no_obs)) {
      FX_LOGS(INFO) << "TestLogEventCountWithAggregation : FAIL";
      return false;
    }
    return SendAndCheckSuccess("TestLogEventCountWithAggregation", logger);
  }
  
  bool TestLogElapsedTimeWithAggregation(CobaltTestAppLogger* logger, SystemClockInterface* clock,
                                         fuchsia::cobalt::ControllerSyncPtr* cobalt_controller,
                                         const size_t backfill_days, uint32_t project_id) {
    FX_LOGS(INFO) << "========================";
    FX_LOGS(INFO) << "TestLogElapsedTimeWithAggregation";
    // Expect to generate |kNumAggregatedObservations| for each day in the
    // backfill period, plus the current day. Expect to generate no observations
    // when GenerateObservations is called for the second time on the same day.
    std::map<std::pair<uint32_t, uint32_t>, uint64_t> expected_num_obs;
    std::map<std::pair<uint32_t, uint32_t>, uint64_t> expect_no_obs;
    for (const auto& [ids, expected] : kNumAggregatedObservations) {
      expected_num_obs[ids] = (1 + backfill_days) * expected;
      expect_no_obs[ids] = 0;
    }
  
    uint32_t day_index = CurrentDayIndex(clock);
    for (uint32_t index : kStreamingTimeIndices) {
      for (std::string component : kStreamingTimeComponentNames) {
        // Log a duration depending on the index.
        if (index != 0) {
          int64_t duration = index * 100;
          if (!logger->LogElapsedTime(cobalt_registry::kStreamingTimeMetricId, index, component,
                                      duration)) {
            FX_LOGS(INFO) << "Failed to log elapsed time for index " << index << " and component "
                          << component << ".";
            FX_LOGS(INFO) << "TestLogElapsedTimeWithAggregation : FAIL";
            return false;
          }
          expected_num_obs[{cobalt_registry::kStreamingTimeMetricId,
                            cobalt_registry::kStreamingTimeStreamingTimePerDeviceTotalReportId}] +=
              kStreamingTimeNumWindowSizes;
        }
      }
    }
  
    if (CurrentDayIndex(clock) != day_index) {
      // TODO(fxbug.dev/52750): The date has changed mid-test. We are currently unable to
      // deal with this so we fail this test and our caller may try again.
      FX_LOGS(INFO) << "Quitting test because the date has changed mid-test.";
      return false;
    }
  
    if (!GenerateObsAndCheckCount(day_index, cobalt_controller, project_id, expected_num_obs)) {
      FX_LOGS(INFO) << "TestLogElapsedTimeWithAggregation : FAIL";
      return false;
    }
    if (!GenerateObsAndCheckCount(day_index, cobalt_controller, project_id, expect_no_obs)) {
      FX_LOGS(INFO) << "TestLogElapsedTimeWithAggregation : FAIL";
      return false;
    }
    return SendAndCheckSuccess("TestLogElapsedTimeWithAggregation", logger);
  }
  
  // INTEGER metrics for update_duration_new, streaming_time_new and application_memory_new.
  bool TestLogInteger(CobaltTestAppLogger* logger, SystemClockInterface* clock,
                      fuchsia::cobalt::ControllerSyncPtr* cobalt_controller,
                      const size_t backfill_days, uint32_t project_id) {
    FX_LOGS(INFO) << "========================";
    FX_LOGS(INFO) << "TestLogInteger";
  
    // All data for a single report is aggregated into a single observation.
    std::map<std::pair<uint32_t, uint32_t>, uint64_t> expected_num_obs = {
        {{cobalt_registry::kUpdateDurationNewMetricId,
          cobalt_registry::kUpdateDurationNewUpdateDurationTimingStatsReportId},
         1},
        {{cobalt_registry::kStreamingTimeNewMetricId,
          cobalt_registry::kStreamingTimeNewStreamingTimePerDeviceTotalReportId},
         1},
        {{cobalt_registry::kApplicationMemoryNewMetricId,
          cobalt_registry::kApplicationMemoryNewApplicationMemoryHistogramsReportId},
         1},
        {{cobalt_registry::kApplicationMemoryNewMetricId,
          cobalt_registry::
              kApplicationMemoryNewApplicationMemoryHistogramsLinearConstantWidthReportId},
         1},
        {{cobalt_registry::kApplicationMemoryNewMetricId,
          cobalt_registry::kApplicationMemoryNewApplicationMemoryStatsReportId},
         1},
    };
    std::map<std::pair<uint32_t, uint32_t>, uint64_t> expect_no_obs = {
        {{cobalt_registry::kUpdateDurationNewMetricId,
          cobalt_registry::kUpdateDurationNewUpdateDurationTimingStatsReportId},
         0},
        {{cobalt_registry::kStreamingTimeNewMetricId,
          cobalt_registry::kStreamingTimeNewStreamingTimePerDeviceTotalReportId},
         0},
        {{cobalt_registry::kApplicationMemoryNewMetricId,
          cobalt_registry::kApplicationMemoryNewApplicationMemoryHistogramsReportId},
         0},
        {{cobalt_registry::kApplicationMemoryNewMetricId,
          cobalt_registry::
              kApplicationMemoryNewApplicationMemoryHistogramsLinearConstantWidthReportId},
         0},
        {{cobalt_registry::kApplicationMemoryNewMetricId,
          cobalt_registry::kApplicationMemoryNewApplicationMemoryStatsReportId},
         0},
    };
  
    uint32_t day_index = CurrentDayIndex(clock);
  
    // For each of the two event_codes, log one observation with an integer value.
    for (uint32_t error_name_index : kUpdateDurationNewErrorNameIndices) {
      for (uint32_t stage_index : kUpdateDurationNewStageIndices) {
        for (int64_t value : kUpdateDurationNewValues) {
          if (!logger->LogInteger(cobalt_registry::kUpdateDurationNewMetricId,
                                  {error_name_index, stage_index}, value)) {
            FX_LOGS(INFO) << "LogInteger(" << cobalt_registry::kUpdateDurationNewMetricId
                          << ", { error_name: " << error_name_index
                          << ", update_duration_stage: " << stage_index << " }, " << value << ")";
            FX_LOGS(INFO) << "TestLogInteger : FAIL";
            return false;
          }
        }
      }
    }
  
    for (uint32_t type_index : kStreamingTimeNewTypeIndices) {
      for (uint32_t mod_name_index : kStreamingTimeNewModuleNameIndices) {
        for (int64_t duration : kStreamingTimeNewValues) {
          if (!logger->LogInteger(cobalt_registry::kStreamingTimeNewMetricId,
                                  {type_index, mod_name_index}, duration)) {
            FX_LOGS(INFO) << "LogInteger(" << cobalt_registry::kStreamingTimeNewMetricId
                          << ", { type: " << type_index << ", module_name: " << mod_name_index
                          << " }, " << duration << ")";
            FX_LOGS(INFO) << "TestLogInteger: FAIL";
            return false;
          }
        }
      }
    }
  
    for (uint32_t memory_type_index : kApplicationMemoryNewMemoryTypeIndices) {
      for (uint32_t app_name_index : kApplicationMemoryNewApplicationNameIndices) {
        for (int64_t value : kApplicationMemoryNewValues) {
          if (!logger->LogInteger(cobalt_registry::kApplicationMemoryNewMetricId,
                                  {memory_type_index, app_name_index}, value)) {
            FX_LOGS(INFO) << "LogInteger(" << cobalt_registry::kApplicationMemoryNewMetricId
                          << ", { memory_type: " << memory_type_index
                          << ", application_name: " << app_name_index << " }, " << value << ")";
            FX_LOGS(INFO) << "TestLogInteger: FAIL";
            return false;
          }
        }
      }
    }
  
    if (CurrentDayIndex(clock) != day_index) {
      // TODO(fxb/52750) The date has changed mid-test. We are currently unable to
      // deal with this so we fail this test and our caller may try again.
      FX_LOGS(INFO) << "Quitting test because the date has changed mid-test.";
      return false;
    }
  
    if (!GenerateObsAndCheckCount(day_index, cobalt_controller, project_id, expected_num_obs)) {
      FX_LOGS(INFO) << "TestLogInteger : FAIL";
      return false;
    }
    if (!GenerateObsAndCheckCount(day_index, cobalt_controller, project_id, expect_no_obs)) {
      FX_LOGS(INFO) << "TestLogInteger : FAIL";
      return false;
    }
    return SendAndCheckSuccess("TestLogInteger", logger);
  }
  
  // OCCURRENCE metrics for features_active_new, file_system_cache_misses_new, and
  // connection_attempts_new.
  bool TestLogOccurrence(CobaltTestAppLogger* logger, SystemClockInterface* clock,
                         fuchsia::cobalt::ControllerSyncPtr* cobalt_controller,
                         const size_t backfill_days, uint32_t project_id) {
    FX_LOGS(INFO) << "========================";
    FX_LOGS(INFO) << "TestLogOccurrence";
  
    // All occurrence count data for a single report is aggregated into a single observation.
    std::map<std::pair<uint32_t, uint32_t>, uint64_t> expected_num_obs = {
        {{cobalt_registry::kFeaturesActiveNewMetricId,
          cobalt_registry::kFeaturesActiveNewFeaturesActiveUniqueDevicesReportId},
         1},
        {{cobalt_registry::kFileSystemCacheMissesNewMetricId,
          cobalt_registry::kFileSystemCacheMissesNewFileSystemCacheMissCountsReportId},
         1},
        {{cobalt_registry::kFileSystemCacheMissesNewMetricId,
          cobalt_registry::kFileSystemCacheMissesNewFileSystemCacheMissHistogramsReportId},
         1},
        {{cobalt_registry::kFileSystemCacheMissesNewMetricId,
          cobalt_registry::kFileSystemCacheMissesNewFileSystemCacheMissStatsReportId},
         1},
        {{cobalt_registry::kConnectionAttemptsNewMetricId,
          cobalt_registry::kConnectionAttemptsNewConnectionAttemptsPerDeviceCountReportId},
         1},
    };
    std::map<std::pair<uint32_t, uint32_t>, uint64_t> expect_no_obs{
        {{cobalt_registry::kFeaturesActiveNewMetricId,
          cobalt_registry::kFeaturesActiveNewFeaturesActiveUniqueDevicesReportId},
         0},
        {{cobalt_registry::kFileSystemCacheMissesNewMetricId,
          cobalt_registry::kFileSystemCacheMissesNewFileSystemCacheMissCountsReportId},
         0},
        {{cobalt_registry::kFileSystemCacheMissesNewMetricId,
          cobalt_registry::kFileSystemCacheMissesNewFileSystemCacheMissHistogramsReportId},
         0},
        {{cobalt_registry::kFileSystemCacheMissesNewMetricId,
          cobalt_registry::kFileSystemCacheMissesNewFileSystemCacheMissStatsReportId},
         0},
        {{cobalt_registry::kConnectionAttemptsNewMetricId,
          cobalt_registry::kConnectionAttemptsNewConnectionAttemptsPerDeviceCountReportId},
         0},
    };
  
    uint32_t day_index = CurrentDayIndex(clock);
  
    for (uint32_t skill_index : kFeaturesActiveNewSkillIndices) {
      for (uint64_t count : kFeaturesActiveNewCounts) {
        if (!logger->LogOccurrence(cobalt_registry::kFeaturesActiveNewMetricId, {skill_index},
                                   count)) {
          FX_LOGS(INFO) << "LogOccurrence(" << cobalt_registry::kFeaturesActiveNewMetricId
                        << ", { skill: " << skill_index << " }, " << count << ")";
          FX_LOGS(INFO) << "TestLogOccurrence : FAIL";
          return false;
        }
      }
    }
  
    for (uint32_t state_index : kFileSystemCacheMissesNewEncryptionStateIndices) {
      for (uint32_t type_index : kFileSystemCacheMissesNewFileSystemTypeIndices) {
        for (int64_t count : kFileSystemCacheMissesNewCounts) {
          if (!logger->LogOccurrence(cobalt_registry::kFileSystemCacheMissesNewMetricId,
                                     {state_index, type_index}, count)) {
            FX_LOGS(INFO) << "LogOccurrence(" << cobalt_registry::kFileSystemCacheMissesNewMetricId
                          << ", { encryption_state: " << state_index
                          << ", file_system_type: " << type_index << " }, " << count << ")";
            FX_LOGS(INFO) << "TestLogOccurrence: FAIL";
            return false;
          }
        }
      }
    }
  
    for (uint32_t status_index : kConnectionAttemptsNewStatusIndices) {
      for (uint32_t host_index : kConnectionAttemptsNewHostNameIndices) {
        for (int64_t count : kConnectionAttemptsNewCounts) {
          if (!logger->LogOccurrence(cobalt_registry::kConnectionAttemptsNewMetricId,
                                     {status_index, host_index}, count)) {
            FX_LOGS(INFO) << "LogOccurrence(" << cobalt_registry::kConnectionAttemptsNewMetricId
                          << ", { status: " << status_index << ", host_name: " << host_index << " }, "
                          << count << ")";
            FX_LOGS(INFO) << "TestLogOccurrence: FAIL";
            return false;
          }
        }
      }
    }
  
    if (CurrentDayIndex(clock) != day_index) {
      // TODO(fxb/52750) The date has changed mid-test. We are currently unable to
      // deal with this so we fail this test and our caller may try again.
      FX_LOGS(INFO) << "Quitting test because the date has changed mid-test.";
      return false;
    }
  
    if (!GenerateObsAndCheckCount(day_index, cobalt_controller, project_id, expected_num_obs)) {
      FX_LOGS(INFO) << "TestLogOccurrence : FAIL";
      return false;
    }
    if (!GenerateObsAndCheckCount(day_index, cobalt_controller, project_id, expect_no_obs)) {
      FX_LOGS(INFO) << "TestLogOccurrence : FAIL";
      return false;
    }
    return SendAndCheckSuccess("TestLogOccurrence", logger);
  }
  
  // power_usage_new and bandwidth_usage_new using INTEGER_HISTOGRAM metric.
  //
  // For each event vector, log one observation in each histogram bucket, using
  // decreasing values per bucket.
  bool TestLogIntegerHistogram(CobaltTestAppLogger* logger, SystemClockInterface* clock,
                               fuchsia::cobalt::ControllerSyncPtr* cobalt_controller,
                               const size_t backfill_days, uint32_t project_id) {
    FX_LOGS(INFO) << "========================";
    FX_LOGS(INFO) << "TestLogIntegerHistogram";
    std::map<uint32_t, uint64_t> histogram;
  
    // All integer histogram data is aggregated into a single observation.
    std::map<std::pair<uint32_t, uint32_t>, uint64_t> expected_num_obs = {
        {{cobalt_registry::kPowerUsageNewMetricId,
          cobalt_registry::kPowerUsageNewPowerUsageHistogramsReportId},
         1},
        {{cobalt_registry::kBandwidthUsageNewMetricId,
          cobalt_registry::kBandwidthUsageNewBandwidthUsageHistogramsReportId},
         1},
    };
    std::map<std::pair<uint32_t, uint32_t>, uint64_t> expect_no_obs = {
        {{cobalt_registry::kPowerUsageNewMetricId,
          cobalt_registry::kPowerUsageNewPowerUsageHistogramsReportId},
         0},
        {{cobalt_registry::kBandwidthUsageNewMetricId,
          cobalt_registry::kBandwidthUsageNewBandwidthUsageHistogramsReportId},
         0},
    };
  
    uint32_t day_index = CurrentDayIndex(clock);
  
    // Set up and send power_usage_new histograms.
    for (uint32_t bucket = 0; bucket < kPowerUsageNewBuckets; bucket++) {
      histogram[bucket] = kPowerUsageNewBuckets - bucket + 1;
    }
    for (uint32_t state_index : kPowerUsageNewApplicationStateIndices) {
      for (uint32_t app_name_index : kPowerUsageNewApplicationNameIndices) {
        if (!logger->LogIntegerHistogram(cobalt_registry::kPowerUsageNewMetricId,
                                         {state_index, app_name_index}, histogram)) {
          FX_LOGS(INFO) << "LogIntegerHistogram(" << cobalt_registry::kPowerUsageNewMetricId
                        << ", { application_state: " << state_index
                        << ", application_name: " << app_name_index << " }, buckets["
                        << kPowerUsageNewBuckets << "])";
          FX_LOGS(INFO) << "TestLogIntegerHistogram : FAIL";
          return false;
        }
      }
    }
  
    histogram.clear();
  
    // Set up and send bandwidth_usage_new histograms.
    for (uint32_t bucket = 0; bucket < kBandwidthUsageNewBuckets; bucket++) {
      histogram[bucket] = kBandwidthUsageNewBuckets - bucket + 1;
    }
    for (uint32_t state_index : kBandwidthUsageNewApplicationStateIndices) {
      for (uint32_t app_name_index : kBandwidthUsageNewApplicationNameIndices) {
        if (!logger->LogIntegerHistogram(cobalt_registry::kBandwidthUsageNewMetricId,
                                         {state_index, app_name_index}, histogram)) {
          FX_LOGS(INFO) << "LogIntegerHistogram(" << cobalt_registry::kBandwidthUsageNewMetricId
                        << ", { application_state: " << state_index
                        << ", application_name: " << app_name_index << " }, buckets["
                        << kBandwidthUsageNewBuckets << "])";
          FX_LOGS(INFO) << "TestLogIntegerHistogram : FAIL";
          return false;
        }
      }
    }
  
    if (CurrentDayIndex(clock) != day_index) {
      // TODO(fxb/52750) The date has changed mid-test. We are currently unable to
      // deal with this so we fail this test and our caller may try again.
      FX_LOGS(INFO) << "Quitting test because the date has changed mid-test.";
      return false;
    }
  
    if (!GenerateObsAndCheckCount(day_index, cobalt_controller, project_id, expected_num_obs)) {
      FX_LOGS(INFO) << "TestLogIntegerHistogram : FAIL";
      return false;
    }
    if (!GenerateObsAndCheckCount(day_index, cobalt_controller, project_id, expect_no_obs)) {
      FX_LOGS(INFO) << "TestLogIntegerHistogram : FAIL";
      return false;
    }
    return SendAndCheckSuccess("TestLogIntegerHistogram", logger);
  }
  
  // error_occurred_components using STRING metric.
  //
  // For each of the three event_codes, log each of the five application components.
  bool TestLogString(CobaltTestAppLogger* logger, SystemClockInterface* clock,
                     fuchsia::cobalt::ControllerSyncPtr* cobalt_controller,
                     const size_t backfill_days, uint32_t project_id) {
    FX_LOGS(INFO) << "========================";
    FX_LOGS(INFO) << "TestLogString";
  
    // All string data is aggregated into a single observation.
    std::map<std::pair<uint32_t, uint32_t>, uint64_t> expected_num_obs;
    std::map<std::pair<uint32_t, uint32_t>, uint64_t> expect_no_obs;
    expected_num_obs[{cobalt_registry::kErrorOccurredComponentsMetricId,
                      cobalt_registry::kErrorOccurredComponentsErrorCountsReportId}] = 1;
    expect_no_obs[{cobalt_registry::kErrorOccurredComponentsMetricId,
                   cobalt_registry::kErrorOccurredComponentsErrorCountsReportId}] = 0;
  
    uint32_t day_index = CurrentDayIndex(clock);
    for (uint32_t status_index : kErrorOccurredComponentsStatusIndices) {
      for (std::string component : kApplicationComponentNames) {
        if (!logger->LogString(cobalt_registry::kErrorOccurredComponentsMetricId, {status_index},
                               component)) {
          FX_LOGS(INFO) << "LogString(" << cobalt_registry::kErrorOccurredComponentsMetricId
                        << ", { status: " << status_index << " }, " << component << ")";
          FX_LOGS(INFO) << "TestLogString : FAIL";
          return false;
        }
      }
    }
  
    if (CurrentDayIndex(clock) != day_index) {
      // TODO(fxb/52750) The date has changed mid-test. We are currently unable to
      // deal with this so we fail this test and our caller may try again.
      FX_LOGS(INFO) << "Quitting test because the date has changed mid-test.";
      return false;
    }
  
    if (!GenerateObsAndCheckCount(day_index, cobalt_controller, project_id, expected_num_obs)) {
      FX_LOGS(INFO) << "TestLogString : FAIL";
      return false;
    }
    if (!GenerateObsAndCheckCount(day_index, cobalt_controller, project_id, expect_no_obs)) {
      FX_LOGS(INFO) << "TestLogString : FAIL";
      return false;
    }
    return SendAndCheckSuccess("TestLogString", logger);
  }
  
  }  // namespace testapp
  }  // namespace cobalt
  