def test_get_device_view(self): """Test getting device view.""" # Create a device device = Dummy.create_device(Dummy.create_user()) # Get the corresponding device view response = self._get_with_params(self.device_url, {"uuid": device.uuid}) # Assert that the view is constructed from the correct templates and # the response context contains the device UUID self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertTemplateUsed(response, "crashreport_stats/device.html", count=1) self.assertTemplateUsed(response, "crashreport_stats/tags/device_overview.html", count=1) self.assertTemplateUsed( response, "crashreport_stats/tags/device_update_history.html", count=1, ) self.assertTemplateUsed( response, "crashreport_stats/tags/device_report_history.html", count=1, ) self.assertTemplateUsed( response, "crashreport_stats/tags/device_crashreport_table.html", count=1, ) self.assertEqual(response.context["uuid"], str(device.uuid))
def test_get_device_stats(self): """Test getting device stats for a device.""" # Create a device with a heartbeat and one report of each type device = Dummy.create_device(Dummy.create_user()) crashreport_date = Dummy.DEFAULT_CRASHREPORT_VALUES["date"] heartbeat = Dummy.create_report(HeartBeat, device, date=crashreport_date.date()) for boot_reason in (Crashreport.SMPL_BOOT_REASONS + Crashreport.CRASH_BOOT_REASONS + ["other boot reason"]): Dummy.create_report( Crashreport, device, boot_reason=boot_reason, date=crashreport_date, ) crashreport_date += timedelta(milliseconds=1) # Get the device statistics response = self._get_with_params(self.device_overview_url, {"uuid": device.uuid}) # Assert that the statistics match self._assert_device_stats_response_is( response=response, uuid=str(device.uuid), board_date=device.board_date, num_heartbeats=1, num_crashreports=len(Crashreport.CRASH_BOOT_REASONS), num_smpls=len(Crashreport.SMPL_BOOT_REASONS), crashes_per_day=len(Crashreport.CRASH_BOOT_REASONS), smpl_per_day=len(Crashreport.SMPL_BOOT_REASONS), last_active=heartbeat.date, )
def _assert_counter_distribution_is_correct(self, report_type, numbers, counter_attribute_name, **kwargs): """Validate a counter distribution in the database.""" if len(numbers) != len(self.unique_entries): raise ValueError("The length of the numbers list must match the " "length of self.unique_entries in the test class" "({} != {})".format(len(numbers), len(self.unique_entries))) # Create some reports for unique_entry, num, username in zip(self.unique_entries, numbers, Dummy.USERNAMES): user = Dummy.create_user(username=username) device = Dummy.create_device(user=user) self._create_reports(report_type, unique_entry, device, num, **kwargs) # Run the command to update the database call_command("stats", "update") # Check whether the numbers of reports match for version in self.version_class.objects.all(): unique_entry_name = getattr(version, self.unique_entry_name) num = numbers[self.unique_entries.index(unique_entry_name)] self.assertEqual(num, getattr(version, counter_attribute_name))
def _assert_older_report_updates_version_date(self, report_type): """Validate that older reports sent later affect the version date.""" user = Dummy.create_user() device = Dummy.create_device(user=user) report = Dummy.create_report(report_type, device=device) # Run the command to update the database call_command("stats", "update") get_params = { self.unique_entry_name: getattr(report, self.unique_entry_name) } version = self.version_class.objects.get(**get_params) report_date = (report.date.date() if report_type == Crashreport else report.date) self.assertEqual(report_date, version.first_seen_on) # Create a new report from an earlier point in time report_date_2 = report.date - timedelta(weeks=1) Dummy.create_report(report_type, device=device, date=report_date_2) # Run the command to update the database call_command("stats", "update") # Get the same version object from before version = self.version_class.objects.get(**get_params) # Validate that the date matches the report recently sent if report_type == Crashreport: report_date_2 = report_date_2.date() self.assertEqual(report_date_2, version.first_seen_on)
def test_get_device_stats_multiple_days_missing_heartbeat(self): """Test getting device stats for a device with missing heartbeat.""" # Create a device with some heartbeats and reports over time device = Dummy.create_device(Dummy.create_user()) num_days = 100 skip_day = round(num_days / 2) for i in range(num_days): report_date = datetime.now(tz=pytz.utc) + timedelta(days=i) # Skip creation of heartbeat at one day if i != skip_day: heartbeat = Dummy.create_report(HeartBeat, device, date=report_date.date()) Dummy.create_report(Crashreport, device, date=report_date) # Get the device statistics response = self._get_with_params(self.device_overview_url, {"uuid": device.uuid}) # Assert that the statistics match self._assert_device_stats_response_is( response=response, uuid=str(device.uuid), board_date=device.board_date, num_heartbeats=num_days - 1, num_crashreports=num_days, num_smpls=0, crashes_per_day=num_days / (num_days - 1), smpl_per_day=0, last_active=heartbeat.date, )
def test_get_device_update_history(self): """Test getting update history stats for a device.""" # Create a device with a heartbeat and one report of each type device = Dummy.create_device(Dummy.create_user()) crashreport_date = Dummy.DEFAULT_CRASHREPORT_VALUES["date"] heartbeat = Dummy.create_report(HeartBeat, device, date=crashreport_date.date()) for boot_reason in (Crashreport.SMPL_BOOT_REASONS + Crashreport.CRASH_BOOT_REASONS + ["other boot reason"]): params = {"boot_reason": boot_reason, "date": crashreport_date} Dummy.create_report(Crashreport, device, **params) crashreport_date += timedelta(milliseconds=1) # Get the device update history statistics response = self._get_with_params(self.device_update_history_url, {"uuid": device.uuid}) # Assert that the statistics match update_history = [{ "build_fingerprint": heartbeat.build_fingerprint, "heartbeats": 1, "max": device.id, "other": 1, "prob_crashes": len(Crashreport.CRASH_BOOT_REASONS), "smpl": len(Crashreport.SMPL_BOOT_REASONS), "update_date": heartbeat.date, }] self.assertEqual(update_history, response.data)
def test_get_device_update_history_no_reports(self): """Test getting update history stats for a device without reports.""" # Create a device device = Dummy.create_device(Dummy.create_user()) # Get the device report history statistics response = self._get_with_params(self.device_update_history_url, {"uuid": device.uuid}) # Assert that the update history is empty self.assertEqual([], response.data)
def test_get_versions_all_view(self): """Test getting versions view.""" # Create a version Dummy.create_version() # Get the versions view response = self.fp_staff_client.get(self.versions_all_url) # Assert that the correct templates are used and the response context # contains the an empty value for is_official_release self._assert_versions_view_templates_are_used(response) self.assertEqual(response.context.get("is_official_release", ""), "")
def test_home_view_filter_devices_by_uuid_part(self): """Test filtering devices by start of UUID.""" # Create a device device = Dummy.create_device(Dummy.create_user()) # Filter devices with start of the created device's UUID response = self.fp_staff_client.post( self.home_url, data={"uuid": str(device.uuid)[:4]}) # Assert that the the client is redirected to the device page self.assertRedirects( response, self._url_with_params(self.device_url, {"uuid": device.uuid}), )
def _create_reports(self, report_type, unique_entry_name, device, number, **kwargs): # Create reports with distinct timestamps report_date = datetime.now(pytz.utc) if report_type == HeartBeat: report_date = report_date.date() for i in range(number): report_attributes = { self.unique_entry_name: unique_entry_name, "device": device, "date": report_date - timedelta(days=i), } report_attributes.update(**kwargs) Dummy.create_report(report_type, **report_attributes)
def test_reset_deletion_of_unrelated_version(self): """Test delete functionality of the reset command.""" # Create a version instance that is not related to any reports Dummy.create_version( self.version_class, **{self.unique_entry_name: self.unique_entries[0]}) # Run the command to reset the database call_command("stats", "reset") # Check whether the unrelated version instance has been deleted self.assertFalse( self.version_class.objects.filter( **{ self.unique_entry_name: self.unique_entries[0] }).exists())
def _assert_manually_changed_released_on_date_is_not_updated( self, report_type, **kwargs): """Test updating of manually changed released_on dates. Validate that a manually changed released_on date is not updated when new reports are sent. """ # Create a report device = Dummy.create_device(user=Dummy.create_user()) report = Dummy.create_report(report_type, device=device, **kwargs) # Run the command to update the database call_command("stats", "update") # Get the corresponding version instance from the database version = self.version_class.objects.get( ** {self.unique_entry_name: getattr(report, self.unique_entry_name)}) # Assert that the released_on date matches the first report date report_date = (report.date.date() if report_type == Crashreport else report.date) self.assertEqual(version.released_on, report_date) # Create a second report with a timestamp earlier in time report_2_date = report.date - timedelta(days=1) Dummy.create_report(report_type, device=device, date=report_2_date, **kwargs) # Manually change the released_on date version_release_date = report_date + timedelta(days=1) version.released_on = version_release_date version.save() # Run the command to update the database call_command("stats", "update") # Get the corresponding version instance from the database version = self.version_class.objects.get( ** {self.unique_entry_name: getattr(report, self.unique_entry_name)}) # Assert that the released_on date still matches the date is was # manually changed to self.assertEqual(version.released_on, version_release_date)
def test_get_device_stats_multiple_days(self): """Test getting device stats for a device that sent more reports.""" # Create a device with some heartbeats and reports over time device = Dummy.create_device(Dummy.create_user()) num_days = 100 for i in range(num_days): report_date = datetime.now(tz=pytz.utc) + timedelta(days=i) heartbeat = Dummy.create_report(HeartBeat, device, date=report_date.date()) Dummy.create_report(Crashreport, device, date=report_date) Dummy.create_report( Crashreport, device, date=report_date + timedelta(minutes=1), boot_reason=Crashreport.SMPL_BOOT_REASONS[0], ) # Get the device statistics response = self._get_with_params(self.device_overview_url, {"uuid": device.uuid}) # Assert that the statistics match self._assert_device_stats_response_is( response=response, uuid=str(device.uuid), board_date=device.board_date, num_heartbeats=num_days, num_crashreports=num_days, num_smpls=num_days, crashes_per_day=1, smpl_per_day=1, last_active=heartbeat.date, )
def test_download_logfile(self): """Test download of log files.""" # Create a device with a crash report along with log file device = Dummy.create_device(Dummy.create_user()) crashreport = Dummy.create_report(Crashreport, device) logfile = Dummy.create_log_file(crashreport) # Get the log file response = self._get_with_params(self.device_logfile_download_url, {"id_logfile": logfile.id}) # Assert that the log file contents are in the response data self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertIn(Dummy.DEFAULT_LOG_FILE_NAME, response.data) expected_logfile_content = Dummy.read_logfile_contents( logfile.logfile.path, Dummy.DEFAULT_LOG_FILE_NAME) self.assertEqual(response.data[Dummy.DEFAULT_LOG_FILE_NAME], expected_logfile_content)
def test_stats_calculation(self): """Test generation of a Version instance.""" user = Dummy.create_user() device = Dummy.create_device(user=user) heartbeat = Dummy.create_report(HeartBeat, device=device) # Expect that we do not have the Version before updating the stats get_params = { self.unique_entry_name: getattr(heartbeat, self.unique_entry_name) } self.assertRaises(self.version_class.DoesNotExist, self.version_class.objects.get, **get_params) # Run the command to update the database call_command("stats", "update") # Assume that a corresponding Version instance has been created version = self.version_class.objects.get(**get_params) self.assertIsNotNone(version)
def _assert_older_reports_update_released_on_date(self, report_type, **kwargs): """Test updating of the released_on date. Validate that the released_on date is updated once an older report is sent. """ # Create a report device = Dummy.create_device(user=Dummy.create_user()) report = Dummy.create_report(report_type, device=device, **kwargs) # Run the command to update the database call_command("stats", "update") # Get the corresponding version instance from the database version = self.version_class.objects.get( ** {self.unique_entry_name: getattr(report, self.unique_entry_name)}) # Assert that the released_on date matches the first report date report_date = (report.date.date() if report_type == Crashreport else report.date) self.assertEqual(version.released_on, report_date) # Create a second report with the a timestamp earlier in time report_2_date = report.date - timedelta(days=1) Dummy.create_report(report_type, device=device, date=report_2_date, **kwargs) # Run the command to update the database call_command("stats", "update") # Get the corresponding version instance from the database version = self.version_class.objects.get( ** {self.unique_entry_name: getattr(report, self.unique_entry_name)}) # Assert that the released_on date matches the older report date if report_type == Crashreport: report_2_date = report_2_date.date() self.assertEqual(version.released_on, report_2_date)
def test_entries_are_unique(self): """Validate the entries' unicity and value.""" # Create some reports for unique_entry, username in zip(self.unique_entries, Dummy.USERNAMES): user = Dummy.create_user(username=username) device = Dummy.create_device(user=user) self._create_reports(HeartBeat, unique_entry, device, 10) # Run the command to update the database call_command("stats", "update") # Check whether the correct amount of distinct versions have been # created versions = self.version_class.objects.all() for version in versions: self.assertIn(getattr(version, self.unique_entry_name), self.unique_entries) self.assertEqual(len(versions), len(self.unique_entries))
def test_get_status(self): """Get the status after some reports have been created.""" # Create a device with a heartbeat and a crash report device = Dummy.create_device(Dummy.create_user()) Dummy.create_report(HeartBeat, device) Dummy.create_report(Crashreport, device) # Create a second device without any reports Dummy.create_device(Dummy.create_user(username=Dummy.USERNAMES[1])) # Assert that the status includes the appropriate numbers (a third # device was created by the setUpTestData() method) response = self.fp_staff_client.get(self.status_url) self._assert_status_response_is(response, num_devices=3, num_crashreports=1, num_heartbeats=1)
def test_get_device_stats_no_reports(self): """Test getting device stats for a device without reports.""" # Create a device device = Dummy.create_device(Dummy.create_user()) # Get the device statistics response = self._get_with_params(self.device_overview_url, {"uuid": device.uuid}) # Assert that the statistics match self._assert_device_stats_response_is( response=response, uuid=str(device.uuid), board_date=device.board_date, num_heartbeats=0, num_crashreports=0, num_smpls=0, crashes_per_day=0.0, smpl_per_day=0.0, last_active=device.board_date, )
def test_home_view_filter_devices_by_uuid_part_ambiguous_result(self): """Test filtering devices with common start of UUIDs.""" # Create two devices device1 = Dummy.create_device(Dummy.create_user()) device2 = Dummy.create_device( Dummy.create_user(username=Dummy.USERNAMES[1])) # Adapt the devices' UUID so that they start with the same characters device1.uuid = "4060fd90-6de1-4b03-a380-4277c703e913" device1.save() device2.uuid = "4061c59b-823d-4ec6-a463-8ac0c1cea67d" device2.save() # Filter devices with first three (common) characters of the UUID response = self.fp_staff_client.post( self.home_url, data={"uuid": str(device1.uuid)[:3]}) # Assert that both devices are part of the result self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertTemplateUsed(response, "crashreport_stats/home.html", count=1) self.assertEqual(set(response.context["devices"]), {device1, device2})
def _assert_reset_updates_counter(self, report_type, counter_attribute_name, **kwargs): # Create a device and corresponding reports device = Dummy.create_device(Dummy.create_user()) num_reports = 5 self._create_reports(report_type, self.unique_entries[0], device, num_reports, **kwargs) # Create a version instance with wrong numbers wrong_num_of_reports = 4 Dummy.create_version( self.version_class, **{ self.unique_entry_name: self.unique_entries[0], counter_attribute_name: wrong_num_of_reports, }) # Run the command to reset the database call_command("stats", "reset") # Check whether the numbers of reports do match version = self.version_class.objects.get( **{self.unique_entry_name: self.unique_entries[0]}) self.assertEqual(num_reports, getattr(version, counter_attribute_name))
def _assert_accumulated_counters_are_correct(self, report_type, counter_attribute_name, **kwargs): """Validate a counter distribution with reports of different devices.""" # Create some devices and corresponding reports devices = [ Dummy.create_device(Dummy.create_user(username=name)) for name in Dummy.USERNAMES ] num_reports = 5 for device in devices: self._create_reports(report_type, self.unique_entries[0], device, num_reports, **kwargs) # Run the command to update the database call_command("stats", "update") # Check whether the numbers of reports match version = self.version_class.objects.get( **{self.unique_entry_name: self.unique_entries[0]}) self.assertEqual( len(Dummy.USERNAMES) * num_reports, getattr(version, counter_attribute_name), )
def test_reset_command_deletion_of_instances(self): """Test the deletion of stats model instances with the reset command. This test validates that model instances get deleted when the reset command is called on a database that only contains a single model instance for each class. """ # Create dummy version instances version = Dummy.create_version() radio_version = Dummy.create_version(RadioVersion) Dummy.create_daily_version(version) Dummy.create_daily_radio_version(radio_version) Dummy.create_stats_metadata() # We expect that the model instances get deleted self._assert_command_output_matches("reset", 1, ["deleted"], self._ALL_MODELS)
def _assert_updating_twice_gives_correct_counters(self, report_type, counter_attribute_name, **boot_reason_param): # Create a two devices and a corresponding reports for 2 different # versions device_1 = Dummy.create_device(Dummy.create_user()) num_reports = 5 self._create_reports(report_type, self.unique_entries[0], device_1, num_reports, **boot_reason_param) device_2 = Dummy.create_device( Dummy.create_user(username=Dummy.USERNAMES[1])) self._create_reports(report_type, self.unique_entries[1], device_2, num_reports, **boot_reason_param) # Run the command to update the database call_command("stats", "update") # Check whether the numbers of reports match for both versions version_1 = self.version_class.objects.get( **{self.unique_entry_name: self.unique_entries[0]}) self.assertEqual(num_reports, getattr(version_1, counter_attribute_name)) version_2 = self.version_class.objects.get( **{self.unique_entry_name: self.unique_entries[1]}) self.assertEqual(num_reports, getattr(version_2, counter_attribute_name)) # Create another report for the first version report_new_attributes = { self.unique_entry_name: self.unique_entries[0], "device": device_1, **boot_reason_param, } Dummy.create_report(report_type, **report_new_attributes) # Run the command to update the database again call_command("stats", "update") # Check whether the numbers of reports match: # Assert that the first version's counter is increased by one version_1 = self.version_class.objects.get( **{self.unique_entry_name: self.unique_entries[0]}) self.assertEqual(num_reports + 1, getattr(version_1, counter_attribute_name)) # Assert that the second versions counter is unchanged version_2 = self.version_class.objects.get( **{self.unique_entry_name: self.unique_entries[1]}) self.assertEqual(num_reports, getattr(version_2, counter_attribute_name))
def test_get_device_report_history_multiple_days(self): """Test getting report history stats for a device for multiple days.""" device = Dummy.create_device(Dummy.create_user()) expected_report_history = [] # Create a device with a heartbeat and one report of each type for 10 # days report_date = Dummy.DEFAULT_CRASHREPORT_VALUES["date"] for _ in range(10): report_date = report_date + timedelta(days=1) Dummy.create_report(HeartBeat, device, date=report_date.date()) for i, boot_reason in enumerate(Crashreport.SMPL_BOOT_REASONS + Crashreport.CRASH_BOOT_REASONS + ["other boot reason"]): Dummy.create_report( Crashreport, device, boot_reason=boot_reason, date=report_date + timedelta(milliseconds=i), ) # Create the expected report history object expected_report_history.append({ "date": report_date.date(), "heartbeats": 1, "smpl": len(Crashreport.SMPL_BOOT_REASONS), "prob_crashes": len(Crashreport.CRASH_BOOT_REASONS), "other": 1, }) # Sort the expected values by date expected_report_history.sort(key=operator.itemgetter("date")) # Get the device report history statistics response = self._get_with_params(self.device_report_history_url, {"uuid": device.uuid}) # Assert that the statistics match self.assertEqual(expected_report_history, response.data)
def _assert_reports_with_same_timestamp_are_counted( self, report_type, counter_attribute_name, username_1=Dummy.USERNAMES[0], username_2=Dummy.USERNAMES[1], **kwargs): """Validate that reports with the same timestamp are counted. Reports from different devices but the same timestamp should be counted as independent reports. """ # Create a report device1 = Dummy.create_device(user=Dummy.create_user( username=username_1)) report1 = Dummy.create_report(report_type, device=device1, **kwargs) # Create a second report with the same timestamp but from another device device2 = Dummy.create_device(user=Dummy.create_user( username=username_2)) Dummy.create_report(report_type, device=device2, date=report1.date, **kwargs) # Run the command to update the database call_command("stats", "update") # Get the corresponding version instance from the database get_params = { self.unique_entry_name: getattr(report1, self.unique_entry_name) } version = self.version_class.objects.get(**get_params) # Assert that both reports are counted self.assertEqual(getattr(version, counter_attribute_name), 2)
def test_get_device_update_history_multiple_updates(self): """Test getting update history stats with multiple updates.""" # Create a device with a heartbeats and crashreport for each build # fingerprint in the dummy values device = Dummy.create_device(Dummy.create_user()) expected_update_history = [] for i, build_fingerprint in enumerate(Dummy.BUILD_FINGERPRINTS): report_date = datetime.now(tz=pytz.utc) + timedelta(days=i) Dummy.create_report( HeartBeat, device, date=report_date, build_fingerprint=build_fingerprint, ) Dummy.create_report( Crashreport, device, date=report_date, build_fingerprint=build_fingerprint, ) # Create the expected update history object expected_update_history.append({ "update_date": report_date.date(), "build_fingerprint": build_fingerprint, "max": device.id, "prob_crashes": 1, "smpl": 0, "other": 0, "heartbeats": 1, }) # Sort the expected values by update date expected_update_history.sort(key=operator.itemgetter("update_date")) # Get the device update history statistics response = self._get_with_params(self.device_update_history_url, {"uuid": device.uuid}) # Assert that the statistics match self.assertEqual(expected_update_history, response.data)
def _create_dummy_version(**kwargs): return Dummy.create_version(**kwargs)
def _create_dummy_daily_version(version, **kwargs): return Dummy.create_daily_radio_version(version, **kwargs)