blob: a59fd3fddff1bf83fd750745c54a56032ad9f897 [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package android.jvmti.cts;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
import android.content.pm.PackageManager;
import android.util.Log;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/**
* Check redefineClasses-related functionality.
*/
public class JvmtiRunTestBasedTest extends JvmtiTestBase {
private PrintStream oldOut, oldErr;
private ByteArrayOutputStream bufferedOut, bufferedErr;
private class TeeLogcatOutputStream extends OutputStream {
private OutputStream os;
private ByteArrayOutputStream lc_os;
public TeeLogcatOutputStream(OutputStream os) {
this.lc_os = new ByteArrayOutputStream();
this.os = os;
}
public void write(int b) throws IOException {
os.write(b);
if (b == (int)'\n') {
lc_os.flush();
Log.i(mActivity.getPackageName(), "Test Output: " + lc_os.toString());
lc_os.reset();
} else {
lc_os.write(b);
}
}
public void close() throws IOException {
flush();
os.close();
lc_os.close();
}
public void flush() throws IOException {
os.flush();
lc_os.flush();
}
}
@Before
public void setUp() throws Exception {
oldOut = System.out;
oldErr = System.err;
bufferedOut = new ByteArrayOutputStream();
bufferedErr = new ByteArrayOutputStream();
if (doExtraLogging()) {
setupExtraLogging();
System.setOut(new PrintStream(new TeeLogcatOutputStream(bufferedOut), true));
System.setErr(new PrintStream(new TeeLogcatOutputStream(bufferedErr), true));
} else {
System.setOut(new PrintStream(bufferedOut, true));
System.setErr(new PrintStream(bufferedErr, true));
}
}
@After
public void tearDown() {
System.setOut(oldOut);
System.setErr(oldErr);
}
private native void setupExtraLogging();
protected boolean doExtraLogging() throws Exception {
return mActivity
.getPackageManager()
.getApplicationInfo(mActivity.getPackageName(), PackageManager.GET_META_DATA)
.metaData
.getBoolean("android.jvmti.cts.run_test.extra_logging", /*defaultValue*/false);
}
protected int getTestNumber() throws Exception {
return mActivity.getPackageManager().getApplicationInfo(mActivity.getPackageName(),
PackageManager.GET_META_DATA).metaData.getInt("android.jvmti.cts.run_test_nr");
}
// Some tests are very sensitive to state of the thread they are running on. To support this we
// can have tests run on newly created threads. This defaults to false.
protected boolean needNewThread() throws Exception {
return mActivity
.getPackageManager()
.getApplicationInfo(mActivity.getPackageName(), PackageManager.GET_META_DATA)
.metaData
.getBoolean("android.jvmti.cts.needs_new_thread", /*defaultValue*/false);
}
@Test
public void testRunTest() throws Exception {
final int nr = getTestNumber();
// Load the test class.
Class<?> testClass = Class.forName("art.Test" + nr);
final Method runMethod = testClass.getDeclaredMethod("run");
if (needNewThread()) {
// Make sure the thread the test is running on has the right name. Some tests are
// sensitive to this. Ideally we would also avoid having a try-catch too but that is more
// trouble than it's worth.
final Throwable[] final_throw = new Throwable[] { null };
Thread main_thread = new Thread(
() -> {
try {
runMethod.invoke(null);
} catch (IllegalArgumentException e) {
throw new Error("Exception thrown", e);
} catch (InvocationTargetException e) {
throw new Error("Exception thrown", e);
} catch (NullPointerException e) {
throw new Error("Exception thrown", e);
} catch (IllegalAccessException e) {
throw new Error("Exception thrown", e);
}
}, "main");
main_thread.setUncaughtExceptionHandler((thread, e) -> { final_throw[0] = e; });
main_thread.start();
main_thread.join();
if (final_throw[0] != null) {
throw new InvocationTargetException(final_throw[0], "Remote exception occurred.");
}
} else {
runMethod.invoke(null);
}
// Load the expected txt file.
InputStream expectedStream = getClass().getClassLoader()
.getResourceAsStream("results." + nr + ".expected.txt");
compare(expectedStream, bufferedOut);
if (bufferedErr.size() > 0) {
throw new IllegalStateException(
"Did not expect System.err output: " + bufferedErr.toString());
}
}
// Very primitive diff. Doesn't do any smart things...
private void compare(InputStream expectedStream, ByteArrayOutputStream resultStream)
throws Exception {
// This isn't really optimal in any way.
BufferedReader expectedReader = new BufferedReader(new InputStreamReader(expectedStream));
BufferedReader resultReader = new BufferedReader(
new InputStreamReader(new ByteArrayInputStream(resultStream.toByteArray())));
StringBuilder resultBuilder = new StringBuilder();
boolean failed = false;
for (;;) {
String expString = expectedReader.readLine();
String resString = resultReader.readLine();
if (expString == null && resString == null) {
// Done.
break;
}
if (expString != null && resString != null && expString.equals(resString)) {
resultBuilder.append(" ");
resultBuilder.append(expString);
resultBuilder.append('\n');
continue;
}
failed = true;
if (expString != null) {
resultBuilder.append("- ");
resultBuilder.append(expString);
resultBuilder.append('\n');
}
if (resString != null) {
resultBuilder.append("+ ");
resultBuilder.append(resString);
resultBuilder.append('\n');
}
}
if (failed) {
throw new IllegalStateException(resultBuilder.toString());
}
}
}