ukm_recorder_impl_unittest.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. // Copyright 2018 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "components/ukm/ukm_recorder_impl.h"
  5. #include "base/bind.h"
  6. #include "base/metrics/metrics_hashes.h"
  7. #include "base/test/task_environment.h"
  8. #include "components/ukm/scheme_constants.h"
  9. #include "components/ukm/test_ukm_recorder.h"
  10. #include "components/ukm/ukm_recorder_observer.h"
  11. #include "services/metrics/public/cpp/ukm_builders.h"
  12. #include "services/metrics/public/cpp/ukm_entry_builder.h"
  13. #include "services/metrics/public/cpp/ukm_source.h"
  14. #include "services/metrics/public/cpp/ukm_source_id.h"
  15. #include "testing/gmock/include/gmock/gmock.h"
  16. #include "testing/gtest/include/gtest/gtest.h"
  17. #include "third_party/metrics_proto/ukm/report.pb.h"
  18. #include "url/gurl.h"
  19. namespace ukm {
  20. using TestEvent1 = builders::PageLoad;
  21. const uint64_t kTestEntryHash = 1234;
  22. const uint64_t kTestMetricsHash = 12345;
  23. const char kTestEntryName[] = "TestEntry";
  24. const char kTestMetrics[] = "TestMetrics";
  25. std::map<uint64_t, builders::EntryDecoder> CreateTestingDecodeMap() {
  26. return {
  27. {kTestEntryHash,
  28. {kTestEntryName,
  29. {
  30. {kTestMetricsHash, kTestMetrics},
  31. }}},
  32. };
  33. }
  34. // Helper class for testing UkmRecorderImpl observers.
  35. class TestUkmObserver : public UkmRecorderObserver {
  36. public:
  37. explicit TestUkmObserver(UkmRecorderImpl* ukm_recorder_impl) {
  38. base::flat_set<uint64_t> event_hashes = {kTestEntryHash};
  39. ukm_recorder_impl->AddUkmRecorderObserver(event_hashes, this);
  40. }
  41. ~TestUkmObserver() override = default;
  42. // UkmRecorderImpl::UkmRecorderObserver override.
  43. void OnEntryAdded(mojom::UkmEntryPtr entry) override {
  44. if (stop_waiting_)
  45. std::move(stop_waiting_).Run();
  46. ASSERT_EQ(entry->event_hash, ukm_entry_->event_hash);
  47. ASSERT_EQ(entry->source_id, ukm_entry_->source_id);
  48. ASSERT_EQ(entry->metrics[kTestMetricsHash],
  49. ukm_entry_->metrics[kTestMetricsHash]);
  50. }
  51. void OnUpdateSourceURL(SourceId source_id,
  52. const std::vector<GURL>& urls) override {
  53. if (stop_waiting_)
  54. std::move(stop_waiting_).Run();
  55. ASSERT_EQ(source_id_, source_id);
  56. ASSERT_EQ(urls_, urls);
  57. }
  58. void OnPurgeRecordingsWithUrlScheme(const std::string& url_scheme) override {
  59. if (stop_waiting_)
  60. std::move(stop_waiting_).Run();
  61. }
  62. void OnPurge() override {
  63. if (stop_waiting_)
  64. std::move(stop_waiting_).Run();
  65. }
  66. void OnUkmAllowedStateChanged(bool allow) override {
  67. if (stop_waiting_)
  68. std::move(stop_waiting_).Run();
  69. EXPECT_EQ(expected_allow_, allow);
  70. }
  71. void WaitOnUkmAllowedStateChanged(bool expected_allow) {
  72. expected_allow_ = expected_allow;
  73. WaitCallback();
  74. }
  75. void WaitAddEntryCallback(uint64_t event_hash, mojom::UkmEntryPtr ukm_entry) {
  76. ukm_entry_ = std::move(ukm_entry);
  77. WaitCallback();
  78. }
  79. void WaitUpdateSourceURLCallback(SourceId source_id,
  80. const std::vector<GURL>& urls) {
  81. source_id_ = source_id;
  82. urls_ = urls;
  83. WaitCallback();
  84. }
  85. void WaitCallback() {
  86. base::RunLoop run_loop;
  87. stop_waiting_ = run_loop.QuitClosure();
  88. run_loop.Run();
  89. }
  90. private:
  91. base::OnceClosure stop_waiting_;
  92. mojom::UkmEntryPtr ukm_entry_;
  93. SourceId source_id_;
  94. std::vector<GURL> urls_;
  95. bool expected_allow_ = false;
  96. };
  97. TEST(UkmRecorderImplTest, IsSampledIn) {
  98. UkmRecorderImpl impl;
  99. for (int i = 0; i < 100; ++i) {
  100. // These are constant regardless of the seed, source, and event.
  101. EXPECT_FALSE(impl.IsSampledIn(-i, i, 0));
  102. EXPECT_TRUE(impl.IsSampledIn(-i, i, 1));
  103. }
  104. // These depend on the source, event, and initial seed. There's no real
  105. // predictability here but should see roughly 50% true and 50% false with
  106. // no obvious correlation and the same for every run of the test.
  107. impl.SetSamplingSeedForTesting(123);
  108. EXPECT_FALSE(impl.IsSampledIn(1, 1, 2));
  109. EXPECT_TRUE(impl.IsSampledIn(1, 2, 2));
  110. EXPECT_FALSE(impl.IsSampledIn(2, 1, 2));
  111. EXPECT_TRUE(impl.IsSampledIn(2, 2, 2));
  112. EXPECT_TRUE(impl.IsSampledIn(3, 1, 2));
  113. EXPECT_FALSE(impl.IsSampledIn(3, 2, 2));
  114. EXPECT_FALSE(impl.IsSampledIn(4, 1, 2));
  115. EXPECT_TRUE(impl.IsSampledIn(4, 2, 2));
  116. impl.SetSamplingSeedForTesting(456);
  117. EXPECT_TRUE(impl.IsSampledIn(1, 1, 2));
  118. EXPECT_FALSE(impl.IsSampledIn(1, 2, 2));
  119. EXPECT_TRUE(impl.IsSampledIn(2, 1, 2));
  120. EXPECT_FALSE(impl.IsSampledIn(2, 2, 2));
  121. EXPECT_FALSE(impl.IsSampledIn(3, 1, 2));
  122. EXPECT_TRUE(impl.IsSampledIn(3, 2, 2));
  123. EXPECT_TRUE(impl.IsSampledIn(4, 1, 2));
  124. EXPECT_FALSE(impl.IsSampledIn(4, 2, 2));
  125. impl.SetSamplingSeedForTesting(789);
  126. EXPECT_TRUE(impl.IsSampledIn(1, 1, 2));
  127. EXPECT_FALSE(impl.IsSampledIn(1, 2, 2));
  128. EXPECT_TRUE(impl.IsSampledIn(2, 1, 2));
  129. EXPECT_FALSE(impl.IsSampledIn(2, 2, 2));
  130. EXPECT_FALSE(impl.IsSampledIn(3, 1, 2));
  131. EXPECT_TRUE(impl.IsSampledIn(3, 2, 2));
  132. EXPECT_TRUE(impl.IsSampledIn(4, 1, 2));
  133. EXPECT_FALSE(impl.IsSampledIn(4, 2, 2));
  134. // Load a configuration for more detailed testing.
  135. std::map<std::string, std::string> params = {
  136. {"y.a", "3"},
  137. {"y.b", "y.a"},
  138. {"y.c", "y.a"},
  139. };
  140. impl.LoadExperimentSamplingParams(params);
  141. EXPECT_LT(impl.default_sampling_rate_, 0);
  142. // Functions under test take hashes instead of strings.
  143. uint64_t hash_ya = base::HashMetricName("y.a");
  144. uint64_t hash_yb = base::HashMetricName("y.b");
  145. uint64_t hash_yc = base::HashMetricName("y.c");
  146. // Check that the parameters are active.
  147. EXPECT_TRUE(impl.IsSampledIn(11, hash_ya));
  148. EXPECT_TRUE(impl.IsSampledIn(22, hash_ya));
  149. EXPECT_FALSE(impl.IsSampledIn(33, hash_ya));
  150. EXPECT_FALSE(impl.IsSampledIn(44, hash_ya));
  151. EXPECT_FALSE(impl.IsSampledIn(55, hash_ya));
  152. // Check that sampled in/out is the same for all three.
  153. for (int source = 0; source < 100; ++source) {
  154. bool sampled_in = impl.IsSampledIn(source, hash_ya);
  155. EXPECT_EQ(sampled_in, impl.IsSampledIn(source, hash_yb));
  156. EXPECT_EQ(sampled_in, impl.IsSampledIn(source, hash_yc));
  157. }
  158. }
  159. TEST(UkmRecorderImplTest, PurgeExtensionRecordings) {
  160. TestUkmRecorder recorder;
  161. // Enable extension sync.
  162. recorder.SetIsWebstoreExtensionCallback(
  163. base::BindRepeating([](base::StringPiece) { return true; }));
  164. // Record some sources and events.
  165. SourceId id1 = ConvertToSourceId(1, SourceIdType::NAVIGATION_ID);
  166. recorder.UpdateSourceURL(id1, GURL("https://www.google.ca"));
  167. SourceId id2 = ConvertToSourceId(2, SourceIdType::NAVIGATION_ID);
  168. recorder.UpdateSourceURL(id2, GURL("chrome-extension://abc/manifest.json"));
  169. SourceId id3 = ConvertToSourceId(3, SourceIdType::NAVIGATION_ID);
  170. recorder.UpdateSourceURL(id3, GURL("http://www.wikipedia.org"));
  171. SourceId id4 = ConvertToSourceId(4, SourceIdType::NAVIGATION_ID);
  172. recorder.UpdateSourceURL(id4, GURL("chrome-extension://abc/index.html"));
  173. TestEvent1(id1).Record(&recorder);
  174. TestEvent1(id2).Record(&recorder);
  175. // All sources and events have been recorded.
  176. EXPECT_TRUE(recorder.extensions_enabled_);
  177. EXPECT_TRUE(recorder.recording_is_continuous_);
  178. EXPECT_EQ(4U, recorder.sources().size());
  179. EXPECT_EQ(2U, recorder.entries().size());
  180. recorder.PurgeRecordingsWithUrlScheme(kExtensionScheme);
  181. // Recorded sources of extension scheme and related events have been cleared.
  182. EXPECT_EQ(2U, recorder.sources().size());
  183. EXPECT_EQ(1U, recorder.sources().count(id1));
  184. EXPECT_EQ(0U, recorder.sources().count(id2));
  185. EXPECT_EQ(1U, recorder.sources().count(id3));
  186. EXPECT_EQ(0U, recorder.sources().count(id4));
  187. EXPECT_FALSE(recorder.recording_is_continuous_);
  188. EXPECT_EQ(1U, recorder.entries().size());
  189. EXPECT_EQ(id1, recorder.entries()[0]->source_id);
  190. // Recording is disabled for extensions, thus new extension URL will not be
  191. // recorded.
  192. recorder.EnableRecording(/* extensions = */ false);
  193. recorder.UpdateSourceURL(id4, GURL("chrome-extension://abc/index.html"));
  194. EXPECT_FALSE(recorder.extensions_enabled_);
  195. EXPECT_EQ(2U, recorder.sources().size());
  196. }
  197. TEST(UkmRecorderImplTest, WebApkSourceUrl) {
  198. base::test::TaskEnvironment env;
  199. ukm::TestAutoSetUkmRecorder test_ukm_recorder;
  200. GURL url("https://example_url.com/manifest.json");
  201. SourceId id =
  202. UkmRecorderImpl::GetSourceIdFromScopeImpl(url, SourceIdType::WEBAPK_ID);
  203. ASSERT_NE(kInvalidSourceId, id);
  204. const auto& sources = test_ukm_recorder.GetSources();
  205. ASSERT_EQ(1ul, sources.size());
  206. auto it = sources.find(id);
  207. ASSERT_NE(sources.end(), it);
  208. EXPECT_EQ(url, it->second->url());
  209. EXPECT_EQ(1u, it->second->urls().size());
  210. EXPECT_EQ(SourceIdType::WEBAPK_ID, GetSourceIdType(id));
  211. }
  212. TEST(UkmRecorderImplTest, PaymentAppScopeUrl) {
  213. base::test::TaskEnvironment env;
  214. ukm::TestAutoSetUkmRecorder test_ukm_recorder;
  215. GURL url("https://bobpay.com");
  216. SourceId id = UkmRecorderImpl::GetSourceIdFromScopeImpl(
  217. url, SourceIdType::PAYMENT_APP_ID);
  218. ASSERT_NE(kInvalidSourceId, id);
  219. const auto& sources = test_ukm_recorder.GetSources();
  220. ASSERT_EQ(1ul, sources.size());
  221. auto it = sources.find(id);
  222. ASSERT_NE(sources.end(), it);
  223. EXPECT_EQ(url, it->second->url());
  224. EXPECT_EQ(1u, it->second->urls().size());
  225. EXPECT_EQ(SourceIdType::PAYMENT_APP_ID, GetSourceIdType(id));
  226. }
  227. TEST(UkmRecorderImplTest, WebIdentityScopeUrl) {
  228. base::test::TaskEnvironment env;
  229. ukm::TestAutoSetUkmRecorder test_ukm_recorder;
  230. GURL url("https://idp.com");
  231. SourceId id = UkmRecorderImpl::GetSourceIdFromScopeImpl(
  232. url, SourceIdType::WEB_IDENTITY_ID);
  233. ASSERT_NE(kInvalidSourceId, id);
  234. const auto& sources = test_ukm_recorder.GetSources();
  235. ASSERT_EQ(1ul, sources.size());
  236. auto it = sources.find(id);
  237. ASSERT_NE(sources.end(), it);
  238. EXPECT_EQ(url, it->second->url());
  239. EXPECT_EQ(1u, it->second->urls().size());
  240. EXPECT_EQ(SourceIdType::WEB_IDENTITY_ID, GetSourceIdType(id));
  241. }
  242. // Tests that UkmRecorderObserver is notified on a new UKM entry.
  243. TEST(UkmRecorderImplTest, ObserverNotifiedOnNewEntry) {
  244. base::test::TaskEnvironment env;
  245. ukm::TestAutoSetUkmRecorder test_ukm_recorder;
  246. TestUkmObserver test_observer(&test_ukm_recorder);
  247. test_ukm_recorder.decode_map_ = CreateTestingDecodeMap();
  248. auto entry = mojom::UkmEntry::New();
  249. entry->event_hash = kTestEntryHash;
  250. entry->source_id = 345;
  251. entry->metrics[kTestMetricsHash] = 10;
  252. test_ukm_recorder.AddEntry(entry->Clone());
  253. test_observer.WaitAddEntryCallback(kTestEntryHash, std::move(entry));
  254. }
  255. // Tests that UkmRecorderObserver is notified on source URL updates.
  256. TEST(UkmRecorderImplTest, ObserverNotifiedOnSourceURLUpdate) {
  257. base::test::TaskEnvironment env;
  258. ukm::TestAutoSetUkmRecorder test_ukm_recorder;
  259. TestUkmObserver test_observer(&test_ukm_recorder);
  260. uint64_t source_id = 345;
  261. GURL url("http://abc.com");
  262. std::vector<GURL> urls;
  263. urls.emplace_back(url);
  264. test_ukm_recorder.UpdateSourceURL(source_id, url);
  265. test_observer.WaitUpdateSourceURLCallback(source_id, urls);
  266. }
  267. // Tests that UkmRecorderObserver is notified on purge.
  268. TEST(UkmRecorderImplTest, ObserverNotifiedOnPurge) {
  269. base::test::TaskEnvironment env;
  270. ukm::TestAutoSetUkmRecorder test_ukm_recorder;
  271. TestUkmObserver test_observer(&test_ukm_recorder);
  272. test_ukm_recorder.PurgeRecordingsWithUrlScheme(kExtensionScheme);
  273. test_observer.WaitCallback();
  274. test_ukm_recorder.Purge();
  275. test_observer.WaitCallback();
  276. }
  277. TEST(UkmRecorderImplTest, ObserverNotifiedOnUkmAllowedStateChanged) {
  278. base::test::TaskEnvironment env;
  279. ukm::TestAutoSetUkmRecorder test_ukm_recorder;
  280. TestUkmObserver test_observer(&test_ukm_recorder);
  281. test_ukm_recorder.OnUkmAllowedStateChanged(false);
  282. test_observer.WaitOnUkmAllowedStateChanged(false);
  283. test_ukm_recorder.OnUkmAllowedStateChanged(true);
  284. test_observer.WaitOnUkmAllowedStateChanged(true);
  285. }
  286. // Tests that adding and removing observers work as expected.
  287. TEST(UkmRecorderImplTest, AddRemoveObserver) {
  288. base::test::TaskEnvironment env;
  289. ukm::TestAutoSetUkmRecorder test_ukm_recorder;
  290. // Adding 3 observers, the first 2 oberserve the same event
  291. // while the last one observes a different event.
  292. UkmRecorderObserver obs1, obs2, obs3;
  293. base::flat_set<uint64_t> events1 = {123};
  294. test_ukm_recorder.AddUkmRecorderObserver(events1, &obs1);
  295. test_ukm_recorder.AddUkmRecorderObserver(events1, &obs2);
  296. base::flat_set<uint64_t> events2 = {345};
  297. test_ukm_recorder.AddUkmRecorderObserver(events2, &obs3);
  298. // Remove the first observer.
  299. test_ukm_recorder.RemoveUkmRecorderObserver(&obs1);
  300. {
  301. base::AutoLock auto_lock(test_ukm_recorder.lock_);
  302. ASSERT_FALSE(test_ukm_recorder.observers_.empty());
  303. // There are still 2 separate events being observed, each
  304. // has one observer now.
  305. ASSERT_NE(test_ukm_recorder.observers_.find(events1),
  306. test_ukm_recorder.observers_.end());
  307. ASSERT_NE(test_ukm_recorder.observers_.find(events2),
  308. test_ukm_recorder.observers_.end());
  309. }
  310. // Removing the 2nd observer.
  311. test_ukm_recorder.RemoveUkmRecorderObserver(&obs2);
  312. {
  313. base::AutoLock auto_lock(test_ukm_recorder.lock_);
  314. // Only the 2nd event is being observed now, the first
  315. // event should be removed from the observers map.
  316. ASSERT_EQ(test_ukm_recorder.observers_.find(events1),
  317. test_ukm_recorder.observers_.end());
  318. ASSERT_NE(test_ukm_recorder.observers_.find(events2),
  319. test_ukm_recorder.observers_.end());
  320. }
  321. // Removing the last observer should clear the observer map.
  322. test_ukm_recorder.RemoveUkmRecorderObserver(&obs3);
  323. {
  324. base::AutoLock auto_lock(test_ukm_recorder.lock_);
  325. ASSERT_TRUE(test_ukm_recorder.observers_.empty());
  326. }
  327. }
  328. } // namespace ukm