1 /* 2 * Copyright (C) 2009 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 package com.google.coretests; 17 18 import java.io.File; 19 import java.io.FileInputStream; 20 import java.io.ObjectInputStream; 21 22 import junit.framework.AssertionFailedError; 23 import junit.framework.Protectable; 24 import junit.framework.TestCase; 25 import junit.framework.TestResult; 26 import junit.textui.TestRunner; 27 28 /** 29 * A wrapper around a single test that allows to execute the test either in the 30 * same thread, in a separate thread, or even in a different process. 31 */ 32 public class CoreTestRunnable implements Runnable { 33 34 private static boolean IS_DALVIK = "Dalvik".equals( 35 System.getProperty("java.vm.name")); 36 37 /** 38 * The test case we are supposed to run. 39 */ 40 private TestCase fTest; 41 42 /** 43 * The TestResult we need to update after the run. 44 */ 45 private TestResult fResult; 46 47 /** 48 * The Protectable that JUnit has created for us. 49 */ 50 private Protectable fProtectable; 51 52 /** 53 * Reflects whether we need to invert the test result, which is used for 54 * treating known failures. 55 */ 56 private boolean fInvert; 57 58 /** 59 * Reflects whether we need to isolate the test, which means we run it in 60 * a separate process. 61 */ 62 private boolean fIsolate; 63 64 /** 65 * If we are isolating the test case, this holds the process that is running 66 * it. 67 */ 68 private Process fProcess; 69 70 /** 71 * Creates a new CoreTestRunnable for the given parameters. 72 */ 73 public CoreTestRunnable(TestCase test, TestResult result, 74 Protectable protectable, boolean invert, boolean isolate) { 75 76 this.fTest = test; 77 this.fProtectable = protectable; 78 this.fResult = result; 79 this.fInvert = invert; 80 this.fIsolate = isolate; 81 } 82 83 /** 84 * Executes the test and stores the results. May be run from a secondary 85 * Thread. 86 */ 87 public void run() { 88 try { 89 if (fIsolate) { 90 runExternally(); 91 } else { 92 runInternally(); 93 } 94 95 if (fInvert) { 96 fInvert = false; 97 throw new AssertionFailedError( 98 "@KnownFailure expected to fail, but succeeded"); 99 } 100 } catch (AssertionFailedError e) { 101 if (!fInvert) { 102 fResult.addFailure(fTest, e); 103 } 104 } catch (ThreadDeath e) { // don't catch ThreadDeath by accident 105 throw e; 106 } catch (Throwable e) { 107 if (!fInvert) { 108 fResult.addError(fTest, e); 109 } 110 } 111 } 112 113 /** 114 * Tells the test case to stop. Only used with isolation. We need to kill 115 * the external process in this case. 116 */ 117 public void stop() { 118 if (fProcess != null) { 119 fProcess.destroy(); 120 } 121 } 122 123 /** 124 * Runs the test case in the same process. This is basically what a 125 * run-of-the-mill JUnit does, except we might also do it in a secondary 126 * thread. 127 */ 128 private void runInternally() throws Throwable { 129 fProtectable.protect(); 130 } 131 132 /** 133 * Runs the test case in a different process. This is what we do for 134 * isolating test cases that have side effects or do suffer from them. 135 */ 136 private void runExternally() throws Throwable { 137 Throwable throwable = null; 138 139 File file = File.createTempFile("isolation", ".tmp"); 140 141 String program = (IS_DALVIK ? "dalvikvm" : "java") + 142 " -classpath " + System.getProperty("java.class.path") + 143 " -Djava.home=" + System.getProperty("java.home") + 144 " -Duser.home=" + System.getProperty("user.home") + 145 " -Djava.io.tmpdir=" + System.getProperty("java.io.tmpdir") + 146 " com.google.coretests.CoreTestIsolator" + 147 " " + fTest.getClass().getName() + 148 " " + fTest.getName() + 149 " " + file.getAbsolutePath(); 150 fProcess = Runtime.getRuntime().exec(program); 151 152 int result = fProcess.waitFor(); 153 154 if (result != TestRunner.SUCCESS_EXIT) { 155 try { 156 FileInputStream fis = new FileInputStream(file); 157 ObjectInputStream ois = new ObjectInputStream(fis); 158 throwable = (Throwable)ois.readObject(); 159 ois.close(); 160 } catch (Exception ex) { 161 throwable = new RuntimeException("Error isolating test: " + program, ex); 162 } 163 } 164 165 file.delete(); 166 167 if (throwable != null) { 168 throw throwable; 169 } 170 } 171 172 } 173