xref: /aosp_15_r20/external/cronet/base/fuchsia/intl_profile_watcher_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2019 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/fuchsia/intl_profile_watcher.h"
6 
7 #include <fuchsia/intl/cpp/fidl_test_base.h>
8 #include <lib/fidl/cpp/binding.h>
9 #include <memory>
10 #include <string>
11 #include <vector>
12 
13 #include "base/check.h"
14 #include "base/memory/ptr_util.h"
15 #include "base/run_loop.h"
16 #include "base/test/bind.h"
17 #include "base/test/mock_callback.h"
18 #include "base/test/task_environment.h"
19 #include "base/threading/sequence_bound.h"
20 #include "base/threading/thread.h"
21 #include "base/time/time.h"
22 #include "testing/gmock/include/gmock/gmock.h"
23 #include "testing/gtest/include/gtest/gtest.h"
24 
25 namespace base {
26 
27 namespace {
28 
29 const char kPrimaryTimeZoneName[] = "Australia/Darwin";
30 const char kSecondaryTimeZoneName[] = "Africa/Djibouti";
31 
32 const char kPrimaryLocaleName[] = "en-US";
33 const char kSecondaryLocaleName[] = "es-419";
34 
35 template <typename FuchsiaStruct>
CopyIdsToFuchsiaStruct(const std::vector<std::string> & raw_ids,std::vector<FuchsiaStruct> * fuchsia_ids)36 void CopyIdsToFuchsiaStruct(const std::vector<std::string>& raw_ids,
37                             std::vector<FuchsiaStruct>* fuchsia_ids) {
38   fuchsia_ids->clear();
39   for (auto id : raw_ids) {
40     FuchsiaStruct fuchsia_id;
41     fuchsia_id.id = id;
42     fuchsia_ids->push_back(fuchsia_id);
43   }
44 }
45 
CreateProfileWithTimeZones(const std::vector<std::string> & zone_ids)46 fuchsia::intl::Profile CreateProfileWithTimeZones(
47     const std::vector<std::string>& zone_ids) {
48   fuchsia::intl::Profile profile;
49   std::vector<::fuchsia::intl::TimeZoneId> time_zone_ids;
50   CopyIdsToFuchsiaStruct(zone_ids, &time_zone_ids);
51   profile.set_time_zones(time_zone_ids);
52   return profile;
53 }
54 
CreateProfileWithLocales(const std::vector<std::string> & locale_ids)55 fuchsia::intl::Profile CreateProfileWithLocales(
56     const std::vector<std::string>& locale_ids) {
57   fuchsia::intl::Profile profile;
58   std::vector<::fuchsia::intl::LocaleId> fuchsia_locale_ids;
59   CopyIdsToFuchsiaStruct(locale_ids, &fuchsia_locale_ids);
60   profile.set_locales(fuchsia_locale_ids);
61   return profile;
62 }
63 
64 // Partial fake implementation of a PropertyProvider.
65 class FakePropertyProvider
66     : public ::fuchsia::intl::testing::PropertyProvider_TestBase {
67  public:
FakePropertyProvider(fidl::InterfaceRequest<::fuchsia::intl::PropertyProvider> provider_request)68   explicit FakePropertyProvider(
69       fidl::InterfaceRequest<::fuchsia::intl::PropertyProvider>
70           provider_request)
71       : binding_(this) {
72     binding_.Bind(std::move(provider_request));
73     DCHECK(binding_.is_bound());
74   }
75   FakePropertyProvider(const FakePropertyProvider&) = delete;
76   FakePropertyProvider& operator=(const FakePropertyProvider&) = delete;
77   ~FakePropertyProvider() override = default;
78 
Close()79   void Close() { binding_.Close(ZX_ERR_PEER_CLOSED); }
SetTimeZones(const std::vector<std::string> & zone_ids)80   void SetTimeZones(const std::vector<std::string>& zone_ids) {
81     CopyIdsToFuchsiaStruct(zone_ids, &time_zone_ids_);
82   }
SetLocales(const std::vector<std::string> & locale_ids)83   void SetLocales(const std::vector<std::string>& locale_ids) {
84     CopyIdsToFuchsiaStruct(locale_ids, &fuchsia_locale_ids_);
85   }
NotifyChange()86   void NotifyChange() { binding_.events().OnChange(); }
87 
88   // PropertyProvider_TestBase implementation.
GetProfile(::fuchsia::intl::PropertyProvider::GetProfileCallback callback)89   void GetProfile(
90       ::fuchsia::intl::PropertyProvider::GetProfileCallback callback) override {
91     fuchsia::intl::Profile profile;
92     profile.set_time_zones(time_zone_ids_);
93     profile.set_locales(fuchsia_locale_ids_);
94     callback(std::move(profile));
95   }
NotImplemented_(const std::string & name)96   void NotImplemented_(const std::string& name) override {
97     ADD_FAILURE() << "Unimplemented function called: " << name;
98   }
99 
100  private:
101   ::fidl::Binding<::fuchsia::intl::PropertyProvider> binding_;
102 
103   std::vector<::fuchsia::intl::TimeZoneId> time_zone_ids_;
104   std::vector<::fuchsia::intl::LocaleId> fuchsia_locale_ids_;
105 };
106 
107 class FakePropertyProviderAsync {
108  public:
FakePropertyProviderAsync(fidl::InterfaceRequest<::fuchsia::intl::PropertyProvider> provider_request)109   explicit FakePropertyProviderAsync(
110       fidl::InterfaceRequest<::fuchsia::intl::PropertyProvider>
111           provider_request)
112       : thread_("Property Provider Thread") {
113     CHECK(thread_.StartWithOptions(
114         base::Thread::Options(base::MessagePumpType::IO, 0)));
115     property_provider_ = base::SequenceBound<FakePropertyProvider>(
116         thread_.task_runner(), std::move(provider_request));
117   }
118   FakePropertyProviderAsync(const FakePropertyProviderAsync&) = delete;
119   FakePropertyProviderAsync& operator=(const FakePropertyProviderAsync&) =
120       delete;
121   ~FakePropertyProviderAsync() = default;
122 
Close()123   void Close() { property_provider_.AsyncCall(&FakePropertyProvider::Close); }
SetTimeZones(const std::vector<std::string> & zone_ids)124   void SetTimeZones(const std::vector<std::string>& zone_ids) {
125     property_provider_.AsyncCall(&FakePropertyProvider::SetTimeZones)
126         .WithArgs(zone_ids);
127   }
SetLocales(const std::vector<std::string> & locale_ids)128   void SetLocales(const std::vector<std::string>& locale_ids) {
129     property_provider_.AsyncCall(&FakePropertyProvider::SetLocales)
130         .WithArgs(locale_ids);
131   }
NotifyChange()132   void NotifyChange() {
133     property_provider_.AsyncCall(&FakePropertyProvider::NotifyChange);
134   }
135 
136  private:
137   base::Thread thread_;
138   base::SequenceBound<FakePropertyProvider> property_provider_;
139 };
140 
141 }  // namespace
142 
143 class GetValuesFromIntlPropertyProviderTest : public testing::Test {
144  public:
GetValuesFromIntlPropertyProviderTest()145   GetValuesFromIntlPropertyProviderTest()
146       : property_provider_(property_provider_ptr_.NewRequest()) {}
147   GetValuesFromIntlPropertyProviderTest(
148       const GetValuesFromIntlPropertyProviderTest&) = delete;
149   GetValuesFromIntlPropertyProviderTest& operator=(
150       const GetValuesFromIntlPropertyProviderTest&) = delete;
151   ~GetValuesFromIntlPropertyProviderTest() override = default;
152 
153  protected:
GetPrimaryLocaleId()154   std::string GetPrimaryLocaleId() {
155     fuchsia::intl::Profile profile =
156         GetProfileFromPropertyProvider(std::move(property_provider_ptr_));
157     return FuchsiaIntlProfileWatcher::GetPrimaryLocaleIdFromProfile(profile);
158   }
159 
GetPrimaryTimeZoneId()160   std::string GetPrimaryTimeZoneId() {
161     fuchsia::intl::Profile profile =
162         GetProfileFromPropertyProvider(std::move(property_provider_ptr_));
163     return FuchsiaIntlProfileWatcher::GetPrimaryTimeZoneIdFromProfile(profile);
164   }
165 
GetProfileFromPropertyProvider(::fuchsia::intl::PropertyProviderSyncPtr property_provider)166   static fuchsia::intl::Profile GetProfileFromPropertyProvider(
167       ::fuchsia::intl::PropertyProviderSyncPtr property_provider) {
168     return FuchsiaIntlProfileWatcher::GetProfileFromPropertyProvider(
169         std::move(property_provider));
170   }
171 
172   ::fuchsia::intl::PropertyProviderSyncPtr property_provider_ptr_;
173   FakePropertyProviderAsync property_provider_;
174 };
175 
176 class IntlProfileWatcherTest : public testing::Test {
177  public:
IntlProfileWatcherTest()178   IntlProfileWatcherTest()
179       : property_provider_(property_provider_ptr_.NewRequest()) {}
180   IntlProfileWatcherTest(const IntlProfileWatcherTest&) = delete;
181   IntlProfileWatcherTest& operator=(const IntlProfileWatcherTest&) = delete;
182   ~IntlProfileWatcherTest() override = default;
183 
184  protected:
185   base::test::SingleThreadTaskEnvironment task_environment_{
186       base::test::SingleThreadTaskEnvironment::MainThreadType::IO};
187 
CreateIntlProfileWatcher(FuchsiaIntlProfileWatcher::ProfileChangeCallback on_profile_changed)188   std::unique_ptr<FuchsiaIntlProfileWatcher> CreateIntlProfileWatcher(
189       FuchsiaIntlProfileWatcher::ProfileChangeCallback on_profile_changed) {
190     return base::WrapUnique(new FuchsiaIntlProfileWatcher(
191         std::move(property_provider_ptr_), std::move(on_profile_changed)));
192   }
193 
194   ::fuchsia::intl::PropertyProviderPtr property_provider_ptr_;
195   FakePropertyProviderAsync property_provider_;
196 
197   base::RunLoop run_loop_;
198 };
199 
200 // Unit tests are run in an environment where intl is not provided.
201 // However, this is not exposed by the API.
TEST(IntlServiceNotAvailableTest,FuchsiaIntlProfileWatcher)202 TEST(IntlServiceNotAvailableTest, FuchsiaIntlProfileWatcher) {
203   base::test::SingleThreadTaskEnvironment task_environment_{
204       base::test::SingleThreadTaskEnvironment::MainThreadType::IO};
205   base::RunLoop run_loop;
206 
207   base::MockCallback<FuchsiaIntlProfileWatcher::ProfileChangeCallback>
208       on_profile_changed;
209   EXPECT_CALL(on_profile_changed, Run(testing::_)).Times(0);
210   auto watcher =
211       std::make_unique<FuchsiaIntlProfileWatcher>(on_profile_changed.Get());
212   EXPECT_TRUE(watcher);
213 
214   run_loop.RunUntilIdle();
215 }
216 
TEST_F(GetValuesFromIntlPropertyProviderTest,GetPrimaryTimeZoneId_RemoteNotBound)217 TEST_F(GetValuesFromIntlPropertyProviderTest,
218        GetPrimaryTimeZoneId_RemoteNotBound) {
219   // Simulate the service not actually being available.
220   property_provider_.Close();
221   EXPECT_STREQ("", GetPrimaryTimeZoneId().c_str());
222 }
223 
TEST_F(GetValuesFromIntlPropertyProviderTest,GetPrimaryTimeZoneId_NoZones)224 TEST_F(GetValuesFromIntlPropertyProviderTest, GetPrimaryTimeZoneId_NoZones) {
225   EXPECT_STREQ("", GetPrimaryTimeZoneId().c_str());
226 }
227 
TEST_F(GetValuesFromIntlPropertyProviderTest,GetPrimaryTimeZoneId_SingleZone)228 TEST_F(GetValuesFromIntlPropertyProviderTest, GetPrimaryTimeZoneId_SingleZone) {
229   property_provider_.SetTimeZones({kPrimaryTimeZoneName});
230   EXPECT_STREQ(kPrimaryTimeZoneName, GetPrimaryTimeZoneId().c_str());
231 }
232 
TEST_F(GetValuesFromIntlPropertyProviderTest,GetPrimaryTimeZoneId_SingleZoneIsEmpty)233 TEST_F(GetValuesFromIntlPropertyProviderTest,
234        GetPrimaryTimeZoneId_SingleZoneIsEmpty) {
235   property_provider_.SetTimeZones({""});
236   EXPECT_STREQ("", GetPrimaryTimeZoneId().c_str());
237 }
238 
TEST_F(GetValuesFromIntlPropertyProviderTest,GetPrimaryTimeZoneId_MoreThanOneZone)239 TEST_F(GetValuesFromIntlPropertyProviderTest,
240        GetPrimaryTimeZoneId_MoreThanOneZone) {
241   property_provider_.SetTimeZones(
242       {kPrimaryTimeZoneName, kSecondaryTimeZoneName});
243   EXPECT_STREQ(kPrimaryTimeZoneName, GetPrimaryTimeZoneId().c_str());
244 }
245 
TEST_F(GetValuesFromIntlPropertyProviderTest,GetPrimaryLocaleId_RemoteNotBound)246 TEST_F(GetValuesFromIntlPropertyProviderTest,
247        GetPrimaryLocaleId_RemoteNotBound) {
248   // Simulate the service not actually being available.
249   property_provider_.Close();
250   EXPECT_STREQ("", GetPrimaryLocaleId().c_str());
251 }
252 
TEST_F(GetValuesFromIntlPropertyProviderTest,GetPrimaryLocaleId_NoZones)253 TEST_F(GetValuesFromIntlPropertyProviderTest, GetPrimaryLocaleId_NoZones) {
254   EXPECT_STREQ("", GetPrimaryLocaleId().c_str());
255 }
256 
TEST_F(GetValuesFromIntlPropertyProviderTest,GetPrimaryLocaleId_SingleLocale)257 TEST_F(GetValuesFromIntlPropertyProviderTest, GetPrimaryLocaleId_SingleLocale) {
258   property_provider_.SetLocales({kPrimaryLocaleName});
259   EXPECT_STREQ(kPrimaryLocaleName, GetPrimaryLocaleId().c_str());
260 }
261 
TEST_F(GetValuesFromIntlPropertyProviderTest,GetPrimaryLocaleId_SingleLocaleIsEmpty)262 TEST_F(GetValuesFromIntlPropertyProviderTest,
263        GetPrimaryLocaleId_SingleLocaleIsEmpty) {
264   property_provider_.SetLocales({""});
265   EXPECT_STREQ("", GetPrimaryLocaleId().c_str());
266 }
267 
TEST_F(GetValuesFromIntlPropertyProviderTest,GetPrimaryLocaleId_MoreThanOneLocale)268 TEST_F(GetValuesFromIntlPropertyProviderTest,
269        GetPrimaryLocaleId_MoreThanOneLocale) {
270   property_provider_.SetLocales({kPrimaryLocaleName, kSecondaryLocaleName});
271   EXPECT_STREQ(kPrimaryLocaleName, GetPrimaryLocaleId().c_str());
272 }
273 
TEST_F(IntlProfileWatcherTest,NoZones_NoNotification)274 TEST_F(IntlProfileWatcherTest, NoZones_NoNotification) {
275   base::MockCallback<FuchsiaIntlProfileWatcher::ProfileChangeCallback> callback;
276   EXPECT_CALL(callback, Run(testing::_)).Times(0);
277   auto watcher = CreateIntlProfileWatcher(callback.Get());
278   run_loop_.RunUntilIdle();
279 }
280 
TEST_F(IntlProfileWatcherTest,ChangeNotification_AfterInitialization)281 TEST_F(IntlProfileWatcherTest, ChangeNotification_AfterInitialization) {
282   auto watcher = CreateIntlProfileWatcher(
283       base::BindLambdaForTesting([quit_loop = run_loop_.QuitClosure()](
284                                      const fuchsia::intl::Profile& profile) {
285         EXPECT_EQ(kPrimaryTimeZoneName,
286                   FuchsiaIntlProfileWatcher::GetPrimaryTimeZoneIdFromProfile(
287                       profile));
288         quit_loop.Run();
289       }));
290 
291   property_provider_.SetTimeZones({kPrimaryTimeZoneName});
292   property_provider_.NotifyChange();
293 
294   run_loop_.Run();
295 }
296 
TEST_F(IntlProfileWatcherTest,ChangeNotification_BeforeInitialization)297 TEST_F(IntlProfileWatcherTest, ChangeNotification_BeforeInitialization) {
298   property_provider_.SetTimeZones({kPrimaryTimeZoneName});
299   property_provider_.NotifyChange();
300 
301   auto watcher = CreateIntlProfileWatcher(
302       base::BindLambdaForTesting([quit_loop = run_loop_.QuitClosure()](
303                                      const fuchsia::intl::Profile& profile) {
304         EXPECT_EQ(kPrimaryTimeZoneName,
305                   FuchsiaIntlProfileWatcher::GetPrimaryTimeZoneIdFromProfile(
306                       profile));
307         quit_loop.Run();
308       }));
309 
310   run_loop_.Run();
311 }
312 
313 // Ensure no crash when the peer service cannot be reached during creation.
TEST_F(IntlProfileWatcherTest,ChannelClosedBeforeCreation)314 TEST_F(IntlProfileWatcherTest, ChannelClosedBeforeCreation) {
315   base::MockCallback<FuchsiaIntlProfileWatcher::ProfileChangeCallback> callback;
316   EXPECT_CALL(callback, Run(testing::_)).Times(0);
317 
318   property_provider_.Close();
319 
320   auto watcher = CreateIntlProfileWatcher(callback.Get());
321 
322   property_provider_.NotifyChange();
323   run_loop_.RunUntilIdle();
324 }
325 
326 // Ensure no crash when the channel is closed after creation.
TEST_F(IntlProfileWatcherTest,ChannelClosedAfterCreation)327 TEST_F(IntlProfileWatcherTest, ChannelClosedAfterCreation) {
328   base::MockCallback<FuchsiaIntlProfileWatcher::ProfileChangeCallback> callback;
329   EXPECT_CALL(callback, Run(testing::_)).Times(0);
330 
331   auto watcher = CreateIntlProfileWatcher(callback.Get());
332 
333   property_provider_.Close();
334 
335   property_provider_.NotifyChange();
336   run_loop_.RunUntilIdle();
337 }
338 
TEST(IntlProfileWatcherGetPrimaryTimeZoneIdFromProfileTest,NoZones)339 TEST(IntlProfileWatcherGetPrimaryTimeZoneIdFromProfileTest, NoZones) {
340   EXPECT_EQ("", FuchsiaIntlProfileWatcher::GetPrimaryTimeZoneIdFromProfile(
341                     fuchsia::intl::Profile()));
342 }
343 
TEST(IntlProfileWatcherGetPrimaryTimeZoneIdFromProfileTest,EmptyZonesList)344 TEST(IntlProfileWatcherGetPrimaryTimeZoneIdFromProfileTest, EmptyZonesList) {
345   EXPECT_EQ("", FuchsiaIntlProfileWatcher::GetPrimaryTimeZoneIdFromProfile(
346                     CreateProfileWithTimeZones({})));
347 }
348 
TEST(IntlProfileWatcherGetPrimaryTimeZoneIdFromProfileTest,OneZone)349 TEST(IntlProfileWatcherGetPrimaryTimeZoneIdFromProfileTest, OneZone) {
350   EXPECT_EQ(kPrimaryTimeZoneName,
351             FuchsiaIntlProfileWatcher::GetPrimaryTimeZoneIdFromProfile(
352                 CreateProfileWithTimeZones({kPrimaryTimeZoneName})));
353 }
354 
TEST(IntlProfileWatcherGetPrimaryTimeZoneIdFromProfileTest,TwoZones)355 TEST(IntlProfileWatcherGetPrimaryTimeZoneIdFromProfileTest, TwoZones) {
356   EXPECT_EQ(kPrimaryTimeZoneName,
357             FuchsiaIntlProfileWatcher::GetPrimaryTimeZoneIdFromProfile(
358                 CreateProfileWithTimeZones(
359                     {kPrimaryTimeZoneName, kSecondaryTimeZoneName})));
360 }
361 
TEST(IntlProfileWatcherGetPrimaryLocaleIdFromProfileTest,NoLocales)362 TEST(IntlProfileWatcherGetPrimaryLocaleIdFromProfileTest, NoLocales) {
363   EXPECT_EQ("", FuchsiaIntlProfileWatcher::GetPrimaryLocaleIdFromProfile(
364                     fuchsia::intl::Profile()));
365 }
366 
TEST(IntlProfileWatcherGetPrimaryLocaleIdFromProfileTest,EmptyLocalesList)367 TEST(IntlProfileWatcherGetPrimaryLocaleIdFromProfileTest, EmptyLocalesList) {
368   EXPECT_EQ("", FuchsiaIntlProfileWatcher::GetPrimaryLocaleIdFromProfile(
369                     CreateProfileWithLocales({})));
370 }
371 
TEST(IntlProfileWatcherGetPrimaryLocaleIdFromProfileTest,OneLocale)372 TEST(IntlProfileWatcherGetPrimaryLocaleIdFromProfileTest, OneLocale) {
373   EXPECT_EQ(kPrimaryLocaleName,
374             FuchsiaIntlProfileWatcher::GetPrimaryLocaleIdFromProfile(
375                 CreateProfileWithLocales({kPrimaryLocaleName})));
376 }
377 
TEST(IntlProfileWatcherGetPrimaryLocaleIdFromProfileTest,MultipleLocales)378 TEST(IntlProfileWatcherGetPrimaryLocaleIdFromProfileTest, MultipleLocales) {
379   EXPECT_EQ(kPrimaryLocaleName,
380             FuchsiaIntlProfileWatcher::GetPrimaryLocaleIdFromProfile(
381                 CreateProfileWithLocales(
382                     {kPrimaryLocaleName, kSecondaryLocaleName})));
383 }
384 
385 }  // namespace base
386