blob: bc48dcff74895b8904487dba0223ee2d71844e91 [file] [log] [blame] [edit]
// 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.
#ifndef SRC_DEVELOPER_FEEDBACK_TESTING_FAKES_COBALT_H_
#define SRC_DEVELOPER_FEEDBACK_TESTING_FAKES_COBALT_H_
#include <fuchsia/cobalt/cpp/fidl.h>
#include <fuchsia/cobalt/test/cpp/fidl.h>
#include <lib/sys/cpp/service_directory.h>
#include <memory>
#include "src/developer/feedback/utils/cobalt_metrics.h"
#include "src/lib/syslog/cpp/logger.h"
namespace feedback {
namespace fakes {
// A wrapper for getting events from a mock_cobalt component in integration tests.
class Cobalt {
public:
Cobalt(std::shared_ptr<sys::ServiceDirectory> environment_services);
~Cobalt();
template <typename EventCodeType>
std::vector<EventCodeType> GetAllEventsOfType(size_t num_expected,
fuchsia::cobalt::test::LogMethod log_method);
private:
template <typename EventCodeType>
void GetNewEventsOfType(const fuchsia::cobalt::test::LoggerQuerier_WatchLogs_Result& result,
std::vector<EventCodeType>* all_events);
fuchsia::cobalt::test::LoggerQuerierSyncPtr logger_querier_;
};
Cobalt::Cobalt(std::shared_ptr<sys::ServiceDirectory> environment_services) {
environment_services->Connect(logger_querier_.NewRequest());
}
Cobalt::~Cobalt() {
using fuchsia::cobalt::test::LogMethod;
FX_CHECK(logger_querier_) << "logger_querier_ disconnected. Cannot reset mock_cobalt, aborting";
// Reset the logger so tests can be run repeatedly.
FX_CHECK(logger_querier_->ResetLogger(kProjectId, LogMethod::LOG_EVENT) == ZX_OK)
<< "Failed to reset EVENT events, aborting";
FX_CHECK(logger_querier_->ResetLogger(kProjectId, LogMethod::LOG_EVENT_COUNT) == ZX_OK)
<< "Failed to reset EVENT_COUNT events, aborting";
FX_CHECK(logger_querier_->ResetLogger(kProjectId, LogMethod::LOG_ELAPSED_TIME) == ZX_OK)
<< "Failed to reset ELAPSED_TIME events, aborting";
FX_CHECK(logger_querier_->ResetLogger(kProjectId, LogMethod::LOG_FRAME_RATE) == ZX_OK)
<< "Failed to reset FRAME_RATE events, aborting";
FX_CHECK(logger_querier_->ResetLogger(kProjectId, LogMethod::LOG_MEMORY_USAGE) == ZX_OK)
<< "Failed to reset MEMORY_USAGE events, aborting";
FX_CHECK(logger_querier_->ResetLogger(kProjectId, LogMethod::LOG_INT_HISTOGRAM) == ZX_OK)
<< "Failed to reset INT_HISTOGRAM events, aborting";
FX_CHECK(logger_querier_->ResetLogger(kProjectId, LogMethod::LOG_COBALT_EVENT) == ZX_OK)
<< "Failed to reset COBALT_EVENT events, aborting";
FX_CHECK(logger_querier_->ResetLogger(kProjectId, LogMethod::LOG_COBALT_EVENTS) == ZX_OK)
<< "Failed to reset COBALT_EVENTS events, aborting";
}
template <typename EventCodeType>
std::vector<EventCodeType> Cobalt::GetAllEventsOfType(size_t num_expected,
fuchsia::cobalt::test::LogMethod log_method) {
fuchsia::cobalt::test::LoggerQuerier_WatchLogs_Result result;
std::vector<EventCodeType> all_events;
// We may need to run WatchLogs() multiple times to collect all of the events generated by
// our component. This is due to the fact that we are communicating with both the
// fuchsia.cobalt/Logger and fuchsia.cobalt.test/LoggerQuerier APIs and are provided no
// guarantees regarding the order in which messages are received. Thus it's conceivable (and
// will actually happen quite often) that the call to WatchLogs() (and maybe even ResetLogger())
// will get to the component serving both APIs before either of the calls to LogEvent() arrive
// and a response containing zero or one Cobalt events is received. So, if you wish to remove
// this for loop it is a prerequisite that you have figured out a way to guarantee the
// ordering of independent, asynchronous messages, made it so that you component only ever
// logs to Cobalt, or don't care about flakes in your tests.
//
// We can set an upper bound on the number of calls to the LoggerQuerier since calls to
// WatchLogs() after the first block if until new events are received. If we assume we send N
// cobalt events, in the worst case WatchLogs() is called before any events are received,
// returning nothing. Then to get the rest of the N sent events we must make at most N calls to
// WatchLogs() since we're guaranteed that each call will return with at least one new event.
for (size_t i = 0; i < num_expected + 1 && all_events.size() < num_expected; ++i) {
FX_CHECK(logger_querier_->WatchLogs(kProjectId, log_method, &result) == ZX_OK);
GetNewEventsOfType<EventCodeType>(result, &all_events);
}
return all_events;
}
template <typename EventCodeType>
void Cobalt::GetNewEventsOfType(const fuchsia::cobalt::test::LoggerQuerier_WatchLogs_Result& result,
std::vector<EventCodeType>* all_events) {
FX_CHECK(result.is_response());
const auto& response = result.response();
for (const auto& event : response.events) {
FX_CHECK(event.metric_id == MetricIDForEventCode(static_cast<EventCodeType>(0)))
<< "Expected metric id: "
<< std::to_string(MetricIDForEventCode(static_cast<EventCodeType>(0))) << "\n"
<< "Actual metic id: " << std::to_string(event.metric_id);
for (const auto& event_code : event.event_codes) {
all_events->push_back(static_cast<EventCodeType>(event_code));
}
}
}
} // namespace fakes
} // namespace feedback
#endif // SRC_DEVELOPER_FEEDBACK_TESTING_FAKES_COBALT_H_