Files
2024-02-15 17:01:50 +00:00

374 lines
11 KiB
Python

from collections.abc import Sequence
import pytest
from fixtures.sdk_crash_detection.crash_event_cocoa import (
get_crash_event,
get_crash_event_with_frames,
get_frames,
)
from sentry.testutils.helpers.options import override_options
from sentry.testutils.pytest.fixtures import django_db_all
from sentry.utils.safe import get_path, set_path
from sentry.utils.sdk_crashes.event_stripper import strip_event_data
from sentry.utils.sdk_crashes.sdk_crash_detection_config import (
SDKCrashDetectionConfig,
build_sdk_crash_detection_configs,
)
from sentry.utils.sdk_crashes.sdk_crash_detector import SDKCrashDetector
@pytest.fixture
def store_event(default_project, factories):
def inner(data):
return factories.store_event(data=data, project_id=default_project.id)
return inner
@pytest.fixture
def configs() -> Sequence[SDKCrashDetectionConfig]:
with override_options(
{
"issues.sdk_crash_detection.cocoa.project_id": 1234,
"issues.sdk_crash_detection.cocoa.sample_rate": 1.0,
"issues.sdk_crash_detection.react-native.project_id": 2,
"issues.sdk_crash_detection.react-native.sample_rate": 0.2,
"issues.sdk_crash_detection.react-native.organization_allowlist": [1],
}
):
return build_sdk_crash_detection_configs()
@pytest.fixture
def store_and_strip_event(configs, store_event):
def inner(data, config=configs[0]):
event = store_event(data=data)
return strip_event_data(event.data, SDKCrashDetector(config=config))
return inner
@django_db_all
@pytest.mark.snuba
def test_strip_event_data_keeps_allowed_keys(store_and_strip_event):
stripped_event_data = store_and_strip_event(data=get_crash_event())
keys_removed = {"tags", "user", "threads", "breadcrumbs", "environment"}
for key in keys_removed:
assert stripped_event_data.get(key) is None, f"key {key} should be removed"
keys_kept = {
"type",
"timestamp",
"platform",
"sdk",
"exception",
"contexts",
}
for key in keys_kept:
assert stripped_event_data.get(key) is not None, f"key {key} should be kept"
@django_db_all
@pytest.mark.snuba
def test_strip_event_data_strips_context(store_and_strip_event):
stripped_event_data = store_and_strip_event(data=get_crash_event())
assert stripped_event_data.get("contexts") == {
"os": {
"name": "iOS",
"version": "16.3",
"build": "20D47",
},
"device": {
"family": "iOS",
"model": "iPhone14,8",
"arch": "arm64e",
"simulator": True,
},
}
@django_db_all
@pytest.mark.snuba
def test_strip_event_data_strips_sdk(store_and_strip_event):
stripped_event_data = store_and_strip_event(data=get_crash_event())
assert stripped_event_data.get("sdk") == {
"name": "sentry.cocoa",
"version": "8.2.0",
}
@django_db_all
@pytest.mark.snuba
def test_strip_event_data_strips_value_if_not_simple_type(store_event, configs):
event = store_event(data=get_crash_event())
event.data["type"] = {"foo": "bar"}
stripped_event_data = strip_event_data(event.data, SDKCrashDetector(config=configs[0]))
assert stripped_event_data.get("type") is None
@django_db_all
@pytest.mark.snuba
def test_strip_event_data_keeps_simple_types(store_event, configs):
event = store_event(data=get_crash_event())
event.data["type"] = True
event.data["datetime"] = 0.1
event.data["timestamp"] = 1
event.data["platform"] = "cocoa"
stripped_event_data = strip_event_data(event.data, SDKCrashDetector(config=configs[0]))
assert stripped_event_data.get("type") is True
assert stripped_event_data.get("datetime") == 0.1
assert stripped_event_data.get("timestamp") == 1
assert stripped_event_data.get("platform") == "cocoa"
@django_db_all
@pytest.mark.snuba
def test_strip_event_data_keeps_simple_exception_properties(store_and_strip_event):
stripped_event_data = store_and_strip_event(data=get_crash_event())
assert get_path(stripped_event_data, "exception", "values", 0, "type") == "EXC_BAD_ACCESS"
assert get_path(stripped_event_data, "exception", "values", 0, "value") is None
@django_db_all
@pytest.mark.snuba
def test_strip_event_data_keeps_exception_mechanism(store_event, configs):
event = store_event(data=get_crash_event())
# set extra data that should be stripped
set_path(event.data, "exception", "values", 0, "mechanism", "foo", value="bar")
set_path(
event.data, "exception", "values", 0, "mechanism", "meta", "signal", "foo", value="bar"
)
set_path(
event.data,
"exception",
"values",
0,
"mechanism",
"meta",
"mach_exception",
"foo",
value="bar",
)
stripped_event_data = strip_event_data(event.data, SDKCrashDetector(config=configs[0]))
mechanism = get_path(stripped_event_data, "exception", "values", 0, "mechanism")
assert mechanism == {
"handled": False,
"type": "mach",
"meta": {
"signal": {"number": 11, "code": 0, "name": "SIGSEGV", "code_name": "SEGV_NOOP"},
"mach_exception": {
"exception": 1,
"code": 1,
"subcode": 0,
"name": "EXC_BAD_ACCESS",
},
},
}
@django_db_all
@pytest.mark.snuba
def test_set_in_app_only_for_sdk_frames(store_and_strip_event):
frames = get_frames("SentryCrashMonitor_CPPException.cpp", sentry_frame_in_app=False)
system_frame_in_app = [
{
"abs_path": "/System/Library/PrivateFrameworks/UIKitCore.framework/UIKitCore",
"in_app": True,
}
]
event_data = get_crash_event_with_frames(system_frame_in_app + list(frames))
stripped_event_data = store_and_strip_event(data=event_data)
stripped_frames = get_path(
stripped_event_data, "exception", "values", -1, "stacktrace", "frames"
)
for stripped_frame in stripped_frames[0::-1]:
assert stripped_frame["in_app"] is False
cocoa_sdk_frame = stripped_frames[-1]
assert cocoa_sdk_frame == {
"function": "SentryCrashMonitor_CPPException.cpp",
"package": "Sentry.framework",
"in_app": True,
"image_addr": "0x100304000",
}
@django_db_all
@pytest.mark.snuba
def test_strip_event_data_keeps_exception_stacktrace(store_and_strip_event):
stripped_event_data = store_and_strip_event(data=get_crash_event())
first_frame = get_path(stripped_event_data, "exception", "values", 0, "stacktrace", "frames", 0)
assert first_frame == {
"function": "function",
"raw_function": "raw_function",
"module": "module",
"abs_path": "abs_path",
"filename": "EventStripperTestFrame.swift",
"in_app": False,
"instruction_addr": "0x1a4e8f000",
"addr_mode": "0x1a4e8f000",
"symbol": "symbol",
"symbol_addr": "0x1a4e8f000",
"image_addr": "0x1a4e8f000",
"package": "/System/Library/PrivateFrameworks/UIKitCore.framework/UIKitCore",
"platform": "platform",
}
@django_db_all
@pytest.mark.snuba
def test_strip_frames(store_and_strip_event):
frames = get_frames("SentryCrashMonitor_CPPException.cpp", sentry_frame_in_app=False)
frames_kept = [
{
"abs_path": "/System/Library/PrivateFrameworks/UIKitCore.framework/UIKitCore",
},
{
"module": "/usr/lib/system/libsystem_c.dylib",
},
]
frames_stripped = [
{
"abs_path": "/System/Librry/PrivateFrameworks/UIKitCore.framework/UIKitCore",
},
{
"module": "a/usr/lib/system/libsystem_c.dylib",
},
]
event_data = get_crash_event_with_frames(frames_kept + frames_stripped + list(frames))
stripped_event_data = store_and_strip_event(data=event_data)
stripped_frames = get_path(
stripped_event_data, "exception", "values", -1, "stacktrace", "frames"
)
assert len(stripped_frames) == 10
cocoa_sdk_frames = stripped_frames[-2:]
assert cocoa_sdk_frames == [
{
"function": "__49-[SentrySwizzleWrapper swizzleSendAction:forKey:]_block_invoke_2",
"package": "Sentry.framework",
"in_app": True,
"image_addr": "0x100304000",
},
{
"function": "SentryCrashMonitor_CPPException.cpp",
"package": "Sentry.framework",
"in_app": True,
"image_addr": "0x100304000",
},
]
@django_db_all
@pytest.mark.snuba
def test_strip_frames_sdk_frames(store_and_strip_event):
frames = get_frames("SentryCrashMonitor_CPPException.cpp", sentry_frame_in_app=False)
# When statically linked the package or module is usually set to the app name
sentry_sdk_frame = frames[-1]
sentry_sdk_frame["package"] = "SomeApp"
sentry_sdk_frame["module"] = "SomeModule"
sentry_sdk_frame["abs_path"] = "SomeApp/SentryDispatchQueueWrapper.m"
event_data = get_crash_event_with_frames(frames)
stripped_event_data = store_and_strip_event(data=event_data)
stripped_frames = get_path(
stripped_event_data, "exception", "values", -1, "stacktrace", "frames"
)
cocoa_sdk_frame = stripped_frames[-1]
assert cocoa_sdk_frame == {
"function": "SentryCrashMonitor_CPPException.cpp",
"package": "Sentry.framework",
"abs_path": "Sentry.framework",
"module": "Sentry.framework",
"filename": "Sentry.framework",
"in_app": True,
"image_addr": "0x100304000",
}
@django_db_all
@pytest.mark.snuba
def test_strip_frames_sdk_frames_keep_after_matcher(store_and_strip_event, configs):
frames = get_frames("SentryCrashMonitor_CPPException.cpp", sentry_frame_in_app=False)
sentry_sdk_frame = frames[-1]
sentry_sdk_frame[
"module"
] = "Users/sentry/git-repos/sentry-react-native/dist/js/integrations/reactnative"
sentry_sdk_frame[
"filename"
] = "/Users/sentry/git-repos/sentry-react-native/dist/js/integrations/reactnative.js"
sentry_sdk_frame[
"abs_path"
] = "app:///Users/sentry/git-repos/sentry-react-native/dist/js/integrations/reactnative.js"
event_data = get_crash_event_with_frames(frames)
config = configs[1]
stripped_event_data = store_and_strip_event(data=event_data, config=config)
stripped_frames = get_path(
stripped_event_data, "exception", "values", -1, "stacktrace", "frames"
)
cocoa_sdk_frame = stripped_frames[-1]
assert cocoa_sdk_frame == {
"function": "SentryCrashMonitor_CPPException.cpp",
"module": "/sentry-react-native/dist/js/integrations/reactnative",
"filename": "/sentry-react-native/dist/js/integrations/reactnative.js",
"abs_path": "/sentry-react-native/dist/js/integrations/reactnative.js",
"package": "sentry-react-native",
"in_app": True,
"image_addr": "0x100304000",
}
@django_db_all
@pytest.mark.snuba
def test_strip_event_without_data_returns_empty_dict(store_and_strip_event):
stripped_event_data = store_and_strip_event(data={})
assert stripped_event_data == {}
@django_db_all
@pytest.mark.snuba
def test_strip_event_without_frames_returns_empty_dict(store_and_strip_event):
event_data = get_crash_event_with_frames([])
set_path(event_data, "exception", value=None)
stripped_event_data = store_and_strip_event(data=event_data)
assert stripped_event_data == {}