1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.tools.r8; 18 19 import java.io.BufferedReader; 20 import java.io.InputStream; 21 import java.io.InputStreamReader; 22 import java.nio.charset.StandardCharsets; 23 import java.util.List; 24 import java.util.stream.Collectors; 25 import org.junit.Assert; 26 import org.junit.Test; 27 28 public class CheckRetracedStacktraceTest { 29 30 private static final String FRAME_PREFIX = 31 " at com.example.android.helloactivitywithr8.HelloActivityWithR8."; 32 getResourceLines(String resource)33 private List<String> getResourceLines(String resource) throws Exception { 34 try (InputStream is = getClass().getResourceAsStream(resource)) { 35 return new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8)) 36 .lines() 37 .collect(Collectors.toList()); 38 } 39 } 40 onErrorDebugInfo()41 private String onErrorDebugInfo() throws Exception { 42 StringBuilder builder = new StringBuilder("\nAdditional debug info:\n"); 43 appendResourceContent(builder, "dexdump.txt"); 44 appendResourceContent(builder, "proguard_dictionary"); 45 appendResourceContent(builder, "stacktrace.txt"); 46 appendResourceContent(builder, "retraced-stacktrace.txt"); 47 return builder.toString(); 48 } 49 appendResourceContent(StringBuilder builder, String resource)50 private void appendResourceContent(StringBuilder builder, String resource) throws Exception { 51 builder.append("==== ").append(resource).append('\n'); 52 getResourceLines("/" + resource).forEach(l -> builder.append(l).append('\n')); 53 } 54 55 @Test checkRawStacktrace()56 public void checkRawStacktrace() throws Exception { 57 String errorInfo = onErrorDebugInfo(); 58 59 List<String> lines = getResourceLines("/stacktrace.txt"); 60 // In release builds a single frame is present, in debug builds two. 61 Assert.assertTrue(errorInfo, 1 < lines.size() && lines.size() <= 3); 62 Assert.assertEquals(errorInfo, "java.lang.RuntimeException: error", lines.get(0)); 63 // The frame lines "at line" is the qualified method and we don't check build hash 64 // and PC to allow minor changes to the test app and compiler without breaking this test. 65 for (int i = 1; i < lines.size(); i++) { 66 String frameLine = lines.get(i); 67 int sourceFileStart = frameLine.indexOf('('); 68 int lineNumberSeparator = frameLine.indexOf(':'); 69 int lineNumberEnd = frameLine.lastIndexOf(')'); 70 int hashInfoSeparator = frameLine.lastIndexOf(' ', lineNumberSeparator); 71 Assert.assertTrue(errorInfo, frameLine.startsWith(FRAME_PREFIX)); 72 Assert.assertEquals( 73 errorInfo, "(go/retraceme", frameLine.substring(sourceFileStart, hashInfoSeparator)); 74 String lineNumberString = frameLine.substring(lineNumberSeparator + 1, lineNumberEnd); 75 try { 76 int lineNumber = Integer.parseInt(lineNumberString); 77 } catch (NumberFormatException e) { 78 Assert.fail("Invalid line number: " + lineNumberString + errorInfo); 79 } 80 } 81 } 82 83 @Test checkRetracedStacktrace()84 public void checkRetracedStacktrace() throws Exception { 85 String errorInfo = onErrorDebugInfo(); 86 87 // Prefix is the qualified class on each line, suffix does not check line numbers to 88 // allow minor changes to the test app without breaking this test. 89 String suffix = "(HelloActivityWithR8.java"; 90 91 List<String> lines = getResourceLines("/retraced-stacktrace.txt"); 92 int expectedLines = 3; 93 Assert.assertEquals( 94 "Expected " 95 + expectedLines 96 + " lines, got: \n=====\n" 97 + String.join("\n", lines) 98 + "\n=====" 99 + errorInfo, 100 expectedLines, 101 lines.size()); 102 Assert.assertEquals(errorInfo, "java.lang.RuntimeException: error", lines.get(0)); 103 String line1 = lines.get(1); 104 Assert.assertEquals( 105 errorInfo, FRAME_PREFIX + "getView" + suffix, line1.substring(0, line1.indexOf(':'))); 106 String line2 = lines.get(2); 107 Assert.assertEquals( 108 errorInfo, FRAME_PREFIX + "onCreate" + suffix, line2.substring(0, line2.indexOf(':'))); 109 } 110 } 111