/* * Copyright (C) 2009 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 com.google.coretests; import java.io.File; import java.io.FileInputStream; import java.io.ObjectInputStream; import junit.framework.AssertionFailedError; import junit.framework.Protectable; import junit.framework.TestCase; import junit.framework.TestResult; import junit.textui.TestRunner; /** * A wrapper around a single test that allows to execute the test either in the * same thread, in a separate thread, or even in a different process. */ public class CoreTestRunnable implements Runnable { private static boolean IS_DALVIK = "Dalvik".equals( System.getProperty("java.vm.name")); /** * The test case we are supposed to run. */ private TestCase fTest; /** * The TestResult we need to update after the run. */ private TestResult fResult; /** * The Protectable that JUnit has created for us. */ private Protectable fProtectable; /** * Reflects whether we need to invert the test result, which is used for * treating known failures. */ private boolean fInvert; /** * Reflects whether we need to isolate the test, which means we run it in * a separate process. */ private boolean fIsolate; /** * If we are isolating the test case, this holds the process that is running * it. */ private Process fProcess; /** * Creates a new CoreTestRunnable for the given parameters. */ public CoreTestRunnable(TestCase test, TestResult result, Protectable protectable, boolean invert, boolean isolate) { this.fTest = test; this.fProtectable = protectable; this.fResult = result; this.fInvert = invert; this.fIsolate = isolate; } /** * Executes the test and stores the results. May be run from a secondary * Thread. */ public void run() { try { if (fIsolate) { runExternally(); } else { runInternally(); } if (fInvert) { fInvert = false; throw new AssertionFailedError( "@KnownFailure expected to fail, but succeeded"); } } catch (AssertionFailedError e) { if (!fInvert) { fResult.addFailure(fTest, e); } } catch (ThreadDeath e) { // don't catch ThreadDeath by accident throw e; } catch (Throwable e) { if (!fInvert) { fResult.addError(fTest, e); } } } /** * Tells the test case to stop. Only used with isolation. We need to kill * the external process in this case. */ public void stop() { if (fProcess != null) { fProcess.destroy(); } } /** * Runs the test case in the same process. This is basically what a * run-of-the-mill JUnit does, except we might also do it in a secondary * thread. */ private void runInternally() throws Throwable { fProtectable.protect(); } /** * Runs the test case in a different process. This is what we do for * isolating test cases that have side effects or do suffer from them. */ private void runExternally() throws Throwable { Throwable throwable = null; File file = File.createTempFile("isolation", ".tmp"); String program = (IS_DALVIK ? "dalvikvm" : "java") + " -classpath " + System.getProperty("java.class.path") + " -Djava.home=" + System.getProperty("java.home") + " -Duser.home=" + System.getProperty("user.home") + " -Djava.io.tmpdir=" + System.getProperty("java.io.tmpdir") + " com.google.coretests.CoreTestIsolator" + " " + fTest.getClass().getName() + " " + fTest.getName() + " " + file.getAbsolutePath(); fProcess = Runtime.getRuntime().exec(program); int result = fProcess.waitFor(); if (result != TestRunner.SUCCESS_EXIT) { try { FileInputStream fis = new FileInputStream(file); ObjectInputStream ois = new ObjectInputStream(fis); throwable = (Throwable)ois.readObject(); ois.close(); } catch (Exception ex) { throwable = new RuntimeException("Error isolating test: " + program, ex); } } file.delete(); if (throwable != null) { throw throwable; } } }