sync_session_durations_metrics_recorder_unittest.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. // Copyright 2019 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/sync/driver/sync_session_durations_metrics_recorder.h"
  5. #include <memory>
  6. #include <string>
  7. #include <vector>
  8. #include "base/test/metrics/histogram_tester.h"
  9. #include "base/test/task_environment.h"
  10. #include "base/timer/timer.h"
  11. #include "components/signin/public/identity_manager/identity_test_environment.h"
  12. #include "components/sync/driver/test_sync_service.h"
  13. #include "services/network/test/test_url_loader_factory.h"
  14. #include "testing/gtest/include/gtest/gtest.h"
  15. namespace syncer {
  16. namespace {
  17. constexpr base::TimeDelta kSessionTime = base::Seconds(10);
  18. class SyncSessionDurationsMetricsRecorderTest : public testing::Test {
  19. public:
  20. SyncSessionDurationsMetricsRecorderTest()
  21. : identity_test_env_(&test_url_loader_factory_) {
  22. sync_service_.SetHasSyncConsent(false);
  23. sync_service_.SetDisableReasons(SyncService::DISABLE_REASON_NOT_SIGNED_IN);
  24. }
  25. SyncSessionDurationsMetricsRecorderTest(
  26. const SyncSessionDurationsMetricsRecorderTest&) = delete;
  27. SyncSessionDurationsMetricsRecorderTest& operator=(
  28. const SyncSessionDurationsMetricsRecorderTest&) = delete;
  29. ~SyncSessionDurationsMetricsRecorderTest() override = default;
  30. void EnableSync() {
  31. identity_test_env_.MakePrimaryAccountAvailable("foo@gmail.com",
  32. signin::ConsentLevel::kSync);
  33. sync_service_.SetHasSyncConsent(true);
  34. sync_service_.SetDisableReasons(SyncService::DisableReasonSet());
  35. sync_service_.FireStateChanged();
  36. }
  37. void SetInvalidCredentialsAuthError() {
  38. GoogleServiceAuthError auth_error(
  39. GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
  40. identity_test_env_.UpdatePersistentErrorOfRefreshTokenForAccount(
  41. identity_test_env_.identity_manager()->GetPrimaryAccountId(
  42. signin::ConsentLevel::kSync),
  43. auth_error);
  44. sync_service_.SetAuthError(auth_error);
  45. sync_service_.SetTransportState(SyncService::TransportState::PAUSED);
  46. }
  47. void ClearAuthError() {
  48. identity_test_env_.SetRefreshTokenForPrimaryAccount();
  49. sync_service_.SetAuthError(GoogleServiceAuthError::AuthErrorNone());
  50. sync_service_.SetTransportState(SyncService::TransportState::ACTIVE);
  51. }
  52. std::string GetSessionHistogramName(const std::string& histogram_suffix) {
  53. return std::string("Session.TotalDuration.") + histogram_suffix;
  54. }
  55. void ExpectOneSessionWithDuration(
  56. const base::HistogramTester& ht,
  57. const std::vector<std::string>& histogram_suffixes,
  58. const base::TimeDelta& expected_session_time) {
  59. for (const std::string& histogram_suffix : histogram_suffixes) {
  60. ht.ExpectTimeBucketCount(GetSessionHistogramName(histogram_suffix),
  61. expected_session_time, 1);
  62. }
  63. }
  64. void ExpectOneSession(const base::HistogramTester& ht,
  65. const std::vector<std::string>& histogram_suffixes) {
  66. for (const std::string& histogram_suffix : histogram_suffixes) {
  67. ht.ExpectTotalCount(GetSessionHistogramName(histogram_suffix), 1);
  68. }
  69. }
  70. void ExpectNoSession(const base::HistogramTester& ht,
  71. const std::vector<std::string>& histogram_suffixes) {
  72. for (const std::string& histogram_suffix : histogram_suffixes) {
  73. ht.ExpectTotalCount(GetSessionHistogramName(histogram_suffix), 0);
  74. }
  75. }
  76. void StartAndEndSession(const base::TimeDelta& session_time) {
  77. SyncSessionDurationsMetricsRecorder metrics_recorder(
  78. &sync_service_, identity_test_env_.identity_manager());
  79. metrics_recorder.OnSessionStarted(base::TimeTicks::Now());
  80. metrics_recorder.OnSessionEnded(session_time);
  81. }
  82. protected:
  83. base::test::SingleThreadTaskEnvironment task_environment_;
  84. network::TestURLLoaderFactory test_url_loader_factory_;
  85. signin::IdentityTestEnvironment identity_test_env_;
  86. TestSyncService sync_service_;
  87. };
  88. TEST_F(SyncSessionDurationsMetricsRecorderTest, WebSignedOut) {
  89. base::HistogramTester ht;
  90. StartAndEndSession(kSessionTime);
  91. ExpectOneSessionWithDuration(ht, {"WithoutAccount"}, kSessionTime);
  92. ExpectNoSession(ht, {"WithAccount"});
  93. }
  94. TEST_F(SyncSessionDurationsMetricsRecorderTest, WebSignedIn) {
  95. identity_test_env_.SetCookieAccounts({{"foo@gmail.com", "foo_gaia_id"}});
  96. base::HistogramTester ht;
  97. StartAndEndSession(kSessionTime);
  98. ExpectOneSessionWithDuration(ht, {"WithAccount"}, kSessionTime);
  99. ExpectNoSession(ht, {"WithoutAccount"});
  100. }
  101. TEST_F(SyncSessionDurationsMetricsRecorderTest, NotOptedInToSync) {
  102. base::HistogramTester ht;
  103. StartAndEndSession(kSessionTime);
  104. ExpectOneSessionWithDuration(ht, {"NotOptedInToSyncWithoutAccount"},
  105. kSessionTime);
  106. ExpectNoSession(ht,
  107. {"NotOptedInToSyncWithAccount", "OptedInToSyncWithoutAccount",
  108. "OptedInToSyncWithAccount"});
  109. }
  110. TEST_F(SyncSessionDurationsMetricsRecorderTest, OptedInToSync_SyncActive) {
  111. EnableSync();
  112. base::HistogramTester ht;
  113. StartAndEndSession(kSessionTime);
  114. ExpectOneSessionWithDuration(ht, {"OptedInToSyncWithAccount"}, kSessionTime);
  115. ExpectNoSession(
  116. ht, {"NotOptedInToSyncWithoutAccount", "NotOptedInToSyncWithoutAccount",
  117. "OptedInToSyncWithoutAccount"});
  118. }
  119. TEST_F(SyncSessionDurationsMetricsRecorderTest,
  120. OptedInToSync_SyncDisabledByUser) {
  121. EnableSync();
  122. sync_service_.SetDisableReasons(SyncService::DISABLE_REASON_USER_CHOICE);
  123. base::HistogramTester ht;
  124. StartAndEndSession(kSessionTime);
  125. // If the user opted in to sync, but then disabled sync (e.g. via policy or
  126. // from the Android OS settings), then they are counted as having opted out
  127. // of sync.
  128. ExpectOneSessionWithDuration(ht, {"NotOptedInToSyncWithAccount"},
  129. kSessionTime);
  130. ExpectNoSession(
  131. ht, {"NotOptedInToSyncWithoutAccount", "OptedInToSyncWithoutAccount",
  132. "OptedInToSyncWithAccount"});
  133. }
  134. TEST_F(SyncSessionDurationsMetricsRecorderTest,
  135. OptedInToSync_PrimaryAccountInAuthError) {
  136. EnableSync();
  137. SetInvalidCredentialsAuthError();
  138. base::HistogramTester ht;
  139. StartAndEndSession(kSessionTime);
  140. ExpectOneSessionWithDuration(ht, {"OptedInToSyncWithoutAccount"},
  141. kSessionTime);
  142. ExpectNoSession(
  143. ht, {"NotOptedInToSyncWithoutAccount", "NotOptedInToSyncWithoutAccount",
  144. "OptedInToSyncWithAccount"});
  145. }
  146. TEST_F(SyncSessionDurationsMetricsRecorderTest,
  147. SyncDisabled_PrimaryAccountInAuthError) {
  148. EnableSync();
  149. SetInvalidCredentialsAuthError();
  150. sync_service_.SetDisableReasons(SyncService::DISABLE_REASON_USER_CHOICE);
  151. base::HistogramTester ht;
  152. StartAndEndSession(kSessionTime);
  153. // If the user opted in to sync, but then disabled sync (e.g. via policy or
  154. // from the Android OS settings), then they are counted as having opted out
  155. // of sync.
  156. // The account is in auth error, so they are also counted as not having any
  157. // browser account.
  158. ExpectOneSessionWithDuration(ht, {"NotOptedInToSyncWithoutAccount"},
  159. kSessionTime);
  160. ExpectNoSession(ht,
  161. {"NotOptedInToSyncWithAccount", "OptedInToSyncWithoutAccount",
  162. "OptedInToSyncWithAccount"});
  163. }
  164. TEST_F(SyncSessionDurationsMetricsRecorderTest,
  165. NotOptedInToSync_SecondaryAccountInAuthError) {
  166. AccountInfo account =
  167. identity_test_env_.MakeAccountAvailable("foo@gmail.com");
  168. identity_test_env_.UpdatePersistentErrorOfRefreshTokenForAccount(
  169. account.account_id,
  170. GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
  171. base::HistogramTester ht;
  172. StartAndEndSession(kSessionTime);
  173. // The account is in auth error, so they are counted as not having any browser
  174. // account.
  175. ExpectOneSessionWithDuration(ht, {"NotOptedInToSyncWithoutAccount"},
  176. kSessionTime);
  177. ExpectNoSession(ht,
  178. {"NotOptedInToSyncWithAccount", "OptedInToSyncWithoutAccount",
  179. "OptedInToSyncWithAccount"});
  180. }
  181. TEST_F(SyncSessionDurationsMetricsRecorderTest, SyncUnknownOnStartup) {
  182. EnableSync();
  183. // Simulate sync initializing (before first connection to the server).
  184. sync_service_.SetLastCycleSnapshot(syncer::SyncCycleSnapshot());
  185. ASSERT_TRUE(sync_service_.IsSyncFeatureActive());
  186. ASSERT_FALSE(sync_service_.HasCompletedSyncCycle());
  187. base::HistogramTester ht;
  188. StartAndEndSession(kSessionTime);
  189. ExpectOneSessionWithDuration(ht, {"NotOptedInToSyncWithAccount"},
  190. kSessionTime);
  191. ExpectNoSession(
  192. ht, {"NotOptedInToSyncWithoutAccount", "OptedInToSyncWithoutAccount",
  193. "OptedInToSyncWithoutAccount"});
  194. }
  195. TEST_F(SyncSessionDurationsMetricsRecorderTest,
  196. SyncUnknownOnStartupThenStarts) {
  197. EnableSync();
  198. // Simulate sync initializing (before first connection to the server).
  199. SyncCycleSnapshot active_sync_snapshot =
  200. sync_service_.GetLastCycleSnapshotForDebugging();
  201. sync_service_.SetLastCycleSnapshot(syncer::SyncCycleSnapshot());
  202. ASSERT_TRUE(sync_service_.IsSyncFeatureActive());
  203. ASSERT_FALSE(sync_service_.HasCompletedSyncCycle());
  204. SyncSessionDurationsMetricsRecorder metrics_recorder(
  205. &sync_service_, identity_test_env_.identity_manager());
  206. {
  207. base::HistogramTester ht;
  208. metrics_recorder.OnSessionStarted(base::TimeTicks::Now());
  209. // Activate sync
  210. sync_service_.SetLastCycleSnapshot(active_sync_snapshot);
  211. ASSERT_TRUE(sync_service_.IsSyncFeatureActive() &&
  212. sync_service_.HasCompletedSyncCycle());
  213. metrics_recorder.OnStateChanged(&sync_service_);
  214. // Sync was in unknown state, so histograms should not be logged.
  215. ExpectNoSession(
  216. ht, {"NotOptedInToSyncWithAccount", "NotOptedInToSyncWithoutAccount",
  217. "OptedInToSyncWithoutAccount", "OptedInToSyncWithoutAccount"});
  218. }
  219. {
  220. base::HistogramTester ht;
  221. metrics_recorder.OnSessionEnded(kSessionTime);
  222. ExpectOneSession(ht, {"OptedInToSyncWithAccount"});
  223. ExpectNoSession(
  224. ht, {"NotOptedInToSyncWithAccount", "NotOptedInToSyncWithoutAccount",
  225. "OptedInToSyncWithoutAccount"});
  226. }
  227. }
  228. TEST_F(SyncSessionDurationsMetricsRecorderTest, EnableSync) {
  229. SyncSessionDurationsMetricsRecorder metrics_recorder(
  230. &sync_service_, identity_test_env_.identity_manager());
  231. {
  232. base::HistogramTester ht;
  233. metrics_recorder.OnSessionStarted(base::TimeTicks::Now());
  234. EnableSync();
  235. // The initial state of the record was: sync_status = OFF, acount_status=OFF
  236. // When sync gets initialized, 2 things happen:
  237. // 1. account_status=ON. => Log NotOptedInToSyncWithoutAccount
  238. // 2. sync_status=ON => Log NotOptedInToSyncWithAccount
  239. ExpectOneSession(ht, {"NotOptedInToSyncWithoutAccount"});
  240. ExpectOneSession(ht, {"NotOptedInToSyncWithAccount"});
  241. ExpectNoSession(
  242. ht, {"OptedInToSyncWithoutAccount", "OptedInToSyncWithAccount"});
  243. }
  244. {
  245. base::HistogramTester ht;
  246. metrics_recorder.OnSessionEnded(kSessionTime);
  247. ExpectOneSession(ht, {"OptedInToSyncWithAccount"});
  248. ExpectNoSession(
  249. ht, {"NotOptedInToSyncWithoutAccount", "NotOptedInToSyncWithoutAccount",
  250. "OptedInToSyncWithoutAccount"});
  251. }
  252. }
  253. TEST_F(SyncSessionDurationsMetricsRecorderTest, EnterAuthError) {
  254. EnableSync();
  255. SyncSessionDurationsMetricsRecorder metrics_recorder(
  256. &sync_service_, identity_test_env_.identity_manager());
  257. {
  258. base::HistogramTester ht;
  259. metrics_recorder.OnSessionStarted(base::TimeTicks::Now());
  260. SetInvalidCredentialsAuthError();
  261. ExpectOneSession(ht, {"OptedInToSyncWithAccount"});
  262. ExpectNoSession(
  263. ht, {"NotOptedInToSyncWithAccount", "NotOptedInToSyncWithoutAccount",
  264. "OptedInToSyncWithoutAccount"});
  265. }
  266. {
  267. base::HistogramTester ht;
  268. metrics_recorder.OnSessionEnded(kSessionTime);
  269. ExpectOneSession(ht, {"OptedInToSyncWithoutAccount"});
  270. ExpectNoSession(
  271. ht, {"NotOptedInToSyncWithAccount", "NotOptedInToSyncWithoutAccount",
  272. "OptedInToSyncWithAccount"});
  273. }
  274. }
  275. TEST_F(SyncSessionDurationsMetricsRecorderTest, FixedAuthError) {
  276. EnableSync();
  277. SetInvalidCredentialsAuthError();
  278. SyncSessionDurationsMetricsRecorder metrics_recorder(
  279. &sync_service_, identity_test_env_.identity_manager());
  280. {
  281. base::HistogramTester ht;
  282. metrics_recorder.OnSessionStarted(base::TimeTicks::Now());
  283. ClearAuthError();
  284. ExpectOneSession(ht, {"OptedInToSyncWithoutAccount"});
  285. ExpectNoSession(
  286. ht, {"NotOptedInToSyncWithAccount", "NotOptedInToSyncWithoutAccount",
  287. "OptedInToSyncWithAccount"});
  288. }
  289. {
  290. base::HistogramTester ht;
  291. metrics_recorder.OnSessionEnded(kSessionTime);
  292. ExpectOneSession(ht, {"OptedInToSyncWithAccount"});
  293. ExpectNoSession(
  294. ht, {"NotOptedInToSyncWithAccount", "NotOptedInToSyncWithoutAccount",
  295. "OptedInToSyncWithoutAccount"});
  296. }
  297. }
  298. } // namespace
  299. } // namespace syncer