test_unreal_full.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. import zipfile
  2. from io import BytesIO
  3. from unittest.mock import patch
  4. import pytest
  5. from django.core.files.uploadedfile import SimpleUploadedFile
  6. from django.urls import reverse
  7. from sentry.lang.native.utils import STORE_CRASH_REPORTS_ALL
  8. from sentry.models.eventattachment import EventAttachment
  9. from sentry.models.files.file import File
  10. from sentry.testutils.cases import TransactionTestCase
  11. from sentry.testutils.factories import get_fixture_path
  12. from sentry.testutils.relay import RelayStoreHelper
  13. from sentry.testutils.skips import requires_kafka, requires_symbolicator
  14. from sentry.utils.safe import get_path
  15. from tests.symbolicator import normalize_native_exception
  16. # IMPORTANT:
  17. #
  18. # This test suite requires Symbolicator in order to run correctly.
  19. # Set `symbolicator.enabled: true` in your `~/.sentry/config.yml` and run `sentry devservices up`
  20. #
  21. # If you are using a local instance of Symbolicator, you need to
  22. # either change `system.url-prefix` option override inside `initialize` fixture to `system.internal-url-prefix`,
  23. # or add `127.0.0.1 host.docker.internal` entry to your `/etc/hosts`
  24. pytestmark = [requires_symbolicator, requires_kafka]
  25. def get_unreal_crash_file():
  26. return get_fixture_path("native", "unreal_crash")
  27. def get_unreal_crash_apple_file():
  28. return get_fixture_path("native", "unreal_crash_apple")
  29. class SymbolicatorUnrealIntegrationTest(RelayStoreHelper, TransactionTestCase):
  30. @pytest.fixture(autouse=True)
  31. def initialize(self, live_server):
  32. self.project.update_option("sentry:builtin_symbol_sources", [])
  33. with patch("sentry.auth.system.is_internal_ip", return_value=True), self.options(
  34. {"system.url-prefix": live_server.url}
  35. ):
  36. # Run test case
  37. yield
  38. def upload_symbols(self):
  39. url = reverse(
  40. "sentry-api-0-dsym-files",
  41. kwargs={
  42. "organization_slug": self.project.organization.slug,
  43. "project_slug": self.project.slug,
  44. },
  45. )
  46. self.login_as(user=self.user)
  47. out = BytesIO()
  48. f = zipfile.ZipFile(out, "w")
  49. f.write(get_fixture_path("native", "unreal_crash.sym"), "crash.sym")
  50. f.close()
  51. response = self.client.post(
  52. url,
  53. {
  54. "file": SimpleUploadedFile(
  55. "symbols.zip", out.getvalue(), content_type="application/zip"
  56. )
  57. },
  58. format="multipart",
  59. )
  60. assert response.status_code == 201, response.content
  61. assert len(response.json()) == 1
  62. def unreal_crash_test_impl(self, filename):
  63. self.project.update_option("sentry:store_crash_reports", STORE_CRASH_REPORTS_ALL)
  64. self.upload_symbols()
  65. # attachments feature has to be on for the files extract stick around
  66. with self.feature("organizations:event-attachments"):
  67. with open(filename, "rb") as f:
  68. event = self.post_and_retrieve_unreal(f.read())
  69. self.insta_snapshot(
  70. {
  71. "contexts": event.data.get("contexts"),
  72. "exception": {
  73. "values": [
  74. normalize_native_exception(x)
  75. for x in get_path(event.data, "exception", "values") or ()
  76. ]
  77. },
  78. "stacktrace": event.data.get("stacktrace"),
  79. "threads": event.data.get("threads"),
  80. "extra": event.data.get("extra"),
  81. "sdk": event.data.get("sdk"),
  82. }
  83. )
  84. return sorted(EventAttachment.objects.filter(event_id=event.event_id), key=lambda x: x.name)
  85. def test_unreal_crash_with_attachments(self):
  86. attachments = self.unreal_crash_test_impl(get_unreal_crash_file())
  87. assert len(attachments) == 4
  88. context, config, minidump, log = attachments
  89. assert context.name == "CrashContext.runtime-xml"
  90. context_file = File.objects.get(id=context.file_id)
  91. assert context_file.type == "unreal.context"
  92. assert context_file.checksum == "835d3e10db5d1799dc625132c819c047261ddcfb"
  93. assert config.name == "CrashReportClient.ini"
  94. config_file = File.objects.get(id=config.file_id)
  95. assert config_file.type == "event.attachment"
  96. assert config_file.checksum == "5839c750bdde8cba4d2a979ea857b8154cffdab5"
  97. assert minidump.name == "UE4Minidump.dmp"
  98. minidump_file = File.objects.get(id=minidump.file_id)
  99. assert minidump_file.type == "event.minidump"
  100. assert minidump_file.checksum == "089d9fd3b5c0cc4426339ab46ec3835e4be83c0f"
  101. assert log.name == "YetAnother.log" # Log file is named after the project
  102. log_file = File.objects.get(id=log.file_id)
  103. assert log_file.type == "unreal.logs"
  104. assert log_file.checksum == "24d1c5f75334cd0912cc2670168d593d5fe6c081"
  105. def test_unreal_apple_crash_with_attachments(self):
  106. attachments = self.unreal_crash_test_impl(get_unreal_crash_apple_file())
  107. assert len(attachments) == 6
  108. context, config, diagnostics, log, info, minidump = attachments
  109. assert context.name == "CrashContext.runtime-xml"
  110. context_file = File.objects.get(id=context.file_id)
  111. assert context_file.type == "unreal.context"
  112. assert context_file.checksum == "5d2723a7d25111645702fcbbcb8e1d038db56c6e"
  113. assert config.name == "CrashReportClient.ini"
  114. config_file = File.objects.get(id=config.file_id)
  115. assert config_file.type == "event.attachment"
  116. assert config_file.checksum == "4d6a2736e3e4969a68b7adbe197b05c171c29ea0"
  117. assert diagnostics.name == "Diagnostics.txt"
  118. diagnostics_file = File.objects.get(id=diagnostics.file_id)
  119. assert diagnostics_file.type == "event.attachment"
  120. assert diagnostics_file.checksum == "aa271bf4e307a78005410234081945352e8fb236"
  121. assert log.name == "YetAnotherMac.log" # Log file is named after the project
  122. log_file = File.objects.get(id=log.file_id)
  123. assert log_file.type == "unreal.logs"
  124. assert log_file.checksum == "735e751a8b6b943dbc0abce0e6d096f4d48a0c1e"
  125. assert info.name == "info.txt"
  126. info_file = File.objects.get(id=info.file_id)
  127. assert info_file.type == "event.attachment"
  128. assert info_file.checksum == "279b27ac5d0e6792d088e0662ce1a18413b772bc"
  129. assert minidump.name == "minidump.dmp"
  130. minidump_file = File.objects.get(id=minidump.file_id)
  131. assert minidump_file.type == "event.applecrashreport"
  132. assert minidump_file.checksum == "728d0f4b09cf5a7942da3893b6db79ac842b701a"