Skip to content

Commit 1a7184b

Browse files
authored
test: assert crash-level fatal in integration tests (#856)
1 parent 2aa321b commit 1a7184b

File tree

5 files changed

+94
-29
lines changed

5 files changed

+94
-29
lines changed

.github/workflows/ci.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ jobs:
121121
submodules: recursive
122122
- uses: actions/setup-python@v4
123123
with:
124-
python-version: '3.10'
124+
python-version: '3.11'
125125
cache: 'pip'
126126

127127
- name: Installing Linux Dependencies

tests/assertions.py

+66-9
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
import datetime
21
import email
32
import gzip
43
import platform
54
import re
65
import sys
6+
from dataclasses import dataclass
7+
from datetime import datetime
8+
9+
import msgpack
710

811
from .conditions import is_android
912

@@ -161,7 +164,7 @@ def assert_minidump(envelope):
161164
assert minidump.payload.bytes.startswith(b"MDMP")
162165

163166

164-
def assert_timestamp(ts, now=datetime.datetime.utcnow()):
167+
def assert_timestamp(ts, now=datetime.utcnow()):
165168
assert ts[:11] == now.isoformat()[:11]
166169

167170

@@ -176,6 +179,14 @@ def assert_event(envelope):
176179
assert_timestamp(event["timestamp"])
177180

178181

182+
def assert_breakpad_crash(envelope):
183+
event = envelope.get_event()
184+
expected = {
185+
"level": "fatal",
186+
}
187+
assert_matches(event, expected)
188+
189+
179190
def assert_exception(envelope):
180191
event = envelope.get_event()
181192
exception = {
@@ -186,7 +197,7 @@ def assert_exception(envelope):
186197
assert_timestamp(event["timestamp"])
187198

188199

189-
def assert_crash(envelope):
200+
def assert_inproc_crash(envelope):
190201
event = envelope.get_event()
191202
assert_matches(event, {"level": "fatal"})
192203
# depending on the unwinder, we currently don’t get any stack frames from
@@ -213,16 +224,62 @@ def assert_no_before_send(envelope):
213224
assert ("adapted_by", "before_send") not in event.items()
214225

215226

227+
@dataclass(frozen=True)
228+
class CrashpadAttachments:
229+
event: dict
230+
breadcrumb1: list
231+
breadcrumb2: list
232+
233+
234+
def _unpack_breadcrumbs(payload):
235+
unpacker = msgpack.Unpacker()
236+
unpacker.feed(payload)
237+
return [unpacked for unpacked in unpacker]
238+
239+
240+
def _load_crashpad_attachments(msg):
241+
event = {}
242+
breadcrumb1 = []
243+
breadcrumb2 = []
244+
for part in msg.walk():
245+
match part.get_filename():
246+
case "__sentry-event":
247+
event = msgpack.unpackb(part.get_payload(decode=True))
248+
case "__sentry-breadcrumb1":
249+
breadcrumb1 = _unpack_breadcrumbs(part.get_payload(decode=True))
250+
case "__sentry-breadcrumb2":
251+
breadcrumb2 = _unpack_breadcrumbs(part.get_payload(decode=True))
252+
253+
return CrashpadAttachments(event, breadcrumb1, breadcrumb2)
254+
255+
256+
def is_valid_timestamp(timestamp):
257+
try:
258+
datetime.fromisoformat(timestamp)
259+
return True
260+
except ValueError:
261+
return False
262+
263+
264+
def _validate_breadcrumb_seq(seq, breadcrumb_func):
265+
for i in seq:
266+
breadcrumb = breadcrumb_func(i)
267+
assert breadcrumb["message"] == str(i)
268+
assert is_valid_timestamp(breadcrumb["timestamp"])
269+
270+
216271
def assert_crashpad_upload(req):
217272
multipart = gzip.decompress(req.get_data())
218273
msg = email.message_from_bytes(bytes(str(req.headers), encoding="utf8") + multipart)
219-
files = [part.get_filename() for part in msg.walk()]
274+
attachments = _load_crashpad_attachments(msg)
275+
276+
if len(attachments.breadcrumb1) > 3:
277+
_validate_breadcrumb_seq(range(97), lambda i: attachments.breadcrumb1[3 + i])
278+
_validate_breadcrumb_seq(
279+
range(97, 101), lambda i: attachments.breadcrumb2[i - 97]
280+
)
220281

221-
# TODO:
222-
# Actually assert that we get a correct event/breadcrumbs payload
223-
assert "__sentry-breadcrumb1" in files
224-
assert "__sentry-breadcrumb2" in files
225-
assert "__sentry-event" in files
282+
assert attachments.event["level"] == "fatal"
226283

227284
assert any(
228285
b'name="upload_file_minidump"' in part.as_bytes()

tests/requirements.txt

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
black==23.3.0
2-
pytest==7.2.2
3-
pytest-httpserver==1.0.6
2+
pytest==7.4.0
3+
pytest-httpserver==1.0.8
4+
msgpack==1.0.5

tests/test_integration_http.py

+10-6
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,25 @@
1-
import pytest
1+
import itertools
2+
import json
23
import os
34
import time
4-
import itertools
55
import uuid
6-
import json
6+
7+
import pytest
8+
79
from . import make_dsn, run, Envelope
8-
from .conditions import has_http, has_breakpad, has_files
910
from .assertions import (
1011
assert_attachment,
1112
assert_meta,
1213
assert_breadcrumb,
1314
assert_stacktrace,
1415
assert_event,
1516
assert_exception,
16-
assert_crash,
17+
assert_inproc_crash,
1718
assert_session,
1819
assert_minidump,
20+
assert_breakpad_crash,
1921
)
22+
from .conditions import has_http, has_breakpad, has_files
2023

2124
pytestmark = pytest.mark.skipif(not has_http, reason="tests need http")
2225

@@ -233,7 +236,7 @@ def test_inproc_crash_http(cmake, httpserver):
233236
assert_breadcrumb(envelope)
234237
assert_attachment(envelope)
235238

236-
assert_crash(envelope)
239+
assert_inproc_crash(envelope)
237240

238241

239242
def test_inproc_reinstall(cmake, httpserver):
@@ -318,6 +321,7 @@ def test_breakpad_crash_http(cmake, httpserver):
318321
assert_breadcrumb(envelope)
319322
assert_attachment(envelope)
320323

324+
assert_breakpad_crash(envelope)
321325
assert_minidump(envelope)
322326

323327

tests/test_integration_stdout.py

+14-11
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@
1212
assert_breadcrumb,
1313
assert_stacktrace,
1414
assert_event,
15-
assert_crash,
15+
assert_inproc_crash,
1616
assert_minidump,
17-
assert_timestamp,
1817
assert_before_send,
1918
assert_no_before_send,
2019
assert_crash_timestamp,
20+
assert_breakpad_crash,
2121
)
2222
from .conditions import has_breakpad, has_files
2323

@@ -92,9 +92,9 @@ def test_multi_process(cmake):
9292

9393
# while the processes are running, we expect two runs
9494
runs = [
95-
run
96-
for run in os.listdir(os.path.join(cwd, ".sentry-native"))
97-
if run.endswith(".run")
95+
db_run
96+
for db_run in os.listdir(os.path.join(cwd, ".sentry-native"))
97+
if db_run.endswith(".run")
9898
]
9999
assert len(runs) == 2
100100

@@ -108,9 +108,9 @@ def test_multi_process(cmake):
108108
subprocess.run([cmd], cwd=cwd)
109109

110110
runs = [
111-
run
112-
for run in os.listdir(os.path.join(cwd, ".sentry-native"))
113-
if run.endswith(".run") or run.endswith(".lock")
111+
db_run
112+
for db_run in os.listdir(os.path.join(cwd, ".sentry-native"))
113+
if db_run.endswith(".run") or db_run.endswith(".lock")
114114
]
115115
assert len(runs) == 0
116116

@@ -136,7 +136,7 @@ def test_inproc_crash_stdout(cmake):
136136
assert_meta(envelope, integration="inproc")
137137
assert_breadcrumb(envelope)
138138
assert_attachment(envelope)
139-
assert_crash(envelope)
139+
assert_inproc_crash(envelope)
140140

141141

142142
def test_inproc_crash_stdout_before_send(cmake):
@@ -148,7 +148,7 @@ def test_inproc_crash_stdout_before_send(cmake):
148148
assert_meta(envelope, integration="inproc")
149149
assert_breadcrumb(envelope)
150150
assert_attachment(envelope)
151-
assert_crash(envelope)
151+
assert_inproc_crash(envelope)
152152
assert_before_send(envelope)
153153

154154

@@ -175,7 +175,7 @@ def test_inproc_crash_stdout_before_send_and_on_crash(cmake):
175175
assert_meta(envelope, integration="inproc")
176176
assert_breadcrumb(envelope)
177177
assert_attachment(envelope)
178-
assert_crash(envelope)
178+
assert_inproc_crash(envelope)
179179

180180

181181
@pytest.mark.skipif(not has_breakpad, reason="test needs breakpad backend")
@@ -189,6 +189,7 @@ def test_breakpad_crash_stdout(cmake):
189189
assert_breadcrumb(envelope)
190190
assert_attachment(envelope)
191191
assert_minidump(envelope)
192+
assert_breakpad_crash(envelope)
192193

193194

194195
@pytest.mark.skipif(not has_breakpad, reason="test needs breakpad backend")
@@ -203,6 +204,7 @@ def test_breakpad_crash_stdout_before_send(cmake):
203204
assert_attachment(envelope)
204205
assert_minidump(envelope)
205206
assert_before_send(envelope)
207+
assert_breakpad_crash(envelope)
206208

207209

208210
@pytest.mark.skipif(not has_breakpad, reason="test needs breakpad backend")
@@ -230,3 +232,4 @@ def test_breakpad_crash_stdout_before_send_and_on_crash(cmake):
230232
assert_meta(envelope, integration="breakpad")
231233
assert_breadcrumb(envelope)
232234
assert_attachment(envelope)
235+
assert_breakpad_crash(envelope)

0 commit comments

Comments
 (0)