blob: 110e5bfdd1ba2f3035fbc002676786e748443cb5 [file] [log] [blame]
// Copyright 2019 The Chromium 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 "base/fuchsia/intl_profile_watcher.h"
#include <fuchsia/intl/cpp/fidl_test_base.h>
#include <lib/fidl/cpp/binding.h>
#include <memory>
#include <string>
#include <vector>
#include "base/check.h"
#include "base/memory/ptr_util.h"
#include "base/run_loop.h"
#include "base/test/bind_test_util.h"
#include "base/test/mock_callback.h"
#include "base/test/task_environment.h"
#include "base/threading/sequence_bound.h"
#include "base/threading/thread.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::fuchsia::intl::Profile;
namespace base {
namespace fuchsia {
namespace {
const char kPrimaryTimeZoneName[] = "Australia/Darwin";
const char kSecondaryTimeZoneName[] = "Africa/Djibouti";
void CopyIdsToTimeZoneIds(
const std::vector<std::string>& zone_ids,
std::vector<::fuchsia::intl::TimeZoneId>* time_zone_ids) {
time_zone_ids->clear();
for (auto id : zone_ids) {
::fuchsia::intl::TimeZoneId time_zone_id;
time_zone_id.id = id;
time_zone_ids->push_back(time_zone_id);
}
}
Profile CreateProfileWithTimeZones(const std::vector<std::string>& zone_ids) {
Profile profile;
std::vector<::fuchsia::intl::TimeZoneId> time_zone_ids;
CopyIdsToTimeZoneIds(zone_ids, &time_zone_ids);
profile.set_time_zones(time_zone_ids);
return profile;
}
// Partial fake implementation of a PropertyProvider.
class FakePropertyProvider
: public ::fuchsia::intl::testing::PropertyProvider_TestBase {
public:
explicit FakePropertyProvider(
fidl::InterfaceRequest<::fuchsia::intl::PropertyProvider>
provider_request)
: binding_(this) {
binding_.Bind(std::move(provider_request));
DCHECK(binding_.is_bound());
}
FakePropertyProvider(const FakePropertyProvider&) = delete;
FakePropertyProvider& operator=(const FakePropertyProvider&) = delete;
~FakePropertyProvider() override = default;
void Close() { binding_.Close(ZX_ERR_PEER_CLOSED); }
void SetTimeZones(const std::vector<std::string>& zone_ids) {
CopyIdsToTimeZoneIds(zone_ids, &time_zone_ids_);
}
void NotifyChange() { binding_.events().OnChange(); }
// PropertyProvider_TestBase implementation.
void GetProfile(
::fuchsia::intl::PropertyProvider::GetProfileCallback callback) override {
Profile profile;
profile.set_time_zones(time_zone_ids_);
callback(std::move(profile));
}
void NotImplemented_(const std::string& name) override {
ADD_FAILURE() << "Unimplemented function called: " << name;
}
private:
::fidl::Binding<::fuchsia::intl::PropertyProvider> binding_;
std::vector<::fuchsia::intl::TimeZoneId> time_zone_ids_;
};
class FakePropertyProviderAsync {
public:
explicit FakePropertyProviderAsync(
fidl::InterfaceRequest<::fuchsia::intl::PropertyProvider>
provider_request)
: thread_("Property Provider Thread") {
base::Thread::Options options(base::MessagePumpType::IO, 0);
CHECK(thread_.StartWithOptions(options));
property_provider_ = base::SequenceBound<FakePropertyProvider>(
thread_.task_runner(), std::move(provider_request));
}
FakePropertyProviderAsync(const FakePropertyProviderAsync&) = delete;
FakePropertyProviderAsync& operator=(const FakePropertyProviderAsync&) =
delete;
~FakePropertyProviderAsync() = default;
void Close() {
property_provider_.Post(FROM_HERE, &FakePropertyProvider::Close);
}
void SetTimeZones(const std::vector<std::string>& zone_ids) {
property_provider_.Post(FROM_HERE, &FakePropertyProvider::SetTimeZones,
zone_ids);
}
void NotifyChange() {
property_provider_.Post(FROM_HERE, &FakePropertyProvider::NotifyChange);
}
private:
base::Thread thread_;
base::SequenceBound<FakePropertyProvider> property_provider_;
};
} // namespace
class GetPrimaryTimeZoneIdFromPropertyProviderTest : public testing::Test {
public:
GetPrimaryTimeZoneIdFromPropertyProviderTest()
: property_provider_(property_provider_ptr_.NewRequest()) {}
GetPrimaryTimeZoneIdFromPropertyProviderTest(
const GetPrimaryTimeZoneIdFromPropertyProviderTest&) = delete;
GetPrimaryTimeZoneIdFromPropertyProviderTest& operator=(
const GetPrimaryTimeZoneIdFromPropertyProviderTest&) = delete;
~GetPrimaryTimeZoneIdFromPropertyProviderTest() override = default;
protected:
static std::string GetPrimaryTimeZoneIdFromPropertyProvider(
::fuchsia::intl::PropertyProviderSyncPtr property_provider) {
return IntlProfileWatcher::GetPrimaryTimeZoneIdFromPropertyProvider(
std::move(property_provider));
}
::fuchsia::intl::PropertyProviderSyncPtr property_provider_ptr_;
FakePropertyProviderAsync property_provider_;
};
class IntlProfileWatcherTest : public testing::Test {
public:
IntlProfileWatcherTest()
: property_provider_(property_provider_ptr_.NewRequest()) {}
IntlProfileWatcherTest(const IntlProfileWatcherTest&) = delete;
IntlProfileWatcherTest& operator=(const IntlProfileWatcherTest&) = delete;
~IntlProfileWatcherTest() override = default;
protected:
base::test::SingleThreadTaskEnvironment task_environment_{
base::test::SingleThreadTaskEnvironment::MainThreadType::IO};
std::unique_ptr<IntlProfileWatcher> CreateIntlProfileWatcher(
IntlProfileWatcher::ProfileChangeCallback on_profile_changed) {
return base::WrapUnique(new IntlProfileWatcher(
std::move(property_provider_ptr_), std::move(on_profile_changed)));
}
::fuchsia::intl::PropertyProviderPtr property_provider_ptr_;
FakePropertyProviderAsync property_provider_;
base::RunLoop run_loop_;
};
// Unit tests are run in an environment where intl is not provided.
// However, this is not exposed by the API.
TEST(IntlServiceNotAvailableTest, IntlProfileWatcher) {
base::test::SingleThreadTaskEnvironment task_environment_{
base::test::SingleThreadTaskEnvironment::MainThreadType::IO};
base::RunLoop run_loop;
base::MockCallback<IntlProfileWatcher::ProfileChangeCallback>
on_profile_changed;
EXPECT_CALL(on_profile_changed, Run(testing::_)).Times(0);
auto watcher = std::make_unique<IntlProfileWatcher>(on_profile_changed.Get());
EXPECT_TRUE(watcher);
run_loop.RunUntilIdle();
}
TEST_F(GetPrimaryTimeZoneIdFromPropertyProviderTest, RemoteNotBound) {
// Simulate the service not actually being available.
property_provider_.Close();
EXPECT_STREQ("", GetPrimaryTimeZoneIdFromPropertyProvider(
std::move(property_provider_ptr_))
.c_str());
}
TEST_F(GetPrimaryTimeZoneIdFromPropertyProviderTest, NoZones) {
EXPECT_STREQ("", GetPrimaryTimeZoneIdFromPropertyProvider(
std::move(property_provider_ptr_))
.c_str());
}
TEST_F(GetPrimaryTimeZoneIdFromPropertyProviderTest, SingleZone) {
property_provider_.SetTimeZones({kPrimaryTimeZoneName});
EXPECT_STREQ(kPrimaryTimeZoneName, GetPrimaryTimeZoneIdFromPropertyProvider(
std::move(property_provider_ptr_))
.c_str());
}
TEST_F(GetPrimaryTimeZoneIdFromPropertyProviderTest, SingleZoneIsEmpty) {
property_provider_.SetTimeZones({""});
EXPECT_STREQ("", GetPrimaryTimeZoneIdFromPropertyProvider(
std::move(property_provider_ptr_))
.c_str());
}
TEST_F(GetPrimaryTimeZoneIdFromPropertyProviderTest, MoreThanOneZone) {
property_provider_.SetTimeZones(
{kPrimaryTimeZoneName, kSecondaryTimeZoneName});
EXPECT_STREQ(kPrimaryTimeZoneName, GetPrimaryTimeZoneIdFromPropertyProvider(
std::move(property_provider_ptr_))
.c_str());
}
TEST_F(IntlProfileWatcherTest, NoZones_NoNotification) {
base::MockCallback<IntlProfileWatcher::ProfileChangeCallback> callback;
EXPECT_CALL(callback, Run(testing::_)).Times(0);
auto watcher = CreateIntlProfileWatcher(callback.Get());
run_loop_.RunUntilIdle();
}
TEST_F(IntlProfileWatcherTest, ChangeNotification_AfterInitialization) {
auto watcher = CreateIntlProfileWatcher(base::BindLambdaForTesting(
[quit_loop = run_loop_.QuitClosure()](const Profile& profile) {
EXPECT_EQ(kPrimaryTimeZoneName,
IntlProfileWatcher::GetPrimaryTimeZoneIdFromProfile(profile));
quit_loop.Run();
}));
property_provider_.SetTimeZones({kPrimaryTimeZoneName});
property_provider_.NotifyChange();
run_loop_.Run();
}
TEST_F(IntlProfileWatcherTest, ChangeNotification_BeforeInitialization) {
property_provider_.SetTimeZones({kPrimaryTimeZoneName});
property_provider_.NotifyChange();
auto watcher = CreateIntlProfileWatcher(base::BindLambdaForTesting(
[quit_loop = run_loop_.QuitClosure()](const Profile& profile) {
EXPECT_EQ(kPrimaryTimeZoneName,
IntlProfileWatcher::GetPrimaryTimeZoneIdFromProfile(profile));
quit_loop.Run();
}));
run_loop_.Run();
}
// Ensure no crash when the peer service cannot be reached during creation.
TEST_F(IntlProfileWatcherTest, ChannelClosedBeforeCreation) {
base::MockCallback<IntlProfileWatcher::ProfileChangeCallback> callback;
EXPECT_CALL(callback, Run(testing::_)).Times(0);
property_provider_.Close();
auto watcher = CreateIntlProfileWatcher(callback.Get());
property_provider_.NotifyChange();
run_loop_.RunUntilIdle();
}
// Ensure no crash when the channel is closed after creation.
TEST_F(IntlProfileWatcherTest, ChannelClosedAfterCreation) {
base::MockCallback<IntlProfileWatcher::ProfileChangeCallback> callback;
EXPECT_CALL(callback, Run(testing::_)).Times(0);
auto watcher = CreateIntlProfileWatcher(callback.Get());
property_provider_.Close();
property_provider_.NotifyChange();
run_loop_.RunUntilIdle();
}
TEST(IntlProfileWatcherGetPrimaryTimeZoneIdFromProfileTest, NoZones) {
EXPECT_EQ("", IntlProfileWatcher::GetPrimaryTimeZoneIdFromProfile(Profile()));
}
TEST(IntlProfileWatcherGetPrimaryTimeZoneIdFromProfileTest, EmptyZonesList) {
EXPECT_EQ("", IntlProfileWatcher::GetPrimaryTimeZoneIdFromProfile(
CreateProfileWithTimeZones({})));
}
TEST(IntlProfileWatcherGetPrimaryTimeZoneIdFromProfileTest, OneZone) {
EXPECT_EQ(kPrimaryTimeZoneName,
IntlProfileWatcher::GetPrimaryTimeZoneIdFromProfile(
CreateProfileWithTimeZones({kPrimaryTimeZoneName})));
}
TEST(IntlProfileWatcherGetPrimaryTimeZoneIdFromProfileTest, TwoZones) {
EXPECT_EQ(kPrimaryTimeZoneName,
IntlProfileWatcher::GetPrimaryTimeZoneIdFromProfile(
CreateProfileWithTimeZones(
{kPrimaryTimeZoneName, kSecondaryTimeZoneName})));
}
} // namespace fuchsia
} // namespace base