chromium/components/memory_pressure/system_memory_pressure_evaluator_fuchsia_unittest.cc

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

#include "components/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 "components/memory_pressure/multi_source_memory_pressure_monitor.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace memory_pressure {

namespace {

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

class TestSystemMemoryPressureEvaluator
    : public SystemMemoryPressureEvaluatorFuchsia {
 public:
  TestSystemMemoryPressureEvaluator(std::unique_ptr<MemoryPressureVoter> voter)
      : SystemMemoryPressureEvaluatorFuchsia(std::move(voter)) {}

  TestSystemMemoryPressureEvaluator(const TestSystemMemoryPressureEvaluator&) =
      delete;
  TestSystemMemoryPressureEvaluator& operator=(
      const TestSystemMemoryPressureEvaluator&) = delete;

  MOCK_METHOD1(OnMemoryPressure,
               void(base::MemoryPressureListener::MemoryPressureLevel level));
};

}  // namespace

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

  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;
  }

  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>();
  TestSystemMemoryPressureEvaluator 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 unexpectedly, "
               "exiting: ZX_ERR_PEER_CLOSED \\(-24\\)");
}

TEST_F(SystemMemoryPressureEvaluatorFuchsiaTest, Basic) {
  base::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));

  TestSystemMemoryPressureEvaluator 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);
}

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

  MultiSourceMemoryPressureMonitor monitor;

  testing::StrictMock<TestSystemMemoryPressureEvaluator> evaluator(
      monitor.CreateVoter());

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

  base::MemoryPressureListener listener(
      FROM_HERE,
      base::BindRepeating(&TestSystemMemoryPressureEvaluator::OnMemoryPressure,
                          base::Unretained(&evaluator)));

  EXPECT_CALL(
      evaluator,
      OnMemoryPressure(
          base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE));
  SendPressureLevel(fuchsia::memorypressure::Level::WARNING);
  EXPECT_EQ(evaluator.current_vote(),
            base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE);
  testing::Mock::VerifyAndClearExpectations(&evaluator);

  // Verify that MODERATE pressure level is reported periodically.
  EXPECT_CALL(
      evaluator,
      OnMemoryPressure(
          base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE));
  task_environment_.FastForwardBy(evaluator.kRenotifyVotePeriod);
  testing::Mock::VerifyAndClearExpectations(&evaluator);

  EXPECT_CALL(
      evaluator,
      OnMemoryPressure(
          base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL));
  SendPressureLevel(fuchsia::memorypressure::Level::CRITICAL);
  EXPECT_EQ(evaluator.current_vote(),
            base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL);
  testing::Mock::VerifyAndClearExpectations(&evaluator);

  // Verify that CRITICAL pressure level is reported periodically.
  EXPECT_CALL(
      evaluator,
      OnMemoryPressure(
          base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL));
  task_environment_.FastForwardBy(evaluator.kRenotifyVotePeriod);
  testing::Mock::VerifyAndClearExpectations(&evaluator);

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

  // Verify that NONE pressure level is not reported periodically.
  task_environment_.FastForwardBy(evaluator.kRenotifyVotePeriod);
  testing::Mock::VerifyAndClearExpectations(&evaluator);
}

}  // namespace memory_pressure