@@ -19,41 +19,40 @@ import org.mockito.kotlin.mock
1919class TombstoneParserTest {
2020 val expectedRegisters =
2121 setOf (
22+ " x0" ,
23+ " x1" ,
24+ " x2" ,
25+ " x3" ,
26+ " x4" ,
27+ " x5" ,
28+ " x6" ,
29+ " x7" ,
2230 " x8" ,
2331 " x9" ,
24- " esr" ,
25- " lr" ,
26- " pst" ,
2732 " x10" ,
28- " x12" ,
2933 " x11" ,
30- " x14 " ,
34+ " x12 " ,
3135 " x13" ,
32- " x16 " ,
36+ " x14 " ,
3337 " x15" ,
34- " sp" ,
35- " x18" ,
38+ " x16" ,
3639 " x17" ,
40+ " x18" ,
3741 " x19" ,
38- " pc" ,
39- " x21" ,
4042 " x20" ,
41- " x0" ,
42- " x23" ,
43- " x1" ,
43+ " x21" ,
4444 " x22" ,
45- " x2" ,
46- " x25" ,
47- " x3" ,
45+ " x23" ,
4846 " x24" ,
49- " x4" ,
50- " x27" ,
51- " x5" ,
47+ " x25" ,
5248 " x26" ,
53- " x6" ,
54- " x29" ,
55- " x7" ,
49+ " x27" ,
5650 " x28" ,
51+ " x29" ,
52+ " lr" ,
53+ " sp" ,
54+ " pc" ,
55+ " pst" ,
5756 )
5857
5958 val inAppIncludes = arrayListOf (" io.sentry.samples.android" )
@@ -64,84 +63,13 @@ class TombstoneParserTest {
6463 val parser = TombstoneParser (inAppIncludes, inAppExcludes, nativeLibraryDir)
6564
6665 @Test
67- fun `parses a snapshot tombstone into Event` () {
68- val tombstoneStream =
69- GZIPInputStream (TombstoneParserTest ::class .java.getResourceAsStream(" /tombstone.pb.gz" ))
70- val streamParser =
71- TombstoneParser (tombstoneStream, inAppIncludes, inAppExcludes, nativeLibraryDir)
72- val event = streamParser.parse()
73-
74- // top-level data
75- assertNotNull(event.eventId)
76- assertEquals(
77- " Fatal signal SIGSEGV (11), SEGV_MAPERR (1), pid = 21891 (io.sentry.samples.android)" ,
78- event.message!! .formatted,
79- )
80- assertEquals(" native" , event.platform)
81- assertEquals(" FATAL" , event.level!! .name)
82-
83- // exception
84- // we only track one native exception (no nesting, one crashed thread)
85- assertEquals(1 , event.exceptions!! .size)
86- val exception = event.exceptions!! [0 ]
87- assertEquals(" SIGSEGV" , exception.type)
88- assertEquals(" Segfault" , exception.value)
89- val crashedThreadId = exception.threadId
90- assertNotNull(crashedThreadId)
91-
92- val mechanism = exception.mechanism
93- assertEquals(" Tombstone" , mechanism!! .type)
94- assertEquals(false , mechanism.isHandled)
95- assertEquals(true , mechanism.synthetic)
96- assertEquals(" SIGSEGV" , mechanism.meta!! [" name" ])
97- assertEquals(11 , mechanism.meta!! [" number" ])
98- assertEquals(" SEGV_MAPERR" , mechanism.meta!! [" code_name" ])
99- assertEquals(1 , mechanism.meta!! [" code" ])
100-
101- // threads
102- assertEquals(62 , event.threads!! .size)
103- for (thread in event.threads!! ) {
104- assertNotNull(thread.id)
105- if (thread.id == crashedThreadId) {
106- assert (thread.isCrashed == true )
107- }
108- assert (thread.stacktrace!! .frames!! .isNotEmpty())
109-
110- for (frame in thread.stacktrace!! .frames!! ) {
111- assertNotNull(frame.function)
112- if (frame.platform == " java" ) {
113- // Java frames have module instead of package/instructionAddr
114- assertNotNull(frame.module)
115- } else {
116- assertNotNull(frame.`package`)
117- assertNotNull(frame.instructionAddr)
118- }
119-
120- if (thread.id == crashedThreadId) {
121- if (frame.isInApp!! ) {
122- assert (
123- frame.module?.startsWith(inAppIncludes[0 ]) == true ||
124- frame.function!! .startsWith(inAppIncludes[0 ]) ||
125- frame.`package`?.startsWith(nativeLibraryDir) == true
126- )
127- }
128- }
129- }
130-
131- assert (thread.stacktrace!! .registers!! .keys.containsAll(expectedRegisters))
132- }
66+ fun `parses tombstone into Event` () {
67+ assertTombstoneParsesCorrectly(" /tombstone.pb.gz" )
68+ }
13369
134- // debug-meta
135- assertEquals(352 , event.debugMeta!! .images!! .size)
136- for (image in event.debugMeta!! .images!! ) {
137- assertEquals(" elf" , image.type)
138- assertNotNull(image.debugId)
139- assertNotNull(image.codeId)
140- assertNotNull(image.codeFile)
141- val imageAddress = image.imageAddr!! .removePrefix(" 0x" ).toLong(16 )
142- assert (imageAddress > 0 )
143- assert (image.imageSize!! > 0 )
144- }
70+ @Test
71+ fun `parses tombstone_r8 with OAT frames into Event` () {
72+ assertTombstoneParsesCorrectly(" /tombstone_r8.pb.gz" )
14573 }
14674
14775 @Test
@@ -425,32 +353,13 @@ class TombstoneParserTest {
425353 }
426354
427355 @Test
428- fun `java frames snapshot test for all threads` () {
429- val tombstoneStream =
430- GZIPInputStream (TombstoneParserTest ::class .java.getResourceAsStream(" /tombstone.pb.gz" ))
431- val parser = TombstoneParser (tombstoneStream, inAppIncludes, inAppExcludes, nativeLibraryDir)
432- val event = parser.parse()
433-
434- val logger = mock<ILogger >()
435- val writer = StringWriter ()
436- val jsonWriter = JsonObjectWriter (writer, 100 )
437- jsonWriter.beginObject()
438- for (thread in event.threads!! ) {
439- val javaFrames = thread.stacktrace!! .frames!! .filter { it.platform == " java" }
440- if (javaFrames.isEmpty()) continue
441- jsonWriter.name(thread.id.toString())
442- jsonWriter.beginArray()
443- for (frame in javaFrames) {
444- frame.serialize(jsonWriter, logger)
445- }
446- jsonWriter.endArray()
447- }
448- jsonWriter.endObject()
449-
450- val actualJson = writer.toString()
451- val expectedJson = readGzippedResourceFile(" /tombstone_java_frames.json.gz" )
356+ fun `java frames snapshot test` () {
357+ assertJavaFramesSnapshot(" /tombstone.pb.gz" , " /tombstone_java_frames.json.gz" )
358+ }
452359
453- assertEquals(expectedJson, actualJson)
360+ @Test
361+ fun `tombstone_r8 java frames snapshot test` () {
362+ assertJavaFramesSnapshot(" /tombstone_r8.pb.gz" , " /tombstone_r8_java_frames.json.gz" )
454363 }
455364
456365 @Test
@@ -566,6 +475,104 @@ class TombstoneParserTest {
566475 return parser.parse()
567476 }
568477
478+ private fun assertTombstoneParsesCorrectly (tombstoneResource : String ) {
479+ val tombstoneStream =
480+ GZIPInputStream (TombstoneParserTest ::class .java.getResourceAsStream(tombstoneResource))
481+ val parser = TombstoneParser (tombstoneStream, inAppIncludes, inAppExcludes, nativeLibraryDir)
482+ val event = parser.parse()
483+
484+ // top-level data
485+ assertNotNull(event.eventId)
486+ assertNotNull(event.message!! .formatted)
487+ assertEquals(" native" , event.platform)
488+ assertEquals(" FATAL" , event.level!! .name)
489+
490+ // exception
491+ assertEquals(1 , event.exceptions!! .size)
492+ val exception = event.exceptions!! [0 ]
493+ assertNotNull(exception.type)
494+ val crashedThreadId = exception.threadId
495+ assertNotNull(crashedThreadId)
496+
497+ val mechanism = exception.mechanism
498+ assertNotNull(mechanism)
499+ assertEquals(" Tombstone" , mechanism.type)
500+ assertEquals(false , mechanism.isHandled)
501+ assertEquals(true , mechanism.synthetic)
502+
503+ // threads
504+ assert (event.threads!! .isNotEmpty())
505+ var hasCrashedThread = false
506+ for (thread in event.threads!! ) {
507+ assertNotNull(thread.id)
508+ if (thread.id == crashedThreadId) {
509+ assert (thread.isCrashed == true )
510+ hasCrashedThread = true
511+ }
512+ assert (thread.stacktrace!! .frames!! .isNotEmpty())
513+
514+ for (frame in thread.stacktrace!! .frames!! ) {
515+ assertNotNull(frame.function)
516+ if (frame.platform == " java" ) {
517+ assertNotNull(frame.module)
518+ assert (frame.function!! .isNotEmpty()) {
519+ " Java frame has empty function name in thread ${thread.id} "
520+ }
521+ assertNotNull(frame.isInApp)
522+ } else {
523+ assertNotNull(frame.`package`)
524+ assertNotNull(frame.instructionAddr)
525+ }
526+ }
527+
528+ assert (thread.stacktrace!! .registers!! .keys.containsAll(expectedRegisters)) {
529+ " Thread ${thread.id} is missing registers: ${expectedRegisters - thread.stacktrace!! .registers!! .keys} "
530+ }
531+ }
532+ assert (hasCrashedThread) { " No crashed thread found matching exception threadId" }
533+
534+ // debug-meta
535+ assertNotNull(event.debugMeta)
536+ assert (event.debugMeta!! .images!! .isNotEmpty())
537+ for (image in event.debugMeta!! .images!! ) {
538+ assertEquals(" elf" , image.type)
539+ assertNotNull(image.debugId)
540+ assertNotNull(image.codeId)
541+ assertNotNull(image.codeFile)
542+ val imageAddress = image.imageAddr!! .removePrefix(" 0x" ).toLong(16 )
543+ assert (imageAddress > 0 )
544+ assert (image.imageSize!! > 0 )
545+ }
546+ }
547+
548+ private fun assertJavaFramesSnapshot (tombstoneResource : String , snapshotResource : String ) {
549+ val tombstoneStream =
550+ GZIPInputStream (TombstoneParserTest ::class .java.getResourceAsStream(tombstoneResource))
551+ val parser = TombstoneParser (tombstoneStream, inAppIncludes, inAppExcludes, nativeLibraryDir)
552+ val event = parser.parse()
553+
554+ val logger = mock<ILogger >()
555+ val writer = StringWriter ()
556+ val jsonWriter = JsonObjectWriter (writer, 100 )
557+ jsonWriter.beginObject()
558+ for (thread in event.threads!! ) {
559+ val javaFrames = thread.stacktrace!! .frames!! .filter { it.platform == " java" }
560+ if (javaFrames.isEmpty()) continue
561+ jsonWriter.name(thread.id.toString())
562+ jsonWriter.beginArray()
563+ for (frame in javaFrames) {
564+ frame.serialize(jsonWriter, logger)
565+ }
566+ jsonWriter.endArray()
567+ }
568+ jsonWriter.endObject()
569+
570+ val actualJson = writer.toString()
571+ val expectedJson = readGzippedResourceFile(snapshotResource)
572+
573+ assertEquals(expectedJson, actualJson)
574+ }
575+
569576 private fun serializeDebugMeta (debugMeta : DebugMeta ): String {
570577 val logger = mock<ILogger >()
571578 val writer = StringWriter ()
0 commit comments