1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 * in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the License 10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 * or implied. See the License for the specific language governing permissions and limitations under 12 * the License. 13 */ 14 package android.jvmti.cts; 15 16 import java.io.BufferedReader; 17 import java.io.ByteArrayInputStream; 18 import java.io.ByteArrayOutputStream; 19 import java.io.InputStream; 20 import java.io.InputStreamReader; 21 import java.io.IOException; 22 import java.io.OutputStream; 23 import java.io.PrintStream; 24 import java.lang.reflect.Method; 25 import java.lang.reflect.InvocationTargetException; 26 27 import android.content.pm.PackageManager; 28 import android.util.Log; 29 30 import org.junit.After; 31 import org.junit.Before; 32 import org.junit.Test; 33 34 /** 35 * Check redefineClasses-related functionality. 36 */ 37 public class JvmtiRunTestBasedTest extends JvmtiTestBase { 38 39 private PrintStream oldOut, oldErr; 40 private ByteArrayOutputStream bufferedOut, bufferedErr; 41 42 private class TeeLogcatOutputStream extends OutputStream { 43 private OutputStream os; 44 private ByteArrayOutputStream lc_os; TeeLogcatOutputStream(OutputStream os)45 public TeeLogcatOutputStream(OutputStream os) { 46 this.lc_os = new ByteArrayOutputStream(); 47 this.os = os; 48 } write(int b)49 public void write(int b) throws IOException { 50 os.write(b); 51 if (b == (int)'\n') { 52 lc_os.flush(); 53 Log.i(mActivity.getPackageName(), "Test Output: " + lc_os.toString()); 54 lc_os.reset(); 55 } else { 56 lc_os.write(b); 57 } 58 } close()59 public void close() throws IOException { 60 flush(); 61 os.close(); 62 lc_os.close(); 63 } flush()64 public void flush() throws IOException { 65 os.flush(); 66 lc_os.flush(); 67 } 68 } 69 70 @Before setUp()71 public void setUp() throws Exception { 72 oldOut = System.out; 73 oldErr = System.err; 74 bufferedOut = new ByteArrayOutputStream(); 75 bufferedErr = new ByteArrayOutputStream(); 76 77 if (doExtraLogging()) { 78 setupExtraLogging(); 79 System.setOut(new PrintStream(new TeeLogcatOutputStream(bufferedOut), true)); 80 System.setErr(new PrintStream(new TeeLogcatOutputStream(bufferedErr), true)); 81 } else { 82 System.setOut(new PrintStream(bufferedOut, true)); 83 System.setErr(new PrintStream(bufferedErr, true)); 84 } 85 } 86 87 @After tearDown()88 public void tearDown() { 89 System.setOut(oldOut); 90 System.setErr(oldErr); 91 } 92 setupExtraLogging()93 private void setupExtraLogging() { 94 setupExtraLogging("plugin,deopt,jdwp,jit,agents,threads"); 95 } 96 setupExtraLogging(String arg)97 private native void setupExtraLogging(String arg); 98 doExtraLogging()99 protected boolean doExtraLogging() throws Exception { 100 return mActivity 101 .getPackageManager() 102 .getApplicationInfo(mActivity.getPackageName(), PackageManager.GET_META_DATA) 103 .metaData 104 .getBoolean("android.jvmti.cts.run_test.extra_logging", /*defaultValue*/false); 105 } 106 getTestNumber()107 protected int getTestNumber() throws Exception { 108 return mActivity.getPackageManager().getApplicationInfo(mActivity.getPackageName(), 109 PackageManager.GET_META_DATA).metaData.getInt("android.jvmti.cts.run_test_nr"); 110 } 111 112 // Some tests are very sensitive to state of the thread they are running on. To support this we 113 // can have tests run on newly created threads. This defaults to false. needNewThread()114 protected boolean needNewThread() throws Exception { 115 return mActivity 116 .getPackageManager() 117 .getApplicationInfo(mActivity.getPackageName(), PackageManager.GET_META_DATA) 118 .metaData 119 .getBoolean("android.jvmti.cts.needs_new_thread", /*defaultValue*/false); 120 } 121 122 @Test testRunTest()123 public void testRunTest() throws Exception { 124 final int nr = getTestNumber(); 125 126 // Load the test class. 127 Class<?> testClass = Class.forName("art.Test" + nr); 128 final Method runMethod = testClass.getDeclaredMethod("run"); 129 if (needNewThread()) { 130 // Make sure the thread the test is running on has the right name. Some tests are 131 // sensitive to this. Ideally we would also avoid having a try-catch too but that is more 132 // trouble than it's worth. 133 final Throwable[] final_throw = new Throwable[] { null }; 134 Thread main_thread = new Thread( 135 () -> { 136 try { 137 runMethod.invoke(null); 138 } catch (IllegalArgumentException e) { 139 throw new Error("Exception thrown", e); 140 } catch (InvocationTargetException e) { 141 throw new Error("Exception thrown", e); 142 } catch (NullPointerException e) { 143 throw new Error("Exception thrown", e); 144 } catch (IllegalAccessException e) { 145 throw new Error("Exception thrown", e); 146 } 147 }, "main"); 148 main_thread.setUncaughtExceptionHandler((thread, e) -> { final_throw[0] = e; }); 149 150 main_thread.start(); 151 main_thread.join(); 152 153 if (final_throw[0] != null) { 154 throw new InvocationTargetException(final_throw[0], "Remote exception occurred."); 155 } 156 } else { 157 runMethod.invoke(null); 158 } 159 160 // Load the expected txt file. 161 InputStream expectedStream = getClass().getClassLoader() 162 .getResourceAsStream("results." + nr + ".expected.txt"); 163 compare(expectedStream, bufferedOut); 164 165 if (bufferedErr.size() > 0) { 166 throw new IllegalStateException( 167 "Did not expect System.err output: " + bufferedErr.toString()); 168 } 169 } 170 171 // Very primitive diff. Doesn't do any smart things... compare(InputStream expectedStream, ByteArrayOutputStream resultStream)172 private void compare(InputStream expectedStream, ByteArrayOutputStream resultStream) 173 throws Exception { 174 // This isn't really optimal in any way. 175 BufferedReader expectedReader = new BufferedReader(new InputStreamReader(expectedStream)); 176 BufferedReader resultReader = new BufferedReader( 177 new InputStreamReader(new ByteArrayInputStream(resultStream.toByteArray()))); 178 StringBuilder resultBuilder = new StringBuilder(); 179 boolean failed = false; 180 for (;;) { 181 String expString = expectedReader.readLine(); 182 String resString = resultReader.readLine(); 183 184 if (expString == null && resString == null) { 185 // Done. 186 break; 187 } 188 189 if (expString != null && resString != null && expString.equals(resString)) { 190 resultBuilder.append(" "); 191 resultBuilder.append(expString); 192 resultBuilder.append('\n'); 193 continue; 194 } 195 196 failed = true; 197 if (expString != null) { 198 resultBuilder.append("- "); 199 resultBuilder.append(expString); 200 resultBuilder.append('\n'); 201 } 202 if (resString != null) { 203 resultBuilder.append("+ "); 204 resultBuilder.append(resString); 205 resultBuilder.append('\n'); 206 } 207 } 208 if (failed) { 209 throw new IllegalStateException(resultBuilder.toString()); 210 } 211 } 212 } 213