# This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. from telemetry_harness.fog_ping_filters import ( FOG_DELETION_REQUEST_PING, FOGDocTypePingFilter, ) from telemetry_harness.fog_testcase import FOGTestCase BASELINE = FOGDocTypePingFilter("baseline") FOG_USAGE_REPORTING = FOGDocTypePingFilter("usage-reporting") FOG_USAGE_DELETION_REQUEST_PING = FOGDocTypePingFilter("usage-deletion-request") CANARY_USAGE_PROFILE_ID = "beefbeef-beef-beef-beef-beeefbeefbee" CANARY_USAGE_PROFILE_GROUP_ID = "b0bacafe-b0ba-cafe-b0ba-cafeb0bacafe" class TestUsageReporting(FOGTestCase): """Tests for FOG usage-reporting ping and usage-id cycling.""" def disable_usage_reporting(self): """Disable usage reporting in the current browser.""" self.marionette.instance.profile.set_persistent_preferences( {"datareporting.usage.uploadEnabled": False} ) self.marionette.set_pref("datareporting.usage.uploadEnabled", False) def enable_usage_reporting(self): """Enable usage reporting in the current browser.""" self.marionette.instance.profile.set_persistent_preferences( {"datareporting.usage.uploadEnabled": True} ) self.marionette.set_pref("datareporting.usage.uploadEnabled", True) def test_usage_reporting_independent_from_telemetry(self): """ Test that the "usage-reporting" ping behaviour is independent from general telemetry. We do not expect a "usage-deletion-request" ping. The "deletion-request" ping should not include the usage-id. The `usage.profile_id` stays the same across telemetry toggling. """ self.marionette.set_pref("telemetry.glean.internal.maxPingsPerMinute", 60) ping1 = self.wait_for_ping( lambda: self.marionette.restart(in_app=True), FOG_USAGE_REPORTING, ping_server=self.fog_ping_server, ) self.assertNotIn("ping_info", ping1["payload"]) self.assertNotIn("client_info", ping1["payload"]) metrics = ping1["payload"]["metrics"] self.assertNotIn("legacy.telemetry.client_id", metrics["uuid"]) self.assertNotIn("legacy.telemetry.profile_group_id", metrics["uuid"]) self.assertIn("usage.profile_id", metrics["uuid"]) usage_id1 = metrics["uuid"]["usage.profile_id"] self.assertIsValidUUID(usage_id1) self.assertNotEqual(CANARY_USAGE_PROFILE_ID, usage_id1) self.assertIn("usage.profile_group_id", metrics["uuid"]) usage_group_id1 = metrics["uuid"]["usage.profile_group_id"] self.assertIsValidUUID(usage_group_id1) self.assertNotEqual(CANARY_USAGE_PROFILE_GROUP_ID, usage_group_id1) # Regular `deletion-request` ping won't have the `usage.profile_id`. # We just wait for it to know it happened. _ping2 = self.wait_for_ping( self.disable_telemetry, FOG_DELETION_REQUEST_PING, ping_server=self.fog_ping_server, ) # Other telemetry stays disabled. # We should still get the usage-reporting ping on a restart. ping3 = self.wait_for_ping( lambda: self.marionette.restart(in_app=True), FOG_USAGE_REPORTING, ping_server=self.fog_ping_server, ) self.assertIn("usage.profile_id", ping3["payload"]["metrics"]["uuid"]) usage_id2 = ping3["payload"]["metrics"]["uuid"]["usage.profile_id"] self.assertIsValidUUID(usage_id2) self.assertEqual(usage_id1, usage_id2) self.assertNotEqual(CANARY_USAGE_PROFILE_ID, usage_id2) self.assertIn("usage.profile_group_id", ping3["payload"]["metrics"]["uuid"]) usage_group_id2 = ping3["payload"]["metrics"]["uuid"]["usage.profile_group_id"] self.assertIsValidUUID(usage_group_id2) self.assertEqual(usage_group_id1, usage_group_id2) self.assertNotEqual(CANARY_USAGE_PROFILE_GROUP_ID, usage_group_id2) def test_usage_deletion_request(self): """ Test the "usage-reporting" ping behaviour and usage-id cycling when disabling telemetry. We expect a "usage-deletion-request" ping, and it should include the usage-id. """ self.marionette.set_pref("telemetry.glean.internal.maxPingsPerMinute", 60) ping1 = self.wait_for_ping( lambda: self.marionette.restart(in_app=True), FOG_USAGE_REPORTING, ping_server=self.fog_ping_server, ) self.assertNotIn("ping_info", ping1["payload"]) self.assertNotIn("client_info", ping1["payload"]) metrics = ping1["payload"]["metrics"] self.assertNotIn("legacy.telemetry.client_id", metrics["uuid"]) self.assertNotIn("legacy.telemetry.profile_group_id", metrics["uuid"]) self.assertIn("usage.profile_id", metrics["uuid"]) usage_id1 = metrics["uuid"]["usage.profile_id"] self.assertIsValidUUID(usage_id1) self.assertNotEqual(CANARY_USAGE_PROFILE_ID, usage_id1) self.assertIn("usage.profile_group_id", metrics["uuid"]) usage_group_id1 = metrics["uuid"]["usage.profile_group_id"] self.assertIsValidUUID(usage_group_id1) self.assertNotEqual(CANARY_USAGE_PROFILE_GROUP_ID, usage_group_id1) # `usage-deletion-request` ping will have the `usage.profile_id`. # # N.b.: the `usage-deletion-request` ping has `include_info_sections: # false`, so no `payload.ping_info.reason` is available for inspection. # We still set the reason in case details change. ping2 = self.wait_for_ping( self.disable_usage_reporting, FOG_USAGE_DELETION_REQUEST_PING, ping_server=self.fog_ping_server, ) metrics = ping2["payload"]["metrics"] self.assertIn("usage.profile_id", metrics["uuid"]) usage_id2 = metrics["uuid"]["usage.profile_id"] self.assertEqual(usage_id1, usage_id2) self.assertIn("usage.profile_group_id", metrics["uuid"]) usage_group_id2 = metrics["uuid"]["usage.profile_group_id"] self.assertEqual(usage_group_id1, usage_group_id2) self.enable_usage_reporting() ping3 = self.wait_for_ping( lambda: self.marionette.restart(in_app=True), FOG_USAGE_REPORTING, ping_server=self.fog_ping_server, ) self.assertIn("usage.profile_id", ping3["payload"]["metrics"]["uuid"]) usage_id3 = ping3["payload"]["metrics"]["uuid"]["usage.profile_id"] self.assertIsValidUUID(usage_id3) self.assertNotEqual(usage_id1, usage_id3) self.assertNotEqual(CANARY_USAGE_PROFILE_ID, usage_id3) self.assertIn("usage.profile_group_id", ping3["payload"]["metrics"]["uuid"]) usage_group_id3 = ping3["payload"]["metrics"]["uuid"]["usage.profile_group_id"] self.assertIsValidUUID(usage_group_id3) self.assertNotEqual(usage_group_id1, usage_group_id3) self.assertNotEqual(CANARY_USAGE_PROFILE_GROUP_ID, usage_group_id3) def test_enabled_state_after_restart(self): """ Test that the "usage-reporting" ping remains enabled and the usage ID and usage group ID remain fixed when restarting the browser. """ self.marionette.set_pref("telemetry.glean.internal.maxPingsPerMinute", 60) # Not guaranteed to send a "usage-reporting" ping. self.enable_usage_reporting() # But restarting should send a "usage-reporting ping". ping1 = self.wait_for_ping( lambda: self.marionette.restart(in_app=True), FOG_USAGE_REPORTING, ping_server=self.fog_ping_server, ) metrics = ping1["payload"]["metrics"] self.assertIn("usage.profile_id", metrics["uuid"]) usage_id1 = metrics["uuid"]["usage.profile_id"] self.assertIsValidUUID(usage_id1) self.assertNotEqual(CANARY_USAGE_PROFILE_ID, usage_id1) self.assertIn("usage.profile_group_id", metrics["uuid"]) usage_group_id1 = metrics["uuid"]["usage.profile_group_id"] self.assertIsValidUUID(usage_group_id1) self.assertNotEqual(CANARY_USAGE_PROFILE_GROUP_ID, usage_group_id1) # Restarting again should maintain enabled state. ping2 = self.wait_for_ping( lambda: self.marionette.restart(in_app=True), FOG_USAGE_REPORTING, ping_server=self.fog_ping_server, ) metrics = ping2["payload"]["metrics"] self.assertIn("usage.profile_id", metrics["uuid"]) usage_id2 = metrics["uuid"]["usage.profile_id"] self.assertEqual(usage_id1, usage_id2) self.assertIn("usage.profile_group_id", metrics["uuid"]) usage_group_id2 = metrics["uuid"]["usage.profile_group_id"] self.assertEqual(usage_group_id1, usage_group_id2) def test_disabled_state_after_restart(self): """ Test that the "usage-reporting" ping remains disabled and the usage ID remains null when restarting the browser. """ self.marionette.set_pref("telemetry.glean.internal.maxPingsPerMinute", 60) self.enable_usage_reporting() # Disabling should send a "usage-deletion-request". ping1 = self.wait_for_ping( self.disable_usage_reporting, FOG_USAGE_DELETION_REQUEST_PING, ping_server=self.fog_ping_server, ) metrics = ping1["payload"]["metrics"] self.assertIn("usage.profile_id", metrics["uuid"]) usage_id1 = metrics["uuid"]["usage.profile_id"] self.assertIsValidUUID(usage_id1) self.assertNotEqual(CANARY_USAGE_PROFILE_ID, usage_id1) self.assertIn("usage.profile_group_id", metrics["uuid"]) usage_group_id1 = metrics["uuid"]["usage.profile_group_id"] self.assertIsValidUUID(usage_group_id1) self.assertNotEqual(CANARY_USAGE_PROFILE_GROUP_ID, usage_group_id1) current_num_pings = len(self.fog_ping_server.pings) # It's not easy to wait for the _absence_ of a ping. So restart and # wait for a _different_ ping, then verify we didn't get any additional # "usage-reporting" pings. _ping2 = self.wait_for_ping( lambda: self.marionette.restart(in_app=True), BASELINE, ping_server=self.fog_ping_server, ) self.assertIs( len(self.fog_ping_server.pings) > current_num_pings, True, "Expected at least 'baseline' ping", ) for ping in self.fog_ping_server.pings[(current_num_pings + 1) :]: self.assertIs( FOG_USAGE_REPORTING(ping), False, "Expected no 'usage-reporting' pings" ) def test_existing_profile_inherits_general_preference(self): # Existing profiles should inherit the general preference, true or # false. New profiles should not inherit the general preference, and # instead should default to true. self.marionette.set_pref("telemetry.glean.internal.maxPingsPerMinute", 60) def healthreportEnabled(): return self.marionette.get_pref("datareporting.healthreport.uploadEnabled") def usageEnabled(): return self.marionette.get_pref("datareporting.usage.uploadEnabled") # Ordering matters here: we want to be sure that we really did restart the browser. last_pid = self.marionette.process_id # New profiles should not inherit the general preference. self.marionette.enforce_gecko_prefs( {"datareporting.healthreport.uploadEnabled": False} ) self.assertIs(healthreportEnabled(), False) self.assertIs(usageEnabled(), True) self.assertNotEqual(last_pid, self.marionette.process_id) last_pid = self.marionette.process_id self.marionette.enforce_gecko_prefs( {"datareporting.healthreport.uploadEnabled": True} ) self.assertIs(healthreportEnabled(), True) self.assertIs(usageEnabled(), True) self.assertNotEqual(last_pid, self.marionette.process_id) last_pid = self.marionette.process_id # Existing profiles have a migration version. self.marionette.enforce_gecko_prefs( prefs={ "datareporting.healthreport.uploadEnabled": False, "browser.migration.version": 150, } ) self.assertIs(healthreportEnabled(), False) self.assertIs(usageEnabled(), False) self.assertNotEqual(last_pid, self.marionette.process_id) last_pid = self.marionette.process_id self.marionette.enforce_gecko_prefs( prefs={ "datareporting.healthreport.uploadEnabled": True, "browser.migration.version": 150, } ) self.assertIs(healthreportEnabled(), True) self.assertIs(usageEnabled(), True) self.assertNotEqual(last_pid, self.marionette.process_id) last_pid = self.marionette.process_id