blob: 92960e5256217d4f7089cd36960e2e4561e5a5eb [file] [log] [blame]
// Copyright 2019 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 <fuchsia/cobalt/cpp/fidl_test_base.h>
#include <fuchsia/hardware/ram/metrics/cpp/fidl_test_base.h>
#include <lib/async/cpp/executor.h>
#include <lib/gtest/test_loop_fixture.h>
#include <lib/inspect/testing/cpp/inspect.h>
#include <lib/sys/cpp/testing/component_context_provider.h>
#include <future>
#include <gtest/gtest.h>
#include <src/cobalt/bin/testing/fake_logger.h>
#include "src/developer/memory/monitor/monitor.h"
namespace monitor {
namespace test {
using namespace fuchsia::memory;
using namespace memory;
using namespace monitor;
class MonitorFidlUnitTest : public gtest::TestLoopFixture {
protected:
MonitorFidlUnitTest()
: monitor_(std::make_unique<Monitor>(context_provider_.TakeContext(), fxl::CommandLine{},
dispatcher(), false, false, false)) {}
void TearDown() override {
monitor_.reset();
TestLoopFixture::TearDown();
}
MonitorPtr monitor() {
MonitorPtr monitor;
context_provider_.ConnectToPublicService(monitor.NewRequest());
return monitor;
}
private:
sys::testing::ComponentContextProvider context_provider_;
std::unique_ptr<Monitor> monitor_;
};
class WatcherForTest : public fuchsia::memory::Watcher {
public:
WatcherForTest(fit::function<void(uint64_t free_bytes)> on_change)
: on_change_(std::move(on_change)) {}
void OnChange(Stats stats) override { on_change_(stats.free_bytes); }
void AddBinding(fidl::InterfaceRequest<Watcher> request) {
bindings_.AddBinding(this, std::move(request));
}
private:
fidl::BindingSet<Watcher> bindings_;
fit::function<void(uint64_t free_bytes)> on_change_;
};
TEST_F(MonitorFidlUnitTest, FreeBytes) {
bool got_free = false;
WatcherForTest watcher([&got_free](uint64_t free_bytes) { got_free = true; });
WatcherPtr watcher_ptr;
watcher.AddBinding(watcher_ptr.NewRequest());
monitor()->Watch(watcher_ptr.Unbind());
RunLoopUntilIdle();
EXPECT_TRUE(got_free);
}
class FakeRamDevice : public fuchsia::hardware::ram::metrics::testing::Device_TestBase {
public:
FakeRamDevice() = default;
fidl::InterfaceRequestHandler<fuchsia::hardware::ram::metrics::Device> GetHandler(
async_dispatcher_t* dispatcher = nullptr) {
return [this,
dispatcher](fidl::InterfaceRequest<fuchsia::hardware::ram::metrics::Device> request) {
binding_.Bind(std::move(request), dispatcher);
};
}
void MeasureBandwidth(fuchsia::hardware::ram::metrics::BandwidthMeasurementConfig config,
MeasureBandwidthCallback completer) override {
auto mul = config.cycles_to_measure / 1024;
fuchsia::hardware::ram::metrics::BandwidthInfo info = {};
info.timestamp = zx::msec(1234).to_nsecs();
info.frequency = 256 * 1024 * 1024;
info.bytes_per_cycle = 1;
info.channels[0].readwrite_cycles = 10 * mul;
info.channels[1].readwrite_cycles = 20 * mul;
info.channels[2].readwrite_cycles = 30 * mul;
info.channels[3].readwrite_cycles = 40 * mul;
info.channels[4].readwrite_cycles = 50 * mul;
info.channels[5].readwrite_cycles = 60 * mul;
info.channels[6].readwrite_cycles = 70 * mul;
info.channels[7].readwrite_cycles = 80 * mul;
fuchsia::hardware::ram::metrics::Device_MeasureBandwidth_Response response(info);
auto result = fuchsia::hardware::ram::metrics::Device_MeasureBandwidth_Result::WithResponse(
std::move(response));
completer(std::move(result));
}
void NotImplemented_(const std::string& name) override { FAIL() << name; }
private:
fidl::Binding<fuchsia::hardware::ram::metrics::Device> binding_{this};
};
class MockLogger : public ::fuchsia::cobalt::testing::Logger_TestBase {
public:
void LogCobaltEvents(std::vector<fuchsia::cobalt::CobaltEvent> events,
LogCobaltEventsCallback callback) override {
num_calls_++;
num_events_ += events.size();
callback(fuchsia::cobalt::Status::OK);
}
void LogEvent(uint32_t metric_id, uint32_t event_code,
LogCobaltEventsCallback callback) override {
num_calls_++;
num_events_ += 1;
callback(fuchsia::cobalt::Status::OK);
}
void NotImplemented_(const std::string& name) override {
ASSERT_TRUE(false) << name << " is not implemented";
}
int num_calls() { return num_calls_; }
int num_events() { return num_events_; }
private:
int num_calls_ = 0;
int num_events_ = 0;
};
class MockLoggerFactory : public ::fuchsia::cobalt::testing::LoggerFactory_TestBase {
public:
MockLogger* logger() { return logger_.get(); }
uint32_t received_project_id() { return received_project_id_; }
void CreateLoggerFromProjectId(uint32_t project_id,
::fidl::InterfaceRequest<fuchsia::cobalt::Logger> logger,
CreateLoggerFromProjectIdCallback callback) override {
received_project_id_ = project_id;
logger_.reset(new MockLogger());
logger_bindings_.AddBinding(logger_.get(), std::move(logger));
callback(fuchsia::cobalt::Status::OK);
}
void NotImplemented_(const std::string& name) override {
ASSERT_TRUE(false) << name << " is not implemented";
}
private:
uint32_t received_project_id_;
std::unique_ptr<MockLogger> logger_;
fidl::BindingSet<fuchsia::cobalt::Logger> logger_bindings_;
};
class MemoryBandwidthInspectTest : public gtest::TestLoopFixture {
public:
MemoryBandwidthInspectTest()
: monitor_(std::make_unique<Monitor>(context_provider_.TakeContext(), fxl::CommandLine{},
dispatcher(), false, false, false)),
executor_(dispatcher()),
ram_binding_(&fake_device_),
logger_factory_(new MockLoggerFactory()) {
// Create metrics
auto service_provider = context_provider_.service_directory_provider();
service_provider->AddService(factory_bindings_.GetHandler(logger_factory_.get(), dispatcher()));
CreateMetrics();
// Set RamDevice
fuchsia::hardware::ram::metrics::DevicePtr ram_device;
ram_binding_.Bind(ram_device.NewRequest());
monitor_->SetRamDevice(std::move(ram_device));
}
void RunPromiseToCompletion(fit::promise<> promise) {
bool done = false;
executor_.schedule_task(std::move(promise).and_then([&]() { done = true; }));
RunLoopUntilIdle();
ASSERT_TRUE(done);
}
fit::result<inspect::Hierarchy> GetHierachyFromInspect() {
fit::result<inspect::Hierarchy> hierarchy;
RunPromiseToCompletion(
inspect::ReadFromInspector(Inspector()).then([&](fit::result<inspect::Hierarchy>& result) {
hierarchy = std::move(result);
}));
return hierarchy;
}
inspect::Inspector Inspector() { return *(monitor_->inspector_.inspector()); }
private:
void CreateMetrics() {
// The Monitor will make asynchronous calls to the MockLogger*s that are also running in this
// class/tests thread. So the call to the Monitor needs to be made on a different thread, such
// that the MockLogger*s running on the main thread can respond to those calls.
std::future<void /*fuchsia::cobalt::Logger_Sync**/> result = std::async([this]() {
monitor_->CreateMetrics();
});
while (result.wait_for(std::chrono::milliseconds(1)) != std::future_status::ready) {
// Run the main thread's loop, allowing the MockLogger* objects to respond to requests.
RunLoopUntilIdle();
}
return result.get();
}
sys::testing::ComponentContextProvider context_provider_;
std::unique_ptr<Monitor> monitor_;
async::Executor executor_;
FakeRamDevice fake_device_;
fidl::Binding<fuchsia::hardware::ram::metrics::Device> ram_binding_;
std::unique_ptr<MockLoggerFactory> logger_factory_;
fidl::BindingSet<fuchsia::cobalt::LoggerFactory> factory_bindings_;
};
TEST_F(MemoryBandwidthInspectTest, MemoryBandwidth) {
RunLoopUntilIdle();
fit::result<inspect::Hierarchy> result = GetHierachyFromInspect();
ASSERT_TRUE(result.is_ok());
auto hierarchy = result.take_value();
auto* metric_node = hierarchy.GetByPath({Metrics::kInspectPlatformNodeName});
ASSERT_TRUE(metric_node);
auto* metric_memory = metric_node->GetByPath({Metrics::kMemoryBandwidthNodeName});
ASSERT_TRUE(metric_memory);
auto* readings = metric_memory->node().get_property<inspect::UintArrayValue>(Metrics::kReadings);
ASSERT_TRUE(readings);
EXPECT_EQ(Metrics::kMemoryBandwidthArraySize, readings->value().size());
EXPECT_EQ(94369704u, readings->value()[0]);
auto* timestamp = metric_memory->node().get_property<inspect::IntPropertyValue>(
Metrics::kReadingMemoryTimestamp);
ASSERT_TRUE(timestamp);
}
} // namespace test
} // namespace monitor