| // 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_ |