blob: d4def222ded68391d7f56b21207230d7f9f2f564 [file] [log] [blame]
// Copyright 2018 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 "encoder/shipping_dispatcher.h"
#include <deque>
#include <set>
#include <string>
#include <utility>
#include "./gtest.h"
#include "config/client_config.h"
#include "encoder/client_secret.h"
#include "encoder/encoder.h"
#include "encoder/project_context.h"
// Generated from shipping_dispatcher_test_config.yaml
#include "encoder/shipping_dispatcher_test_config.h"
namespace cobalt {
namespace encoder {
typedef ObservationMetadata::ShufflerBackend ShufflerBackend;
using config::ClientConfig;
using tensorflow_statusor::StatusOr;
namespace {
// These values must match the values specified in the invocation of
// generate_test_config_h() in CMakeLists.txt. and in the invocation of
// cobalt_config_header("generate_shipping_manager_test_config") in BUILD.gn.
const uint32_t kCustomerId = 1;
const uint32_t kProjectId = 1;
const size_t kNoOpEncodingByteOverhead = 30;
const size_t kMaxBytesPerObservation = 50;
const size_t kMaxBytesPerEnvelope = 200;
const size_t kMaxBytesTotal = 1000;
// Note(rudominer) Because kMinEnvelopeSendSize = 170 and
// kMaxBytesPerEnvelope = 200, and our tests use Observations of size
// 40 bytes, the worker thread will attempt to send Envelopes that contain
// exactly 5, 40-byte Observations. (4 * 40 < 170 and 6 * 40 > 200 ).
const size_t kMinEnvelopeSendSize = 170;
const std::chrono::seconds kMaxSeconds = ShippingManager::kMaxSeconds;
// Returns a ProjectContext obtained by parsing the configuration specified
// in shipping_manager_test_config.yaml
std::shared_ptr<ProjectContext> GetTestProject() {
// Parse the base64-encoded, serialized CobaltConfig in
// shipping_manager_test_config.h. This is generated from
// shipping_manager_test_config.yaml. Edit that yaml file to make changes. The
// variable name below, |cobalt_config_base64|, must match what is
// specified in the build files.
std::unique_ptr<ClientConfig> client_config =
ClientConfig::CreateFromCobaltConfigBase64(cobalt_config_base64);
EXPECT_NE(nullptr, client_config);
return std::shared_ptr<ProjectContext>(new ProjectContext(
kCustomerId, kProjectId,
std::shared_ptr<ClientConfig>(client_config.release())));
}
class FakeSystemData : public SystemDataInterface {
public:
FakeSystemData() {
system_profile_.set_os(SystemProfile::FUCHSIA);
system_profile_.set_arch(SystemProfile::ARM_64);
system_profile_.set_board_name("Fake Board Name");
}
const SystemProfile& system_profile() const override {
return system_profile_;
};
static void CheckSystemProfile(const Envelope& envelope) {
// SystemProfile is not placed in the envelope at this time.
EXPECT_EQ(SystemProfile::UNKNOWN_OS, envelope.system_profile().os());
EXPECT_EQ(SystemProfile::UNKNOWN_ARCH, envelope.system_profile().arch());
EXPECT_EQ("", envelope.system_profile().board_name());
}
private:
SystemProfile system_profile_;
};
class FakeHTTPClient : public clearcut::HTTPClient {
public:
explicit FakeHTTPClient(std::set<uint32_t>* seen_event_codes)
: clearcut::HTTPClient(), seen_event_codes_(seen_event_codes) {}
std::future<StatusOr<clearcut::HTTPResponse>> Post(
clearcut::HTTPRequest request,
std::chrono::steady_clock::time_point _ignored) {
clearcut::LogRequest req;
req.ParseFromString(request.body);
for (auto event : req.log_event()) {
seen_event_codes_->insert(event.event_code());
}
clearcut::HTTPResponse response = {.http_code = 200};
clearcut::LogResponse resp;
resp.SerializeToString(&response.response);
std::promise<StatusOr<clearcut::HTTPResponse>> response_promise;
response_promise.set_value(response);
return response_promise.get_future();
}
private:
std::set<uint32_t>* seen_event_codes_;
};
class FakeShippingManager : public ShippingManager {
public:
FakeShippingManager(std::chrono::seconds schedule_interval,
std::chrono::seconds min_interval)
: ShippingManager(
ShippingManager::SizeParams(kMaxBytesPerObservation,
kMaxBytesPerEnvelope, kMaxBytesTotal,
kMinEnvelopeSendSize),
ShippingManager::ScheduleParams(schedule_interval, min_interval),
ShippingManager::EnvelopeMakerParams("", EncryptedMessage::NONE, "",
EncryptedMessage::NONE)),
envelopes_sent_(0) {}
void SendEnvelopeToBackend(
std::unique_ptr<EnvelopeMaker> envelope_to_send,
std::deque<std::unique_ptr<EnvelopeMaker>>* envelopes_that_failed) {
std::lock_guard<std::mutex> lock(mutex_);
envelopes_sent_ += 1;
}
int32_t envelopes_sent() {
std::lock_guard<std::mutex> lock(mutex_);
return envelopes_sent_;
}
private:
std::mutex mutex_;
int32_t envelopes_sent_;
};
} // namespace
class ShippingDispatcherTest : public ::testing::Test {
public:
ShippingDispatcherTest()
: shipping_dispatcher_(new ShippingDispatcher()),
project_(GetTestProject()),
encoder_(project_, ClientSecret::GenerateNewSecret(), &system_data_) {}
protected:
void Init(std::chrono::seconds schedule_interval,
std::chrono::seconds min_interval) {
shipping_dispatcher_->Register(
ObservationMetadata::LEGACY_BACKEND,
std::make_unique<FakeShippingManager>(schedule_interval, min_interval));
shipping_dispatcher_->Register(
ObservationMetadata::V1_BACKEND,
std::make_unique<FakeShippingManager>(schedule_interval, min_interval));
shipping_dispatcher_->Start();
}
ShippingManager::Status AddObservation(size_t num_bytes, uint32_t metric_id) {
CHECK(num_bytes > kNoOpEncodingByteOverhead) << " num_bytes=" << num_bytes;
Encoder::Result result = encoder_.EncodeString(
metric_id, kNoOpEncodingId,
std::string("x", num_bytes - kNoOpEncodingByteOverhead));
return shipping_dispatcher_->AddObservation(*result.observation,
std::move(result.metadata));
}
FakeShippingManager* Manager(ShufflerBackend backend) {
return reinterpret_cast<FakeShippingManager*>(
shipping_dispatcher_->manager(backend).ConsumeValueOrDie());
}
FakeSystemData system_data_;
std::set<uint32_t> seen_clearcut_event_codes_;
std::unique_ptr<ShippingDispatcher> shipping_dispatcher_;
std::shared_ptr<ProjectContext> project_;
Encoder encoder_;
};
TEST_F(ShippingDispatcherTest, ConstructAndDestruct) {
Init(kMaxSeconds, kMaxSeconds);
}
TEST_F(ShippingDispatcherTest, SendObservationToDefault) {
Init(kMaxSeconds, std::chrono::seconds::zero());
EXPECT_EQ(ShippingManager::kOk, AddObservation(40, kDefaultMetricId));
shipping_dispatcher_->RequestSendSoon();
shipping_dispatcher_->WaitUntilIdle(kMaxSeconds);
EXPECT_EQ(1, Manager(ObservationMetadata::LEGACY_BACKEND)->envelopes_sent());
EXPECT_EQ(0, Manager(ObservationMetadata::V1_BACKEND)->envelopes_sent());
}
TEST_F(ShippingDispatcherTest, SendObservationToLegacy) {
Init(kMaxSeconds, std::chrono::seconds::zero());
EXPECT_EQ(ShippingManager::kOk, AddObservation(40, kLegacyMetricId));
shipping_dispatcher_->RequestSendSoon();
shipping_dispatcher_->WaitUntilIdle(kMaxSeconds);
EXPECT_EQ(1, Manager(ObservationMetadata::LEGACY_BACKEND)->envelopes_sent());
EXPECT_EQ(0, Manager(ObservationMetadata::V1_BACKEND)->envelopes_sent());
}
TEST_F(ShippingDispatcherTest, SendObservationToV1Backend) {
Init(kMaxSeconds, std::chrono::seconds::zero());
EXPECT_EQ(ShippingManager::kOk, AddObservation(40, kClearcutMetricId));
shipping_dispatcher_->RequestSendSoon();
shipping_dispatcher_->WaitUntilIdle(kMaxSeconds);
EXPECT_EQ(0, Manager(ObservationMetadata::LEGACY_BACKEND)->envelopes_sent());
EXPECT_EQ(1, Manager(ObservationMetadata::V1_BACKEND)->envelopes_sent());
}
TEST_F(ShippingDispatcherTest, DistributeObservationsProperly) {
Init(kMaxSeconds, std::chrono::seconds::zero());
EXPECT_EQ(ShippingManager::kOk, AddObservation(40, kDefaultMetricId));
shipping_dispatcher_->RequestSendSoon();
shipping_dispatcher_->WaitUntilIdle(kMaxSeconds);
EXPECT_EQ(ShippingManager::kOk, AddObservation(40, kLegacyMetricId));
shipping_dispatcher_->RequestSendSoon();
shipping_dispatcher_->WaitUntilIdle(kMaxSeconds);
EXPECT_EQ(ShippingManager::kOk, AddObservation(40, kClearcutMetricId));
shipping_dispatcher_->RequestSendSoon();
shipping_dispatcher_->WaitUntilIdle(kMaxSeconds);
EXPECT_EQ(2, Manager(ObservationMetadata::LEGACY_BACKEND)->envelopes_sent());
EXPECT_EQ(1, Manager(ObservationMetadata::V1_BACKEND)->envelopes_sent());
}
} // namespace encoder
} // namespace cobalt