blob: 70bc52287db23025fa1ee04fd7c5541d0c3593f4 [file] [log] [blame]
// Copyright 2022 The Chromium OS 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 <cryptohome/uss_experiment_config_fetcher.h>
#include <memory>
#include <string>
#include <utility>
#include <brillo/http/http_transport_fake.h>
#include <brillo/mime_utils.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <shill/dbus-proxy-mocks.h>
#include <shill/dbus-constants.h>
using ::testing::_;
using ::testing::DoAll;
using ::testing::Invoke;
using ::testing::Return;
using org::chromium::flimflam::ManagerProxyMock;
namespace cryptohome {
namespace {
constexpr char kGstaticUrlPrefix[] =
"https://www.gstatic.com/uss-experiment/v1.json";
constexpr char kDefaultConfig[] = R"(
{
"default": {
"last_invalid": 3,
"population": 0.3
},
"stable-channel": {
"last_invalid": 4,
"population": 0.01
},
"testimage-channel": {
"population": 1
}
}
)";
constexpr char kInvalidConfig[] = "not a json file";
// This is what the kConnectionState property will get set to for mocked
// calls into shill flimflam manager.
std::string* g_connection_state;
} // namespace
// Handles calls for getting the network state.
bool GetShillProperties(
brillo::VariantDictionary* dict,
brillo::ErrorPtr* error,
int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) {
dict->emplace(shill::kConnectionStateProperty, *g_connection_state);
return true;
}
class UssExperimentConfigFetcherTest : public ::testing::Test {
protected:
using FetchSuccessCallback =
base::RepeatingCallback<void(int last_invalid, double population)>;
void SetUp() override {
g_connection_state = &initial_connection_state_;
fake_transport_ = std::make_shared<brillo::http::fake::Transport>();
auto mock_proxy = std::make_unique<ManagerProxyMock>();
mock_proxy_ = mock_proxy.get();
fetcher_ = std::make_unique<UssExperimentConfigFetcher>();
fetcher_->SetTransportForTesting(fake_transport_);
fetcher_->SetProxyForTesting(std::move(mock_proxy));
}
void AddSimpleReplyHandler(int status_code, const std::string& reply_text) {
fake_transport_->AddSimpleReplyHandler(
kGstaticUrlPrefix, brillo::http::request_type::kGet, status_code,
reply_text, brillo::mime::application::kJson);
}
void OnManagerPropertyChangeRegistration() {
fetcher_->OnManagerPropertyChangeRegistration(/*interface=*/"",
/*signal_name=*/"",
/*success=*/true);
}
void OnConnectionStateChange(const std::string& state) {
fetcher_->OnManagerPropertyChange(shill::kConnectionStateProperty, state);
}
void SetConnectionState(std::string state) {
initial_connection_state_ = state;
}
void SetReleaseTrack(std::string track) {
fetcher_->SetReleaseTrackForTesting(track);
}
void FetchAndExpectSuccessWith(int expected_last_invalid,
double expected_population) {
bool called = false;
fetcher_->Fetch(base::BindRepeating(
[](bool* called, int expected_last_invalid, double expected_population,
int last_invalid, double population) {
*called = true;
EXPECT_EQ(expected_last_invalid, last_invalid);
EXPECT_DOUBLE_EQ(expected_population, population);
},
&called, expected_last_invalid, expected_population));
EXPECT_TRUE(called);
}
void FetchAndExpectError() {
bool called = false;
fetcher_->Fetch(
base::BindRepeating([](bool* called, int last_invalid,
double population) { *called = true; },
&called));
EXPECT_FALSE(called);
}
std::string initial_connection_state_;
std::unique_ptr<UssExperimentConfigFetcher> fetcher_;
std::shared_ptr<brillo::http::fake::Transport> fake_transport_;
ManagerProxyMock* mock_proxy_;
};
TEST_F(UssExperimentConfigFetcherTest, OnlineWhenFirstConnected) {
SetConnectionState("online");
EXPECT_CALL(*mock_proxy_, GetProperties(_, _, _))
.WillOnce(DoAll(Invoke(&GetShillProperties), Return(true)));
// We will test fetching logic in other test cases.
AddSimpleReplyHandler(brillo::http::status_code::NotFound, "");
// The fetcher should find out that the connection state is already "online"
// when registered. It will then fetch config on the server (but won't
// succeed).
OnManagerPropertyChangeRegistration();
EXPECT_EQ(fake_transport_->GetRequestCount(), 1);
}
TEST_F(UssExperimentConfigFetcherTest, OnlineAfterFirstConnected) {
SetConnectionState("idle");
EXPECT_CALL(*mock_proxy_, GetProperties(_, _, _))
.WillOnce(DoAll(Invoke(&GetShillProperties), Return(true)));
// The fetcher should find out that the connection state not "online" yet
// when registered, and wait for property change signals.
OnManagerPropertyChangeRegistration();
// Connection state changed to "connected", but not yet "online".
OnConnectionStateChange("connected");
// We will test fetching logic in other test cases.
AddSimpleReplyHandler(brillo::http::status_code::NotFound, "");
// After connection state changed to "online", the fetcher will fetch config
// on the server (but won't succeed).
OnConnectionStateChange("online");
EXPECT_EQ(fake_transport_->GetRequestCount(), 1);
}
TEST_F(UssExperimentConfigFetcherTest, FetchAndParseConfigSuccess) {
AddSimpleReplyHandler(brillo::http::status_code::Ok, kDefaultConfig);
SetReleaseTrack("stable-channel");
FetchAndExpectSuccessWith(4, 0.01);
SetReleaseTrack("testimage-channel");
FetchAndExpectSuccessWith(3, 1);
SetReleaseTrack("beta-channel");
FetchAndExpectSuccessWith(3, 0.3);
EXPECT_EQ(fake_transport_->GetRequestCount(), 3);
}
TEST_F(UssExperimentConfigFetcherTest, FetchAndParseConfigError) {
AddSimpleReplyHandler(brillo::http::status_code::Ok, kInvalidConfig);
SetReleaseTrack("stable-channel");
FetchAndExpectError();
EXPECT_EQ(fake_transport_->GetRequestCount(), 1);
}
} // namespace cryptohome