// Copyright 2020 The Chromium 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 "base/util/memory_pressure/system_memory_pressure_evaluator_fuchsia.h"

#include <fuchsia/memorypressure/cpp/fidl.h>
#include <fuchsia/memorypressure/cpp/fidl_test_base.h>

#include "base/fuchsia/scoped_service_binding.h"
#include "base/fuchsia/test_component_context_for_process.h"
#include "base/run_loop.h"
#include "base/test/task_environment.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace util {

namespace {
class MockMemoryPressureVoter : public MemoryPressureVoter {
 public:
  MOCK_METHOD2(SetVote,
               void(base::MemoryPressureListener::MemoryPressureLevel, bool));
};
}  // namespace

class SystemMemoryPressureEvaluatorFuchsiaTest
    : public testing::Test,
      public fuchsia::memorypressure::testing::Provider_TestBase {
 public:
  SystemMemoryPressureEvaluatorFuchsiaTest()
      : task_environment_(base::test::TaskEnvironment::MainThreadType::IO) {}

  void SendPressureLevel(fuchsia::memorypressure::Level level) {
    base::RunLoop wait_loop;
    watcher_->OnLevelChanged(
        level, [quit_loop = wait_loop.QuitClosure()]() { quit_loop.Run(); });
    wait_loop.Run();
  }

  bool have_watcher() const { return watcher_.is_bound(); }

  // fuchsia::memorypressure::Provider implementation.
  void RegisterWatcher(fidl::InterfaceHandle<fuchsia::memorypressure::Watcher>
                           watcher) override {
    watcher_.Bind(std::move(watcher));
    SendPressureLevel(fuchsia::memorypressure::Level::NORMAL);
  }

 protected:
  // fuchsia::memorypressure::testing::Provider_TestBase implementation.
  void NotImplemented_(const std::string& name) override {
    ADD_FAILURE() << "Unexpected call to method: " << name;
  }

  const base::test::SingleThreadTaskEnvironment task_environment_;

  base::TestComponentContextForProcess test_context_;

  fuchsia::memorypressure::WatcherPtr watcher_;
};

using SystemMemoryPressureEvaluatorFuchsiaDeathTest =
    SystemMemoryPressureEvaluatorFuchsiaTest;

TEST_F(SystemMemoryPressureEvaluatorFuchsiaDeathTest, ProviderUnavailable) {
  auto voter = std::make_unique<MockMemoryPressureVoter>();
  SystemMemoryPressureEvaluatorFuchsia evaluator(std::move(voter));

  // Spin the loop to allow the evaluator to notice that the Provider is not
  // available and verify that this causes a fatal failure.
  ASSERT_DEATH(base::RunLoop().RunUntilIdle(),
               "fuchsia\\.memorypressure\\.Provider disconnected: "
               "ZX_ERR_PEER_CLOSED \\(-24\\)");
}

TEST_F(SystemMemoryPressureEvaluatorFuchsiaTest, Basic) {
  base::fuchsia::ScopedServiceBinding<::fuchsia::memorypressure::Provider>
      publish_provider(test_context_.additional_services(), this);

  auto voter = std::make_unique<MockMemoryPressureVoter>();
  // NONE pressure will be reported once the first Watch() call returns, and
  // then again when the fakes system level transitions from CRITICAL->NORMAL.
  EXPECT_CALL(
      *voter,
      SetVote(base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, false))
      .Times(2);
  EXPECT_CALL(
      *voter,
      SetVote(base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL,
              true));
  EXPECT_CALL(
      *voter,
      SetVote(base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
              true));

  SystemMemoryPressureEvaluatorFuchsia evaluator(std::move(voter));

  // Spin the loop to ensure that RegisterWatcher() is processed.
  base::RunLoop().RunUntilIdle();
  ASSERT_TRUE(have_watcher());

  SendPressureLevel(fuchsia::memorypressure::Level::CRITICAL);
  EXPECT_EQ(evaluator.current_vote(),
            base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL);

  SendPressureLevel(fuchsia::memorypressure::Level::NORMAL);
  EXPECT_EQ(evaluator.current_vote(),
            base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE);

  SendPressureLevel(fuchsia::memorypressure::Level::WARNING);
  EXPECT_EQ(evaluator.current_vote(),
            base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE);
}

}  // namespace util
