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