Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: assert crash-level fatal in integration tests #856

Merged
merged 1 commit into from
Jun 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ jobs:
submodules: recursive
- uses: actions/setup-python@v4
with:
python-version: '3.10'
python-version: '3.11'
cache: 'pip'

- name: Installing Linux Dependencies
Expand Down
75 changes: 66 additions & 9 deletions tests/assertions.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import datetime
import email
import gzip
import platform
import re
import sys
from dataclasses import dataclass
from datetime import datetime

import msgpack

from .conditions import is_android

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


def assert_timestamp(ts, now=datetime.datetime.utcnow()):
def assert_timestamp(ts, now=datetime.utcnow()):
assert ts[:11] == now.isoformat()[:11]


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


def assert_breakpad_crash(envelope):
event = envelope.get_event()
expected = {
"level": "fatal",
}
assert_matches(event, expected)


def assert_exception(envelope):
event = envelope.get_event()
exception = {
Expand All @@ -186,7 +197,7 @@ def assert_exception(envelope):
assert_timestamp(event["timestamp"])


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


@dataclass(frozen=True)
class CrashpadAttachments:
event: dict
breadcrumb1: list
breadcrumb2: list


def _unpack_breadcrumbs(payload):
unpacker = msgpack.Unpacker()
unpacker.feed(payload)
return [unpacked for unpacked in unpacker]


def _load_crashpad_attachments(msg):
event = {}
breadcrumb1 = []
breadcrumb2 = []
for part in msg.walk():
match part.get_filename():
case "__sentry-event":
event = msgpack.unpackb(part.get_payload(decode=True))
case "__sentry-breadcrumb1":
breadcrumb1 = _unpack_breadcrumbs(part.get_payload(decode=True))
case "__sentry-breadcrumb2":
breadcrumb2 = _unpack_breadcrumbs(part.get_payload(decode=True))

return CrashpadAttachments(event, breadcrumb1, breadcrumb2)


def is_valid_timestamp(timestamp):
try:
datetime.fromisoformat(timestamp)
return True
except ValueError:
return False


def _validate_breadcrumb_seq(seq, breadcrumb_func):
for i in seq:
breadcrumb = breadcrumb_func(i)
assert breadcrumb["message"] == str(i)
assert is_valid_timestamp(breadcrumb["timestamp"])


def assert_crashpad_upload(req):
multipart = gzip.decompress(req.get_data())
msg = email.message_from_bytes(bytes(str(req.headers), encoding="utf8") + multipart)
files = [part.get_filename() for part in msg.walk()]
attachments = _load_crashpad_attachments(msg)

if len(attachments.breadcrumb1) > 3:
_validate_breadcrumb_seq(range(97), lambda i: attachments.breadcrumb1[3 + i])
_validate_breadcrumb_seq(
range(97, 101), lambda i: attachments.breadcrumb2[i - 97]
)

# TODO:
# Actually assert that we get a correct event/breadcrumbs payload
assert "__sentry-breadcrumb1" in files
assert "__sentry-breadcrumb2" in files
assert "__sentry-event" in files
assert attachments.event["level"] == "fatal"

assert any(
b'name="upload_file_minidump"' in part.as_bytes()
Expand Down
5 changes: 3 additions & 2 deletions tests/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
black==23.3.0
pytest==7.2.2
pytest-httpserver==1.0.6
pytest==7.4.0
pytest-httpserver==1.0.8
msgpack==1.0.5
16 changes: 10 additions & 6 deletions tests/test_integration_http.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
import pytest
import itertools
import json
import os
import time
import itertools
import uuid
import json

import pytest

from . import make_dsn, run, Envelope
from .conditions import has_http, has_breakpad, has_files
from .assertions import (
assert_attachment,
assert_meta,
assert_breadcrumb,
assert_stacktrace,
assert_event,
assert_exception,
assert_crash,
assert_inproc_crash,
assert_session,
assert_minidump,
assert_breakpad_crash,
)
from .conditions import has_http, has_breakpad, has_files

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

Expand Down Expand Up @@ -233,7 +236,7 @@ def test_inproc_crash_http(cmake, httpserver):
assert_breadcrumb(envelope)
assert_attachment(envelope)

assert_crash(envelope)
assert_inproc_crash(envelope)


def test_inproc_reinstall(cmake, httpserver):
Expand Down Expand Up @@ -318,6 +321,7 @@ def test_breakpad_crash_http(cmake, httpserver):
assert_breadcrumb(envelope)
assert_attachment(envelope)

assert_breakpad_crash(envelope)
assert_minidump(envelope)


Expand Down
25 changes: 14 additions & 11 deletions tests/test_integration_stdout.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@
assert_breadcrumb,
assert_stacktrace,
assert_event,
assert_crash,
assert_inproc_crash,
assert_minidump,
assert_timestamp,
assert_before_send,
assert_no_before_send,
assert_crash_timestamp,
assert_breakpad_crash,
)
from .conditions import has_breakpad, has_files

Expand Down Expand Up @@ -92,9 +92,9 @@ def test_multi_process(cmake):

# while the processes are running, we expect two runs
runs = [
run
for run in os.listdir(os.path.join(cwd, ".sentry-native"))
if run.endswith(".run")
db_run
for db_run in os.listdir(os.path.join(cwd, ".sentry-native"))
if db_run.endswith(".run")
]
assert len(runs) == 2

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

runs = [
run
for run in os.listdir(os.path.join(cwd, ".sentry-native"))
if run.endswith(".run") or run.endswith(".lock")
db_run
for db_run in os.listdir(os.path.join(cwd, ".sentry-native"))
if db_run.endswith(".run") or db_run.endswith(".lock")
]
assert len(runs) == 0

Expand All @@ -136,7 +136,7 @@ def test_inproc_crash_stdout(cmake):
assert_meta(envelope, integration="inproc")
assert_breadcrumb(envelope)
assert_attachment(envelope)
assert_crash(envelope)
assert_inproc_crash(envelope)


def test_inproc_crash_stdout_before_send(cmake):
Expand All @@ -148,7 +148,7 @@ def test_inproc_crash_stdout_before_send(cmake):
assert_meta(envelope, integration="inproc")
assert_breadcrumb(envelope)
assert_attachment(envelope)
assert_crash(envelope)
assert_inproc_crash(envelope)
assert_before_send(envelope)


Expand All @@ -175,7 +175,7 @@ def test_inproc_crash_stdout_before_send_and_on_crash(cmake):
assert_meta(envelope, integration="inproc")
assert_breadcrumb(envelope)
assert_attachment(envelope)
assert_crash(envelope)
assert_inproc_crash(envelope)


@pytest.mark.skipif(not has_breakpad, reason="test needs breakpad backend")
Expand All @@ -189,6 +189,7 @@ def test_breakpad_crash_stdout(cmake):
assert_breadcrumb(envelope)
assert_attachment(envelope)
assert_minidump(envelope)
assert_breakpad_crash(envelope)


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


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