// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "tools/mac/power/power_sampler/smc_sampler.h"

#include <memory>

#include "base/containers/flat_map.h"
#include "base/memory/ptr_util.h"
#include "components/power_metrics/smc_mac.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "tools/mac/power/power_sampler/battery_sampler.h"

namespace power_sampler {

namespace {

using testing::UnorderedElementsAre;

class TestSMCReader : public power_metrics::SMCReader {
 public:
  TestSMCReader()
      : power_metrics::SMCReader(base::mac::ScopedIOObject<io_object_t>()) {}

  void set_key(SMCKeyIdentifier key, absl::optional<double> value) {
    keys_[key] = value;
  }

  // power_metrics::SMCReader:
  absl::optional<double> ReadKey(SMCKeyIdentifier identifier) override {
    return keys_[identifier];
  }

 private:
  base::flat_map<SMCKeyIdentifier, absl::optional<double>> keys_;
};

}  // namespace

class SMCSamplerTest : public testing::Test {
 public:
  SMCSamplerTest() {
    std::unique_ptr<TestSMCReader> reader = std::make_unique<TestSMCReader>();
    reader_ = reader.get();
    sampler_ = base::WrapUnique(new SMCSampler(std::move(reader)));
  }

  TestSMCReader* reader_ = nullptr;
  std::unique_ptr<SMCSampler> sampler_;
};

TEST_F(SMCSamplerTest, NameAndGetDatumNameUnits) {
  EXPECT_EQ("smc", sampler_->GetName());

  auto datum_name_units = sampler_->GetDatumNameUnits();
  EXPECT_THAT(datum_name_units,
              UnorderedElementsAre(std::make_pair("total_power", "w"),
                                   std::make_pair("cpu_package_cpu_power", "w"),
                                   std::make_pair("cpu_package_gpu_power", "w"),
                                   std::make_pair("gpu0_power", "w"),
                                   std::make_pair("gpu1_power", "w"),
                                   std::make_pair("cpu_temperature", "C")));
}

TEST_F(SMCSamplerTest, GetSample_AllFieldsAvailable) {
  reader_->set_key(SMCKeyIdentifier::TotalPower, 1);
  reader_->set_key(SMCKeyIdentifier::CPUPower, 2);
  reader_->set_key(SMCKeyIdentifier::iGPUPower, 3);
  reader_->set_key(SMCKeyIdentifier::GPU0Power, 4);
  reader_->set_key(SMCKeyIdentifier::GPU1Power, 5);
  reader_->set_key(SMCKeyIdentifier::CPUTemperature, 6);

  Sampler::Sample sample = sampler_->GetSample(base::TimeTicks());
  EXPECT_THAT(sample,
              UnorderedElementsAre(std::make_pair("total_power", 1),
                                   std::make_pair("cpu_package_cpu_power", 2),
                                   std::make_pair("cpu_package_gpu_power", 3),
                                   std::make_pair("gpu0_power", 4),
                                   std::make_pair("gpu1_power", 5),
                                   std::make_pair("cpu_temperature", 6)));
}

TEST_F(SMCSamplerTest, GetSample_IndividualFieldNotAvailable) {
  reader_->set_key(SMCKeyIdentifier::TotalPower, 1);
  reader_->set_key(SMCKeyIdentifier::CPUPower, 2);
  reader_->set_key(SMCKeyIdentifier::iGPUPower, 3);
  reader_->set_key(SMCKeyIdentifier::GPU0Power, 4);
  reader_->set_key(SMCKeyIdentifier::GPU1Power, 5);
  reader_->set_key(SMCKeyIdentifier::CPUTemperature, 6);

  {
    reader_->set_key(SMCKeyIdentifier::TotalPower, absl::nullopt);
    Sampler::Sample sample = sampler_->GetSample(base::TimeTicks());
    EXPECT_THAT(sample,
                UnorderedElementsAre(std::make_pair("cpu_package_cpu_power", 2),
                                     std::make_pair("cpu_package_gpu_power", 3),
                                     std::make_pair("gpu0_power", 4),
                                     std::make_pair("gpu1_power", 5),
                                     std::make_pair("cpu_temperature", 6)));
    reader_->set_key(SMCKeyIdentifier::TotalPower, 1);
  }

  {
    reader_->set_key(SMCKeyIdentifier::CPUPower, absl::nullopt);
    Sampler::Sample sample = sampler_->GetSample(base::TimeTicks());
    EXPECT_THAT(sample,
                UnorderedElementsAre(std::make_pair("total_power", 1),
                                     std::make_pair("cpu_package_gpu_power", 3),
                                     std::make_pair("gpu0_power", 4),
                                     std::make_pair("gpu1_power", 5),
                                     std::make_pair("cpu_temperature", 6)));
    reader_->set_key(SMCKeyIdentifier::CPUPower, 2);
  }

  {
    reader_->set_key(SMCKeyIdentifier::iGPUPower, absl::nullopt);
    Sampler::Sample sample = sampler_->GetSample(base::TimeTicks());
    EXPECT_THAT(sample,
                UnorderedElementsAre(std::make_pair("total_power", 1),
                                     std::make_pair("cpu_package_cpu_power", 2),
                                     std::make_pair("gpu0_power", 4),
                                     std::make_pair("gpu1_power", 5),
                                     std::make_pair("cpu_temperature", 6)));
    reader_->set_key(SMCKeyIdentifier::iGPUPower, 3);
  }

  {
    reader_->set_key(SMCKeyIdentifier::GPU0Power, absl::nullopt);
    Sampler::Sample sample = sampler_->GetSample(base::TimeTicks());
    EXPECT_THAT(sample,
                UnorderedElementsAre(std::make_pair("total_power", 1),
                                     std::make_pair("cpu_package_cpu_power", 2),
                                     std::make_pair("cpu_package_gpu_power", 3),
                                     std::make_pair("gpu1_power", 5),
                                     std::make_pair("cpu_temperature", 6)));
    reader_->set_key(SMCKeyIdentifier::GPU0Power, 4);
  }

  {
    reader_->set_key(SMCKeyIdentifier::GPU1Power, absl::nullopt);
    Sampler::Sample sample = sampler_->GetSample(base::TimeTicks());
    EXPECT_THAT(sample,
                UnorderedElementsAre(std::make_pair("total_power", 1),
                                     std::make_pair("cpu_package_cpu_power", 2),
                                     std::make_pair("cpu_package_gpu_power", 3),
                                     std::make_pair("gpu0_power", 4),
                                     std::make_pair("cpu_temperature", 6)));
    reader_->set_key(SMCKeyIdentifier::GPU1Power, 5);
  }

  {
    reader_->set_key(SMCKeyIdentifier::CPUTemperature, absl::nullopt);
    Sampler::Sample sample = sampler_->GetSample(base::TimeTicks());
    EXPECT_THAT(sample,
                UnorderedElementsAre(std::make_pair("total_power", 1),
                                     std::make_pair("cpu_package_cpu_power", 2),
                                     std::make_pair("cpu_package_gpu_power", 3),
                                     std::make_pair("gpu0_power", 4),
                                     std::make_pair("gpu1_power", 5)));
    reader_->set_key(SMCKeyIdentifier::CPUTemperature, 6);
  }
}

}  // namespace power_sampler
