Home | History | Annotate | Download | only in coretests
      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