blob: ac59231317aa6599ac42783d74e5b81d24596a33 [file] [log] [blame]
Mitja Nikolaus03e412b2018-09-18 17:50:15 +02001"""Utility functions shared by all crashreports tests."""
2
Mitja Nikolaus6e118472018-10-04 11:15:29 +02003import os
Mitja Nikolaus03e412b2018-09-18 17:50:15 +02004from typing import Optional
5
6from django.contrib.auth.models import User
7from django.urls import reverse
8from rest_framework import status
9from rest_framework.test import APITestCase, APIClient
10
11from crashreports.models import Crashreport
12
13
14class InvalidCrashTypeError(BaseException):
15 """Invalid crash type encountered.
16
17 The valid crash type values (strings) are:
18 - 'crash';
19 - 'smpl';
20 - 'other'.
21
22 Args:
23 - crash_type: The invalid crash type.
24 """
25
26 def __init__(self, crash_type):
27 """Initialise the exception using the crash type to build a message.
28
29 Args:
30 crash_type: The invalid crash type.
31 """
32 super(InvalidCrashTypeError, self).__init__(
33 "{} is not a valid crash type".format(crash_type)
34 )
35
36
37class Dummy:
38 """Dummy values for devices, heartbeats and crashreports."""
39
40 DEFAULT_DUMMY_DEVICE_REGISTER_VALUES = {
41 "board_date": "2015-12-15T01:23:45Z",
42 "chipset": "Qualcomm MSM8974PRO-AA",
43 }
44
45 DEFAULT_DUMMY_HEARTBEAT_VALUES = {
46 "uuid": None,
47 "app_version": 10100,
48 "uptime": (
49 "up time: 16 days, 21:49:56, idle time: 5 days, 20:55:04, "
50 "sleep time: 10 days, 20:46:27"
51 ),
52 "build_fingerprint": (
53 "Fairphone/FP2/FP2:6.0.1/FP2-gms-18.03.1/FP2-gms-18.03.1:user/"
54 "release-keys"
55 ),
56 "radio_version": "4437.1-FP2-0-08",
57 "date": "2018-03-19T09:58:30.386Z",
58 }
59
60 DEFAULT_DUMMY_CRASHREPORTS_VALUES = DEFAULT_DUMMY_HEARTBEAT_VALUES.copy()
61 DEFAULT_DUMMY_CRASHREPORTS_VALUES.update(
62 {
63 "is_fake_report": 0,
64 "boot_reason": "why?",
65 "power_on_reason": "it was powered on",
66 "power_off_reason": "something happened and it went off",
67 }
68 )
69
70 CRASH_TYPE_TO_BOOT_REASON_MAP = {
71 "crash": Crashreport.BOOT_REASON_KEYBOARD_POWER_ON,
72 "smpl": Crashreport.BOOT_REASON_RTC_ALARM,
73 "other": "whatever",
74 }
75
Mitja Nikolaus6e118472018-10-04 11:15:29 +020076 DEFAULT_DUMMY_LOG_FILE_PATH = os.path.join(
77 "resources", "test", "test_logfile.zip"
78 )
79
Mitja Nikolaus03e412b2018-09-18 17:50:15 +020080 @staticmethod
81 def _update_copy(original, update):
82 """Merge fields of update into a copy of original."""
83 data = original.copy()
84 data.update(update)
85 return data
86
87 @staticmethod
88 def device_register_data(**kwargs):
89 """Return the data required to register a device.
90
91 Use the values passed as keyword arguments or default to the ones
92 from `Dummy.DEFAULT_DUMMY_DEVICE_REGISTER_VALUES`.
93 """
94 return Dummy._update_copy(
95 Dummy.DEFAULT_DUMMY_DEVICE_REGISTER_VALUES, kwargs
96 )
97
98 @staticmethod
99 def heartbeat_data(**kwargs):
100 """Return the data required to create a heartbeat.
101
102 Use the values passed as keyword arguments or default to the ones
103 from `Dummy.DEFAULT_DUMMY_HEARTBEAT_VALUES`.
104 """
105 return Dummy._update_copy(Dummy.DEFAULT_DUMMY_HEARTBEAT_VALUES, kwargs)
106
107 @staticmethod
108 def crashreport_data(report_type: Optional[str] = None, **kwargs):
109 """Return the data required to create a crashreport.
110
111 Use the values passed as keyword arguments or default to the ones
112 from `Dummy.DEFAULT_DUMMY_CRASHREPORTS_VALUES`.
113
114 Args:
115 report_type: A valid value from
116 `Dummy.CRASH_TYPE_TO_BOOT_REASON_MAP.keys()` that will
117 define the boot reason if not explicitly defined in the
118 keyword arguments already.
119 """
120 data = Dummy._update_copy(
121 Dummy.DEFAULT_DUMMY_CRASHREPORTS_VALUES, kwargs
122 )
123 if report_type and "boot_reason" not in kwargs:
124 if report_type not in Dummy.CRASH_TYPE_TO_BOOT_REASON_MAP:
125 raise InvalidCrashTypeError(report_type)
126 data["boot_reason"] = Dummy.CRASH_TYPE_TO_BOOT_REASON_MAP.get(
127 report_type
128 )
129 return data
130
131
132class HiccupCrashreportsAPITestCase(APITestCase):
133 """Base class that offers a device registration method."""
134
135 REGISTER_DEVICE_URL = "api_v1_register_device"
136
137 def setUp(self):
138 """Create an admin user for accessing the API.
139
140 The APIClient that can be used to make authenticated requests to the
141 server is stored in self.admin.
142 """
143 admin_user = User.objects.create_superuser(
144 "somebody", "somebody@example.com", "thepassword"
145 )
146 self.admin = APIClient()
147 self.admin.force_authenticate(admin_user)
148
149 def _register_device(self, **kwargs):
150 """Register a new device.
151
152 Arguments:
153 **kwargs: The data to pass the dummy data creation
154 method `Dummy.device_register_data`.
155 Returns:
156 (UUID, APIClient, str): The uuid of the new device as well as an
157 authentication token and the associated user with credentials.
158
159 """
160 data = Dummy.device_register_data(**kwargs)
161 response = self.client.post(reverse(self.REGISTER_DEVICE_URL), data)
162 self.assertEqual(response.status_code, status.HTTP_200_OK)
163
164 uuid = response.data["uuid"]
165 token = response.data["token"]
166 user = APIClient()
167 user.credentials(HTTP_AUTHORIZATION="Token " + token)
168
169 return uuid, user, token