Skip to content

Commit 5aeaf34

Browse files
committed
feat(r8-tests): Add R8 special formats tests
1 parent 24a5ed0 commit 5aeaf34

File tree

2 files changed

+173
-0
lines changed

2 files changed

+173
-0
lines changed

tests/r8-special-formats.NOTES.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# R8 Special Formats fixtures: failures & required behavior changes
2+
3+
Ported from upstream R8 retrace fixtures:
4+
- `src/test/java/com/android/tools/r8/retrace/stacktraces/NamedModuleStackTrace.java`
5+
- `src/test/java/com/android/tools/r8/retrace/stacktraces/AutoStackTrace.java`
6+
- `src/test/java/com/android/tools/r8/retrace/stacktraces/PGStackTrace.java`
7+
- `src/test/java/com/android/tools/r8/retrace/stacktraces/LongLineStackTrace.java`
8+
9+
This doc lists **only the failing tests** and explains, one-by-one, what would need to change in `rust-proguard` to match upstream R8 retrace behavior.
10+
11+
## `test_named_module_stacktrace`
12+
13+
- **Upstream behavior**: Recognizes and preserves Java 9+ “named module / classloader” prefixes in stack frames (e.g. `classloader.../named_module@9.0/...`) while still retracing the class/method inside.
14+
- **Current crate behavior**: Only the last frame (`at a.e(...)`) is retraced; frames with module/classloader prefixes remain unmapped and are emitted unchanged (still tab-indented).
15+
- **Why it fails**:
16+
- `parse_frame` treats everything before the last `.` as the “class”, so `classloader.../named_module@9.0/a` becomes the class name (contains `/.../`), which won’t match any mapping key (`classloader.a.b.a` / `a`).
17+
- **What needs fixing**:
18+
- **Frame parsing**: support the `StackTraceElement#toString()` module/classloader prefix forms:
19+
- `<loader>/<module>@<ver>/<class>.<method>(...)`
20+
- `<loader>//<class>.<method>(...)`
21+
- `<module>@<ver>/<class>.<method>(...)`
22+
- `<module>/<class>.<method>(...)`
23+
- Preserve the prefix when formatting, but apply mapping to the `<class>.<method>` portion.
24+
25+
## `test_auto_stacktrace`
26+
27+
- **Upstream behavior**: Parses and retraces “auto” frame locations of the form:
28+
- `at qtr.a(:com.google.android.gms@...:46)`
29+
where the file section contains multiple `:` characters and begins with `:`.
30+
- **Current crate behavior**: These frames are not parsed, so they are emitted unchanged.
31+
- **Why it fails**:
32+
- `parse_frame` currently splits the location on the **first** `:`; with a leading `:` the “file” becomes empty and line parsing fails.
33+
- More generally, the location payload requires splitting on the **last** `:` to isolate the line number.
34+
- **What needs fixing**:
35+
- **Location parsing**: parse `(...:<line>)` by splitting on the **last** `:` and allowing additional colons in the file name/payload, including a leading `:`.
36+
37+
## `test_pg_stacktrace`
38+
39+
- **Upstream behavior**: Recognizes and retraces logcat-prefixed frames (the frame appears after `AndroidRuntime:`), and also expands `(...(PG:<line>))` into a synthesized Java file name (`SectionHeaderListController.java:<line>`).
40+
- **Current crate behavior**: Treats these as plain text lines and emits them unchanged.
41+
- **Why it fails**:
42+
- `parse_frame` only matches lines that (after trimming) start with `at ` and end with `)`. In logcat format, the trimmed line begins with the timestamp and tag, not `at`.
43+
- The `PG:` file name needs special handling to synthesize a proper Java file name from the class.
44+
- **What needs fixing**:
45+
- **Format support**: detect and parse stack frames embedded in logcat lines, preserving the logcat prefix while remapping the embedded `at ...(...)` portion.
46+
- **PG source file synthesis**: treat `PG` as “unknown source file placeholder” and replace it with `<ClassSimpleName>.java` when formatting.
47+

tests/r8-special-formats.rs

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
//! Tests for R8 retrace "Special Formats" fixtures.
2+
//!
3+
//! Ported from the upstream R8 retrace fixtures in:
4+
//! - `src/test/java/com/android/tools/r8/retrace/stacktraces/NamedModuleStackTrace.java`
5+
//! - `src/test/java/com/android/tools/r8/retrace/stacktraces/AutoStackTrace.java`
6+
//! - `src/test/java/com/android/tools/r8/retrace/stacktraces/PGStackTrace.java`
7+
//! - `src/test/java/com/android/tools/r8/retrace/stacktraces/LongLineStackTrace.java`
8+
//!
9+
//! Notes:
10+
//! - Fixture mapping indentation is normalized to 4-space member indentation so it is parsed by this
11+
//! crate's Proguard mapping parser.
12+
//! - Expected stacktrace indentation is normalized to this crate's output (`" at ..."`).
13+
#![allow(clippy::unwrap_used)]
14+
15+
use proguard::{ProguardCache, ProguardMapper, ProguardMapping};
16+
17+
fn assert_remap_stacktrace(mapping: &str, input: &str, expected: &str) {
18+
let mapper = ProguardMapper::from(mapping);
19+
let actual = mapper.remap_stacktrace(input).unwrap();
20+
assert_eq!(actual.trim_end(), expected.trim_end());
21+
22+
let mapping = ProguardMapping::new(mapping.as_bytes());
23+
let mut buf = Vec::new();
24+
ProguardCache::write(&mapping, &mut buf).unwrap();
25+
let cache = ProguardCache::parse(&buf).unwrap();
26+
cache.test();
27+
28+
let actual = cache.remap_stacktrace(input).unwrap();
29+
assert_eq!(actual.trim_end(), expected.trim_end());
30+
}
31+
32+
// =============================================================================
33+
// NamedModuleStackTrace
34+
// =============================================================================
35+
36+
const NAMED_MODULE_STACKTRACE_MAPPING: &str = r#"com.android.tools.r8.Classloader -> classloader.a.b.a:
37+
com.android.tools.r8.Main -> a:
38+
101:101:void main(java.lang.String[]):1:1 -> a
39+
12:12:void foo(java.lang.String[]):2:2 -> b
40+
80:80:void bar(java.lang.String[]):3:3 -> c
41+
81:81:void baz(java.lang.String[]):4:4 -> d
42+
9:9:void qux(java.lang.String[]):5:5 -> e
43+
"#;
44+
45+
#[test]
46+
fn test_named_module_stacktrace() {
47+
let input = r#"SomeFakeException: this is a fake exception
48+
at classloader.a.b.a/named_module@9.0/a.a(:101)
49+
at classloader.a.b.a//a.b(App.java:12)
50+
at named_module@2.1/a.c(Lib.java:80)
51+
at named_module/a.d(Lib.java:81)
52+
at a.e(MyClass.java:9)
53+
"#;
54+
55+
let expected = r#"SomeFakeException: this is a fake exception
56+
at classloader.a.b.a/named_module@9.0/com.android.tools.r8.Main.main(Main.java:1)
57+
at classloader.a.b.a//com.android.tools.r8.Main.foo(Main.java:2)
58+
at named_module@2.1/com.android.tools.r8.Main.bar(Main.java:3)
59+
at named_module/com.android.tools.r8.Main.baz(Main.java:4)
60+
at com.android.tools.r8.Main.qux(Main.java:5)
61+
"#;
62+
63+
assert_remap_stacktrace(NAMED_MODULE_STACKTRACE_MAPPING, input, expected);
64+
}
65+
66+
// =============================================================================
67+
// AutoStackTrace
68+
// =============================================================================
69+
70+
const AUTO_STACKTRACE_MAPPING: &str = r#"com.android.tools.r8.AutoTest -> qtr:
71+
46:46:void foo(int):200:200 -> a
72+
17:19:void foo(int,int):23:25 -> a
73+
"#;
74+
75+
#[test]
76+
fn test_auto_stacktrace() {
77+
let input = r#"java.io.IOException: INVALID_SENDER
78+
at qtr.a(:com.google.android.gms@203915081@20.39.15 (060808-335085812):46)
79+
at qtr.a(:com.google.android.gms@203915081@20.39.15 (060808-335085812):18)
80+
"#;
81+
82+
let expected = r#"java.io.IOException: INVALID_SENDER
83+
at com.android.tools.r8.AutoTest.foo(AutoTest.java:200)
84+
at com.android.tools.r8.AutoTest.foo(AutoTest.java:24)
85+
"#;
86+
87+
assert_remap_stacktrace(AUTO_STACKTRACE_MAPPING, input, expected);
88+
}
89+
90+
// =============================================================================
91+
// PGStackTrace (logcat + "PG:" source file format)
92+
// =============================================================================
93+
94+
const PG_STACKTRACE_MAPPING: &str = r#"com.google.apps.sectionheader.SectionHeaderListController -> com.google.apps.sectionheader.SectionHeaderListController:
95+
com.google.apps.Controller -> com.google.apps.Controller:
96+
"#;
97+
98+
#[test]
99+
fn test_pg_stacktrace() {
100+
let input = r#"09-16 15:43:01.249 23316 23316 E AndroidRuntime: java.lang.NullPointerException: Attempt to invoke virtual method 'boolean com.google.android.foo(com.google.android.foo.Data$Key)' on a null object reference
101+
09-16 15:43:01.249 23316 23316 E AndroidRuntime: at com.google.apps.sectionheader.SectionHeaderListController.onToolbarStateChanged(PG:586)
102+
09-16 15:43:01.249 23316 23316 E AndroidRuntime: at com.google.apps.Controller.onToolbarStateChanged(PG:1087)
103+
"#;
104+
105+
let expected = r#"09-16 15:43:01.249 23316 23316 E AndroidRuntime: java.lang.NullPointerException: Attempt to invoke virtual method 'boolean com.google.android.foo(com.google.android.foo.Data$Key)' on a null object reference
106+
09-16 15:43:01.249 23316 23316 E AndroidRuntime: at com.google.apps.sectionheader.SectionHeaderListController.onToolbarStateChanged(SectionHeaderListController.java:586)
107+
09-16 15:43:01.249 23316 23316 E AndroidRuntime: at com.google.apps.Controller.onToolbarStateChanged(Controller.java:1087)
108+
"#;
109+
110+
assert_remap_stacktrace(PG_STACKTRACE_MAPPING, input, expected);
111+
}
112+
113+
// =============================================================================
114+
// LongLineStackTrace
115+
// =============================================================================
116+
117+
#[test]
118+
fn test_long_line_stacktrace_passthrough() {
119+
// Upstream fixture has an empty mapping and expects the single long line to be preserved.
120+
// We keep this as a smoke test that we don't crash on very long lines.
121+
let input = r#"asdf():::asfasidfsadfsafassdfsalfkaskldfasjkl908435 439593409 5309 843 5980349085 9043598 04930 5 9084389 549 385 908435098435980 4390 5890435908 4389 0509345890 23904239s909090safasiofas90f0-safads0-fas0-f-0f-0fasdf0-asswioj df jaiowj fioweoji fqiwope fopiwqej fqweiopj fwqejiof qwoepijf eiwoj fqwioepjf wiqeof jqweoifiqu t8324981qu2398 rt3289 rt2489t euhiorjg kdfgf8u432iojt3u8io432jk t3u49t 489u4389u t438u9 t43y89t 3 489t y8934t34 89ytu8943tu8 984u3 t 8u934asdf(:asdfas0dfasd0fa0)S)DFD)SDF_SD)FSDKFJlsalk;dfjaklsdf())(SDFSdfaklsdfas0d9fwe89rio223oi4rwoiuqaoiwqiowpjaklcvewtujoiwrjweof asdfjaswdj foisadj f aswioj df jaiowj fioweoji fqiwope fopiwqej fqweiopj fwqejiof qwoepijf eiwoj fqwioepjf wiqeof jqweoifiqu t8324981qu2398 rt3289 rt2489t euhiorjg kdfgf8u432iojt3u8io432jk t3u49t 489u4389u t438u9 t43y89t 3 489t y8934t34 89ytu8943tu8 984u3 t 8u93asdf(:asdfas0dfasd0fa0)S)DFD)SDF_SD)FSDKFJlsalk;dfjaklsdf())(SDFSdfaklsdfas0d9fwe89rio223oi4rwoiuqaoiwqiowpjaklcvewtujoiwrjweof asdfjaswdj foisadj f aswioj df jaiowj fioweoji fqiwope fopiwqej fqweiopj fwqejiof qwoepijf eiwoj fqwioepjf wiqeof jqweoifiqu t8324981qu2398 rt3289 rt2489t euhiorjg kdfgf8u432iojt3u8io432jk t3u49t 489u4389u t438u9 t43y89t 3 489t y8934t34 89ytu8943tu8 984u3 t 8u9344asdf(:asdfas0dfasd0fa0)S)DFD)SDF_SD)FSDKFJlsalk;dfjaklsdf())(SDFSdfaklsdfas0d9fwe89rio223oi4rwoiuqaoiwqiowpjaklcvewtujoiwrjweof asdfjaswdj foisadj f aswioj df jaiowj fioweoji fqiwope fopiwqej fqweiopj fwqejiof qwoepijf eiwoj fqwioepjf wiqeof jqweoifiqu t8324981qu2398 rt3289 rt2489t euhiorjg kdfgfasdfsdf123asdfas9dfas09df9a090asdasdfasfasdf290909009fw90wf9w0f9e09w0f90w
122+
"#;
123+
124+
let expected = input;
125+
assert_remap_stacktrace("", input, expected);
126+
}

0 commit comments

Comments
 (0)