summaryrefslogtreecommitdiffstats
path: root/test-runner
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2008-12-17 18:05:43 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2008-12-17 18:05:43 -0800
commitf013e1afd1e68af5e3b868c26a653bbfb39538f8 (patch)
tree7ad6c8fd9c7b55f4b4017171dec1cb760bbd26bf /test-runner
parente70cfafe580c6f2994c4827cd8a534aabf3eb05c (diff)
downloadframeworks_base-f013e1afd1e68af5e3b868c26a653bbfb39538f8.zip
frameworks_base-f013e1afd1e68af5e3b868c26a653bbfb39538f8.tar.gz
frameworks_base-f013e1afd1e68af5e3b868c26a653bbfb39538f8.tar.bz2
Code drop from //branches/cupcake/...@124589
Diffstat (limited to 'test-runner')
-rw-r--r--test-runner/android/test/ActivityInstrumentationTestCase.java5
-rw-r--r--test-runner/android/test/ActivityInstrumentationTestCase2.java174
-rw-r--r--test-runner/android/test/AndroidTestRunner.java8
-rw-r--r--test-runner/android/test/DatabaseTestUtils.java57
-rw-r--r--test-runner/android/test/InstrumentationCoreTestRunner.java43
-rw-r--r--test-runner/android/test/InstrumentationTestRunner.java387
-rwxr-xr-xtest-runner/android/test/NoExecTestResult.java39
-rw-r--r--test-runner/android/test/ProviderTestCase.java10
-rw-r--r--test-runner/android/test/ProviderTestCase2.java82
-rw-r--r--test-runner/android/test/mock/MockContentResolver.java7
-rw-r--r--test-runner/android/test/mock/MockPackageManager.java8
-rw-r--r--test-runner/android/test/suitebuilder/TestMethod.java14
-rw-r--r--test-runner/android/test/suitebuilder/TestPredicates.java6
-rw-r--r--test-runner/android/test/suitebuilder/TestSuiteBuilder.java48
14 files changed, 783 insertions, 105 deletions
diff --git a/test-runner/android/test/ActivityInstrumentationTestCase.java b/test-runner/android/test/ActivityInstrumentationTestCase.java
index b198a7a..e5a9991 100644
--- a/test-runner/android/test/ActivityInstrumentationTestCase.java
+++ b/test-runner/android/test/ActivityInstrumentationTestCase.java
@@ -27,7 +27,12 @@ import java.lang.reflect.Field;
* automatically here by {@link #setUp} and {@link #tearDown}.
*
* <p>If you prefer an isolated unit test, see {@link android.test.ActivityUnitTestCase}.
+ *
+ * @deprecated new tests should be written using
+ * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
+ * configuring the Activity under test
*/
+@Deprecated
public abstract class ActivityInstrumentationTestCase<T extends Activity>
extends ActivityTestCase {
String mPackage;
diff --git a/test-runner/android/test/ActivityInstrumentationTestCase2.java b/test-runner/android/test/ActivityInstrumentationTestCase2.java
new file mode 100644
index 0000000..7a84eca
--- /dev/null
+++ b/test-runner/android/test/ActivityInstrumentationTestCase2.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2008 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.test;
+
+import android.app.Activity;
+import android.content.Intent;
+
+import java.lang.reflect.Method;
+
+/**
+ * This class provides functional testing of a single activity. The activity under test will
+ * be created using the system infrastructure (by calling InstrumentationTestCase.launchActivity())
+ * and you will then be able to manipulate your Activity directly.
+ *
+ * <p>Other options supported by this test case include:
+ * <ul>
+ * <li>You can run any test method on the UI thread (see {@link android.test.UiThreadTest}).</li>
+ * <li>You can inject custom Intents into your Activity (see
+ * {@link #setActivityIntent(Intent)}).</li>
+ * </ul>
+ *
+ * <p>This class replaces {@link android.test.ActivityInstrumentationTestCase}, which is deprecated.
+ * New tests should be written using this base class.
+ *
+ * <p>If you prefer an isolated unit test, see {@link android.test.ActivityUnitTestCase}.
+ */
+public abstract class ActivityInstrumentationTestCase2<T extends Activity>
+ extends ActivityTestCase {
+ String mPackage;
+ Class<T> mActivityClass;
+ boolean mInitialTouchMode = false;
+ Intent mActivityIntent = null;
+
+ /**
+ * @param pkg The package of the instrumentation.
+ * @param activityClass The activity to test.
+ */
+ public ActivityInstrumentationTestCase2(String pkg, Class<T> activityClass) {
+ mPackage = pkg;
+ mActivityClass = activityClass;
+ }
+
+ /**
+ * Get the Activity under test, starting it if necessary.
+ *
+ * For each test method invocation, the Activity will not actually be created until the first
+ * time this method is called.
+ *
+ * <p>If you wish to provide custom setup values to your Activity, you may call
+ * {@link #setActivityIntent(Intent)} and/or {@link #setActivityInitialTouchMode(boolean)}
+ * before your first call to getActivity(). Calling them after your Activity has
+ * started will have no effect.
+ *
+ * <p><b>NOTE:</b> Activities under test may not be started from within the UI thread.
+ * If your test method is annotated with {@link android.test.UiThreadTest}, then your Activity
+ * will be started automatically just before your test method is run. You still call this
+ * method in order to get the Activity under test.
+ *
+ * @return the Activity under test
+ */
+ @Override
+ public T getActivity() {
+ Activity a = super.getActivity();
+ if (a == null) {
+ // set initial touch mode
+ getInstrumentation().setInTouchMode(mInitialTouchMode);
+ // inject custom intent, if provided
+ if (mActivityIntent == null) {
+ a = launchActivity(mPackage, mActivityClass, null);
+ } else {
+ a = launchActivityWithIntent(mPackage, mActivityClass, mActivityIntent);
+ }
+ setActivity(a);
+ }
+ return (T) a;
+ }
+
+ /**
+ * Call this method before the first call to {@link #getActivity} to inject a customized Intent
+ * into the Activity under test.
+ *
+ * <p>If you do not call this, the default intent will be provided. If you call this after
+ * your Activity has been started, it will have no effect.
+ *
+ * <p><b>NOTE:</b> Activities under test may not be started from within the UI thread.
+ * If your test method is annotated with {@link android.test.UiThreadTest}, then you must call
+ * {@link #setActivityIntent(Intent)} from {@link #setUp()}.
+ *
+ * <p>The default Intent (if this method is not called) is:
+ * action = {@link Intent#ACTION_MAIN}
+ * flags = {@link Intent#FLAG_ACTIVITY_NEW_TASK}
+ * All other fields are null or empty.
+ *
+ * @param i The Intent to start the Activity with, or null to reset to the default Intent.
+ */
+ public void setActivityIntent(Intent i) {
+ mActivityIntent = i;
+ }
+
+ /**
+ * Call this method before the first call to {@link #getActivity} to set the initial touch
+ * mode for the Activity under test.
+ *
+ * <p>If you do not call this, the touch mode will be false. If you call this after
+ * your Activity has been started, it will have no effect.
+ *
+ * <p><b>NOTE:</b> Activities under test may not be started from within the UI thread.
+ * If your test method is annotated with {@link android.test.UiThreadTest}, then you must call
+ * {@link #setActivityInitialTouchMode(boolean)} from {@link #setUp()}.
+ *
+ * @param initialTouchMode true if the Activity should be placed into "touch mode" when started
+ */
+ public void setActivityInitialTouchMode(boolean initialTouchMode) {
+ mInitialTouchMode = initialTouchMode;
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ boolean mInitialTouchMode = false;
+ Intent mActivityIntent = null;
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ // Finish the Activity off (unless was never launched anyway)
+ Activity a = super.getActivity();
+ if (a != null) {
+ a.finish();
+ setActivity(null);
+ }
+
+ // Scrub out members - protects against memory leaks in the case where someone
+ // creates a non-static inner class (thus referencing the test case) and gives it to
+ // someone else to hold onto
+ scrubClass(ActivityInstrumentationTestCase2.class);
+
+ super.tearDown();
+ }
+
+ /**
+ * Runs the current unit test. If the unit test is annotated with
+ * {@link android.test.UiThreadTest}, force the Activity to be created before switching to
+ * the UI thread.
+ */
+ @Override
+ protected void runTest() throws Throwable {
+ try {
+ Method method = getClass().getMethod(getName(), (Class[]) null);
+ if (method.isAnnotationPresent(UiThreadTest.class)) {
+ getActivity();
+ }
+ } catch (Exception e) {
+ // eat the exception here; super.runTest() will catch it again and handle it properly
+ }
+ super.runTest();
+ }
+
+}
diff --git a/test-runner/android/test/AndroidTestRunner.java b/test-runner/android/test/AndroidTestRunner.java
index 353255e..79cedb0 100644
--- a/test-runner/android/test/AndroidTestRunner.java
+++ b/test-runner/android/test/AndroidTestRunner.java
@@ -35,6 +35,7 @@ public class AndroidTestRunner extends BaseTestRunner {
private String mTestClassName;
private List<TestCase> mTestCases;
private Context mContext;
+ private boolean mSkipExecution = false;
private List<TestListener> mTestListeners = Lists.newArrayList();
private Instrumentation mInstrumentation;
@@ -124,8 +125,15 @@ public class AndroidTestRunner extends BaseTestRunner {
}
protected TestResult createTestResult() {
+ if (mSkipExecution) {
+ return new NoExecTestResult();
+ }
return new TestResult();
}
+
+ void setSkipExecution(boolean skip) {
+ mSkipExecution = skip;
+ }
public List<TestCase> getTestCases() {
return mTestCases;
diff --git a/test-runner/android/test/DatabaseTestUtils.java b/test-runner/android/test/DatabaseTestUtils.java
new file mode 100644
index 0000000..23e0aba
--- /dev/null
+++ b/test-runner/android/test/DatabaseTestUtils.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2008 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.test;
+
+import com.google.android.collect.Sets;
+
+import android.database.sqlite.SQLiteDatabase;
+import android.database.Cursor;
+
+import java.util.Set;
+
+/**
+ * A collection of utilities for writing unit tests for database code.
+ * @hide pending API council approval
+ */
+public class DatabaseTestUtils {
+
+ /**
+ * Compares the schema of two databases and asserts that they are equal.
+ * @param expectedDb the db that is known to have the correct schema
+ * @param db the db whose schema should be checked
+ */
+ public static void assertSchemaEquals(SQLiteDatabase expectedDb, SQLiteDatabase db) {
+ Set<String> expectedSchema = getSchemaSet(expectedDb);
+ Set<String> schema = getSchemaSet(db);
+ MoreAsserts.assertEquals(expectedSchema, schema);
+ }
+
+ private static Set<String> getSchemaSet(SQLiteDatabase db) {
+ Set<String> schemaSet = Sets.newHashSet();
+
+ Cursor entityCursor = db.rawQuery("SELECT sql FROM sqlite_master", null);
+ try {
+ while (entityCursor.moveToNext()) {
+ String sql = entityCursor.getString(0);
+ schemaSet.add(sql);
+ }
+ } finally {
+ entityCursor.close();
+ }
+ return schemaSet;
+ }
+}
diff --git a/test-runner/android/test/InstrumentationCoreTestRunner.java b/test-runner/android/test/InstrumentationCoreTestRunner.java
new file mode 100644
index 0000000..6b1a4e4
--- /dev/null
+++ b/test-runner/android/test/InstrumentationCoreTestRunner.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2008 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.test;
+
+import java.io.File;
+
+import android.os.Bundle;
+
+/**
+ * This test runner extends the default InstrumentationTestRunner. It overrides
+ * the {@code onCreate(Bundle)} method and sets the system properties necessary
+ * for many core tests to run. This is needed because there are some core tests
+ * that need writing access to the filesystem.
+ *
+ * @hide
+ */
+public class InstrumentationCoreTestRunner extends InstrumentationTestRunner {
+
+ @Override
+ public void onCreate(Bundle arguments) {
+ super.onCreate(arguments);
+
+ File cacheDir = getTargetContext().getCacheDir();
+
+ System.setProperty("user.language", "en");
+ System.setProperty("user.region", "US");
+ System.setProperty("java.io.tmpdir", cacheDir.getAbsolutePath());
+ }
+}
diff --git a/test-runner/android/test/InstrumentationTestRunner.java b/test-runner/android/test/InstrumentationTestRunner.java
index 930da12..8b8d472 100644
--- a/test-runner/android/test/InstrumentationTestRunner.java
+++ b/test-runner/android/test/InstrumentationTestRunner.java
@@ -16,6 +16,19 @@
package android.test;
+import static android.test.suitebuilder.TestPredicates.REJECT_PERFORMANCE;
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.os.Bundle;
+import android.os.Debug;
+import android.os.Looper;
+import android.test.suitebuilder.TestMethod;
+import android.test.suitebuilder.TestPredicates;
+import android.test.suitebuilder.TestSuiteBuilder;
+import android.util.Log;
+
+import com.android.internal.util.Predicate;
+
import junit.framework.AssertionFailedError;
import junit.framework.Test;
import junit.framework.TestCase;
@@ -25,18 +38,12 @@ import junit.framework.TestSuite;
import junit.runner.BaseTestRunner;
import junit.textui.ResultPrinter;
-import android.app.Activity;
-import android.app.Instrumentation;
-import android.content.Context;
-import android.os.Bundle;
-import android.os.Debug;
-import android.os.Looper;
-import android.test.suitebuilder.InstrumentationTestSuiteBuilder;
-import android.test.suitebuilder.TestSuiteBuilder;
-import android.test.suitebuilder.UnitTestSuiteBuilder;
-
import java.io.ByteArrayOutputStream;
+import java.io.File;
import java.io.PrintStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
/**
* An {@link Instrumentation} that runs various types of {@link junit.framework.TestCase}s against
@@ -69,10 +76,16 @@ import java.io.PrintStream;
* <b>Running all tests:</b> adb shell am instrument -w
* com.android.foo/android.test.InstrumentationTestRunner
* <p/>
- * <b>Running unit tests:</b> adb shell am instrument -w -e unit true
+ * <b>Running all small tests:</b> adb shell am instrument -w
+ * -e size small
* com.android.foo/android.test.InstrumentationTestRunner
* <p/>
- * <b>Running instrumentation tests:</b> adb shell am instrument -w -e func true
+ * <b>Running all medium tests:</b> adb shell am instrument -w
+ * -e size medium
+ * com.android.foo/android.test.InstrumentationTestRunner
+ * <p/>
+ * <b>Running all large tests:</b> adb shell am instrument -w
+ * -e size large
* com.android.foo/android.test.InstrumentationTestRunner
* <p/>
* <b>Running a single testcase:</b> adb shell am instrument -w
@@ -83,8 +96,30 @@ import java.io.PrintStream;
* -e class com.android.foo.FooTest#testFoo
* com.android.foo/android.test.InstrumentationTestRunner
* <p/>
+ * <b>Running multiple tests:</b> adb shell am instrument -w
+ * -e class com.android.foo.FooTest,com.android.foo.TooTest
+ * com.android.foo/android.test.InstrumentationTestRunner
+ * <p/>
+ * <b>Including performance tests:</b> adb shell am instrument -w
+ * -e perf true
+ * com.android.foo/android.test.InstrumentationTestRunner
+ * <p/>
* <b>To debug your tests, set a break point in your code and pass:</b>
* -e debug true
+ * <p/>
+ * <b>To run in 'log only' mode</b>
+ * -e log true
+ * This option will load and iterate through all test classes and methods, but will bypass actual
+ * test execution. Useful for quickly obtaining info on the tests to be executed by an
+ * instrumentation command.
+ * <p/>
+ * <b>To generate EMMA code coverage:</b>
+ * -e coverage true
+ * Note: this requires an emma instrumented build. By default, the code coverage results file
+ * will be saved as /sdcard/coverage.ec, unless overridden by coverageFile flag (see below)
+ * <p/>
+ * <b> To specify EMMA code coverage results file path:</b>
+ * -e coverageFile /sdcard/myFile.ec
* <br/>
* in addition to the other arguments.
*/
@@ -108,17 +143,36 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu
/** @hide */
public static final String ARGUMENT_TEST_CLASS = "class";
/** @hide */
- public static final String ARGUMENT_UNIT_CLASS = "unit";
+ public static final String ARGUMENT_TEST_PACKAGE = "package";
/** @hide */
- public static final String ARGUMENT_FUNC_CLASS = "func";
+ public static final String ARGUMENT_TEST_SIZE_PREDICATE = "size";
/** @hide */
- public static final String ARGUMENT_TEST_PACKAGE = "package";
+ public static final String ARGUMENT_INCLUDE_PERF = "perf";
+
+ private static final String SMALL_SUITE = "small";
+ private static final String MEDIUM_SUITE = "medium";
+ private static final String LARGE_SUITE = "large";
+
+ private static final String ARGUMENT_LOG_ONLY = "log";
+
+
+ /**
+ * This constant defines the maximum allowed runtime (in ms) for a test included in the "small" suite.
+ * It is used to make an educated guess at what suite an unlabeled test belongs.
+ */
+ private static final float SMALL_SUITE_MAX_RUNTIME = 100;
+
+ /**
+ * This constant defines the maximum allowed runtime (in ms) for a test included in the "medium" suite.
+ * It is used to make an educated guess at what suite an unlabeled test belongs.
+ */
+ private static final float MEDIUM_SUITE_MAX_RUNTIME = 1000;
/**
* The following keys are used in the status bundle to provide structured reports to
* an IInstrumentationWatcher.
*/
-
+
/**
* This value, if stored with key {@link android.app.Instrumentation#REPORT_KEY_IDENTIFIER},
* identifies InstrumentationTestRunner as the source of the report. This is sent with all
@@ -150,6 +204,16 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu
*/
public static final String REPORT_KEY_NAME_TEST = "test";
/**
+ * If included in the status or final bundle sent to an IInstrumentationWatcher, this key
+ * reports the run time in seconds of the current test.
+ */
+ private static final String REPORT_KEY_RUN_TIME = "runtime";
+ /**
+ * If included in the status or final bundle sent to an IInstrumentationWatcher, this key
+ * reports the guessed suite assignment for the current test.
+ */
+ private static final String REPORT_KEY_SUITE_ASSIGNMENT = "suiteassignment";
+ /**
* The test is starting.
*/
public static final int REPORT_VALUE_RESULT_START = 1;
@@ -172,14 +236,19 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu
*/
public static final String REPORT_KEY_STACK = "stack";
+ private static final String DEFAULT_COVERAGE_FILE_PATH = "/sdcard/coverage.ec";
+
+ private static final String LOG_TAG = "InstrumentationTestRunner";
+
private final Bundle mResults = new Bundle();
private AndroidTestRunner mTestRunner;
private boolean mDebug;
private boolean mJustCount;
+ private boolean mSuiteAssignmentMode;
private int mTestCount;
- private boolean mBundleOutput;
- private boolean mDatabaseOutput;
private String mPackageOfTests;
+ private boolean mCoverage;
+ private String mCoverageFilePath;
@Override
public void onCreate(Bundle arguments) {
@@ -190,90 +259,95 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu
{getTargetContext().getPackageCodePath(), getContext().getPackageCodePath()};
ClassPathPackageInfoSource.setApkPaths(apkPaths);
- boolean useUnitTestSuite = false;
- boolean useFunctionalTestSuite = false;
-
- String testClassName = null;
- Test test = null;
+ Predicate<TestMethod> testSizePredicate = null;
+ boolean includePerformance = false;
+ String testClassesArg = null;
+ boolean logOnly = false;
if (arguments != null) {
// Test class name passed as an argument should override any meta-data declaration.
- testClassName = arguments.getString(ARGUMENT_TEST_CLASS);
+ testClassesArg = arguments.getString(ARGUMENT_TEST_CLASS);
mDebug = getBooleanArgument(arguments, "debug");
mJustCount = getBooleanArgument(arguments, "count");
- mBundleOutput = getBooleanArgument(arguments, "bundle");
- mDatabaseOutput = getBooleanArgument(arguments, "database");
- useUnitTestSuite = getBooleanArgument(arguments, ARGUMENT_UNIT_CLASS);
- useFunctionalTestSuite = getBooleanArgument(arguments, ARGUMENT_FUNC_CLASS);
+ mSuiteAssignmentMode = getBooleanArgument(arguments, "suiteAssignment");
mPackageOfTests = arguments.getString(ARGUMENT_TEST_PACKAGE);
+ testSizePredicate = getSizePredicateFromArg(
+ arguments.getString(ARGUMENT_TEST_SIZE_PREDICATE));
+ includePerformance = getBooleanArgument(arguments, ARGUMENT_INCLUDE_PERF);
+ logOnly = getBooleanArgument(arguments, ARGUMENT_LOG_ONLY);
+ mCoverage = getBooleanArgument(arguments, "coverage");
+ mCoverageFilePath = arguments.getString("coverageFile");
+ }
+
+ TestSuiteBuilder testSuiteBuilder = new TestSuiteBuilder(getClass().getName(),
+ getTargetContext().getClassLoader());
+
+ if (testSizePredicate != null) {
+ testSuiteBuilder.addRequirements(testSizePredicate);
+ }
+ if (!includePerformance) {
+ testSuiteBuilder.addRequirements(REJECT_PERFORMANCE);
}
- if (testClassName == null) {
+ if (testClassesArg == null) {
+ TestSuite testSuite = null;
if (mPackageOfTests != null) {
- test = createPackageTestSuite(
- getTargetContext(), useUnitTestSuite,
- useFunctionalTestSuite, mPackageOfTests);
+ testSuiteBuilder.includePackages(mPackageOfTests);
} else {
- test = getTestSuite();
+ testSuite = getTestSuite();
+ testSuiteBuilder.addTestSuite(testSuite);
}
- if (test == null) {
- test = createDefaultTestSuite(getTargetContext(), useUnitTestSuite,
- useFunctionalTestSuite);
+ if (testSuite == null) {
+ testSuiteBuilder.includePackages(getTargetContext().getPackageName());
}
- mTestCount = test.countTestCases();
+ } else {
+ parseTestClasses(testClassesArg, testSuiteBuilder);
}
mTestRunner = getAndroidTestRunner();
mTestRunner.setContext(getTargetContext());
mTestRunner.setInstrumentaiton(this);
- mTestRunner.addTestListener(new TestPrinter("TestRunner", false));
- if (mDatabaseOutput) {
- mTestRunner.addTestListener(new TestRecorder());
- }
-
- if (testClassName != null) {
- int methodSeparatorIndex = testClassName.indexOf('#');
- String testMethodName = null;
-
- if (methodSeparatorIndex > 0) {
- testMethodName = testClassName.substring(methodSeparatorIndex + 1);
- testClassName = testClassName.substring(0, methodSeparatorIndex);
- }
- mTestRunner.setTestClassName(testClassName, testMethodName);
- mTestCount = mTestRunner.getTestCases().size();
+ mTestRunner.setSkipExecution(logOnly);
+ mTestRunner.setTest(testSuiteBuilder.build());
+ mTestCount = mTestRunner.getTestCases().size();
+ if (mSuiteAssignmentMode) {
+ mTestRunner.addTestListener(new SuiteAssignmentPrinter());
} else {
- mTestRunner.setTest(test);
- }
- // add this now that we know the count
- if (!mBundleOutput) {
+ mTestRunner.addTestListener(new TestPrinter("TestRunner", false));
mTestRunner.addTestListener(new WatcherResultPrinter(mTestCount));
}
-
start();
}
- private TestSuite createDefaultTestSuite(Context context, boolean useUnitTestSuite,
- boolean useFunctionalTestSuite) {
- return createPackageTestSuite(context, useUnitTestSuite, useFunctionalTestSuite, context.getPackageName());
+ /**
+ * Parses and loads the specified set of test classes
+ * @param testClassArg - comma-separated list of test classes and methods
+ * @param testSuiteBuilder - builder to add tests to
+ */
+ private void parseTestClasses(String testClassArg, TestSuiteBuilder testSuiteBuilder) {
+ String[] testClasses = testClassArg.split(",");
+ for (String testClass : testClasses) {
+ parseTestClass(testClass, testSuiteBuilder);
+ }
}
- private TestSuite createPackageTestSuite(Context context, boolean useUnitTestSuite,
- boolean useFunctionalTestSuite, String packageName) {
- TestSuiteBuilder testSuiteBuilder = null;
+ /**
+ * Parse and load the given test class and, optionally, method
+ * @param testClassName - full package name of test class and optionally method to add. Expected
+ * format: com.android.TestClass#testMethod
+ * @param testSuiteBuilder - builder to add tests to
+ */
+ private void parseTestClass(String testClassName, TestSuiteBuilder testSuiteBuilder) {
+ int methodSeparatorIndex = testClassName.indexOf('#');
+ String testMethodName = null;
- if (useUnitTestSuite) {
- testSuiteBuilder = new UnitTestSuiteBuilder(getClass().getName(),
- context.getClassLoader());
- } else if (useFunctionalTestSuite) {
- testSuiteBuilder = new InstrumentationTestSuiteBuilder(getClass().getName(),
- context.getClassLoader());
- } else {
- testSuiteBuilder = new TestSuiteBuilder(getClass().getName(),
- context.getClassLoader());
+ if (methodSeparatorIndex > 0) {
+ testMethodName = testClassName.substring(methodSeparatorIndex + 1);
+ testClassName = testClassName.substring(0, methodSeparatorIndex);
}
-
- return testSuiteBuilder.includePackages(packageName).build();
+ testSuiteBuilder.addTestClassByName(testClassName, testMethodName,
+ getTargetContext());
}
protected AndroidTestRunner getAndroidTestRunner() {
@@ -284,7 +358,23 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu
String tagString = arguments.getString(tag);
return tagString != null && Boolean.parseBoolean(tagString);
}
-
+
+ /*
+ * Returns the size predicate object, corresponding to the "size" argument value.
+ */
+ private Predicate<TestMethod> getSizePredicateFromArg(String sizeArg) {
+
+ if (SMALL_SUITE.equals(sizeArg)) {
+ return TestPredicates.SELECT_SMALL;
+ } else if (MEDIUM_SUITE.equals(sizeArg)) {
+ return TestPredicates.SELECT_MEDIUM;
+ } else if (LARGE_SUITE.equals(sizeArg)) {
+ return TestPredicates.SELECT_LARGE;
+ } else {
+ return null;
+ }
+ }
+
@Override
public void onStart() {
Looper.prepare();
@@ -303,25 +393,24 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu
try {
StringResultPrinter resultPrinter = new StringResultPrinter(writer);
- if (mBundleOutput) {
- mTestRunner.addTestListener(new BundleTestListener(mResults));
- } else {
- mTestRunner.addTestListener(resultPrinter);
- }
-
+ mTestRunner.addTestListener(resultPrinter);
+
long startTime = System.currentTimeMillis();
mTestRunner.runTest();
long runTime = System.currentTimeMillis() - startTime;
resultPrinter.print(mTestRunner.getTestResult(), runTime);
} finally {
- if (!mBundleOutput) {
- mResults.putString(Instrumentation.REPORT_KEY_STREAMRESULT,
- String.format("\nTest results for %s=%s",
- mTestRunner.getTestClassName(),
- byteArrayOutputStream.toString()));
+ mResults.putString(Instrumentation.REPORT_KEY_STREAMRESULT,
+ String.format("\nTest results for %s=%s",
+ mTestRunner.getTestClassName(),
+ byteArrayOutputStream.toString()));
+
+ if (mCoverage) {
+ generateCoverageReport();
}
writer.close();
+
finish(Activity.RESULT_OK, mResults);
}
}
@@ -344,6 +433,51 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu
public ClassLoader getLoader() {
return null;
}
+
+ private void generateCoverageReport() {
+ // use reflection to call emma dump coverage method, to avoid
+ // always statically compiling against emma jar
+ java.io.File coverageFile = new java.io.File(getCoverageFilePath());
+ try {
+ Class emmaRTClass = Class.forName("com.vladium.emma.rt.RT");
+ Method dumpCoverageMethod = emmaRTClass.getMethod("dumpCoverageData",
+ coverageFile.getClass(), boolean.class, boolean.class);
+
+ dumpCoverageMethod.invoke(null, coverageFile, false, false);
+
+ } catch (ClassNotFoundException e) {
+ reportEmmaError("Is emma jar on classpath?", e);
+ } catch (SecurityException e) {
+ reportEmmaError(e);
+ } catch (NoSuchMethodException e) {
+ reportEmmaError(e);
+ } catch (IllegalArgumentException e) {
+ reportEmmaError(e);
+ } catch (IllegalAccessException e) {
+ reportEmmaError(e);
+ } catch (InvocationTargetException e) {
+ reportEmmaError(e);
+ }
+ }
+
+ private String getCoverageFilePath() {
+ if (mCoverageFilePath == null) {
+ return DEFAULT_COVERAGE_FILE_PATH;
+ }
+ else {
+ return mCoverageFilePath;
+ }
+ }
+
+ private void reportEmmaError(Exception e) {
+ reportEmmaError("", e);
+ }
+
+ private void reportEmmaError(String hint, Exception e) {
+ String msg = "Failed to generate emma coverage. " + hint;
+ Log.e(LOG_TAG, msg, e);
+ mResults.putString(Instrumentation.REPORT_KEY_STREAMRESULT, "\nError: " + msg);
+ }
// TODO kill this, use status() and prettyprint model for better output
private class StringResultPrinter extends ResultPrinter {
@@ -354,15 +488,85 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu
synchronized void print(TestResult result, long runTime) {
printHeader(runTime);
- if (mBundleOutput) {
- printErrors(result);
- printFailures(result);
- }
printFooter(result);
}
}
/**
+ * This class sends status reports back to the IInstrumentationWatcher about
+ * which suite each test belongs.
+ */
+ private class SuiteAssignmentPrinter implements TestListener
+ {
+
+ private Bundle mTestResult;
+ private long mStartTime;
+ private long mEndTime;
+ private boolean mTimingValid;
+
+ public SuiteAssignmentPrinter() {
+ }
+
+ /**
+ * send a status for the start of a each test, so long tests can be seen as "running"
+ */
+ public void startTest(Test test) {
+ mTimingValid = true;
+ mStartTime = System.currentTimeMillis();
+ }
+
+ /**
+ * @see junit.framework.TestListener#addError(Test, Throwable)
+ */
+ public void addError(Test test, Throwable t) {
+ mTimingValid = false;
+ }
+
+ /**
+ * @see junit.framework.TestListener#addFailure(Test, AssertionFailedError)
+ */
+ public void addFailure(Test test, AssertionFailedError t) {
+ mTimingValid = false;
+ }
+
+ /**
+ * @see junit.framework.TestListener#endTest(Test)
+ */
+ public void endTest(Test test) {
+ float runTime;
+ String assignmentSuite;
+ mEndTime = System.currentTimeMillis();
+ mTestResult = new Bundle();
+
+ if (!mTimingValid || mStartTime < 0) {
+ assignmentSuite = "NA";
+ runTime = -1;
+ } else {
+ runTime = mEndTime - mStartTime;
+ if (runTime < SMALL_SUITE_MAX_RUNTIME
+ && !InstrumentationTestCase.class.isAssignableFrom(test.getClass())) {
+ assignmentSuite = SMALL_SUITE;
+ } else if (runTime < MEDIUM_SUITE_MAX_RUNTIME) {
+ assignmentSuite = MEDIUM_SUITE;
+ } else {
+ assignmentSuite = LARGE_SUITE;
+ }
+ }
+ // Clear mStartTime so that we can verify that it gets set next time.
+ mStartTime = -1;
+
+ mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT,
+ test.getClass().getName() + "#" + ((TestCase) test).getName()
+ + "\nin " + assignmentSuite + " suite\nrunTime: "
+ + String.valueOf(runTime) + "\n");
+ mTestResult.putFloat(REPORT_KEY_RUN_TIME, runTime);
+ mTestResult.putString(REPORT_KEY_SUITE_ASSIGNMENT, assignmentSuite);
+
+ sendStatus(0, mTestResult);
+ }
+ }
+
+ /**
* This class sends status reports back to the IInstrumentationWatcher
*/
private class WatcherResultPrinter implements TestListener
@@ -437,6 +641,5 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu
// TODO report the end of the cycle
// TODO report runtime for each test
}
-
-
}
+
diff --git a/test-runner/android/test/NoExecTestResult.java b/test-runner/android/test/NoExecTestResult.java
new file mode 100755
index 0000000..1ee62c1
--- /dev/null
+++ b/test-runner/android/test/NoExecTestResult.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2008 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.test;
+
+import junit.framework.TestCase;
+import junit.framework.TestResult;
+
+/**
+ * A benign test result that does no actually test execution, just runs
+ * through the motions
+ *
+ * {@hide} Not needed for SDK.
+ */
+class NoExecTestResult extends TestResult {
+
+ /**
+ * Override parent to just inform listeners of test,
+ * and skip test execution.
+ */
+ @Override
+ protected void run(final TestCase test) {
+ startTest(test);
+ endTest(test);
+ }
+
+}
diff --git a/test-runner/android/test/ProviderTestCase.java b/test-runner/android/test/ProviderTestCase.java
index 5882387..445b4eb 100644
--- a/test-runner/android/test/ProviderTestCase.java
+++ b/test-runner/android/test/ProviderTestCase.java
@@ -11,9 +11,12 @@ import android.database.DatabaseUtils;
* If you would like to test a single content provider with an
* {@link InstrumentationTestCase}, this provides some of the boiler plate in {@link #setUp} and
* {@link #tearDown}.
+ *
+ * @deprecated this class extends InstrumentationTestCase but should extend AndroidTestCase. Use
+ * ProviderTestCase2, which corrects this problem, instead.
*/
public abstract class ProviderTestCase<T extends ContentProvider>
- extends InstrumentationTestCase {
+ extends InstrumentationTestCase {
Class<T> mProviderClass;
String mProviderAuthority;
@@ -66,18 +69,17 @@ public abstract class ProviderTestCase<T extends ContentProvider>
String databaseName, int databaseVersion, String sql)
throws IllegalAccessException, InstantiationException {
final String filenamePrefix = "test.";
- MockContentResolver mockContentResolver = new MockContentResolver();
+ MockContentResolver resolver = new MockContentResolver();
RenamingDelegatingContext targetContextWrapper = new RenamingDelegatingContext(
new MockContext(), // The context that most methods are delegated to
targetContext, // The context that file methods are delegated to
filenamePrefix);
Context context = new IsolatedContext(
- mockContentResolver, targetContextWrapper);
+ resolver, targetContextWrapper);
DatabaseUtils.createDbFromSqlStatements(context, databaseName, databaseVersion, sql);
T provider = providerClass.newInstance();
provider.attachInfo(context, null);
- MockContentResolver resolver = new MockContentResolver();
resolver.addProvider(authority, provider);
return resolver;
diff --git a/test-runner/android/test/ProviderTestCase2.java b/test-runner/android/test/ProviderTestCase2.java
new file mode 100644
index 0000000..714b77b
--- /dev/null
+++ b/test-runner/android/test/ProviderTestCase2.java
@@ -0,0 +1,82 @@
+package android.test;
+
+import android.content.ContentProvider;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.test.mock.MockContext;
+import android.test.mock.MockContentResolver;
+import android.database.DatabaseUtils;
+
+/**
+ * If you would like to test a single content provider with an
+ * {@link android.test.InstrumentationTestCase}, this provides some of the boiler plate in
+ * {@link #setUp} and {@link #tearDown}.
+ * @hide pending API council approval
+ */
+public abstract class ProviderTestCase2<T extends ContentProvider> extends AndroidTestCase {
+
+ Class<T> mProviderClass;
+ String mProviderAuthority;
+
+ private IsolatedContext mProviderContext;
+ private MockContentResolver mResolver;
+
+ public ProviderTestCase2(Class<T> providerClass, String providerAuthority) {
+ mProviderClass = providerClass;
+ mProviderAuthority = providerAuthority;
+ }
+
+ /**
+ * The content provider that will be set up for use in each test method.
+ */
+ private T mProvider;
+
+ public T getProvider() {
+ return mProvider;
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mResolver = new MockContentResolver();
+ final String filenamePrefix = "test.";
+ RenamingDelegatingContext targetContextWrapper = new RenamingDelegatingContext(
+ new MockContext(), // The context that most methods are delegated to
+ getContext(), // The context that file methods are delegated to
+ filenamePrefix);
+ mProviderContext = new IsolatedContext(mResolver, targetContextWrapper);
+
+ mProvider = mProviderClass.newInstance();
+ mProvider.attachInfo(mProviderContext, null);
+ assertNotNull(mProvider);
+ mResolver.addProvider(mProviderAuthority, getProvider());
+ }
+
+ public MockContentResolver getMockContentResolver() {
+ return mResolver;
+ }
+
+ public IsolatedContext getMockContext() {
+ return mProviderContext;
+ }
+
+ public static <T extends ContentProvider> ContentResolver newResolverWithContentProviderFromSql(
+ Context targetContext, String filenamePrefix, Class<T> providerClass, String authority,
+ String databaseName, int databaseVersion, String sql)
+ throws IllegalAccessException, InstantiationException {
+ MockContentResolver resolver = new MockContentResolver();
+ RenamingDelegatingContext targetContextWrapper = new RenamingDelegatingContext(
+ new MockContext(), // The context that most methods are delegated to
+ targetContext, // The context that file methods are delegated to
+ filenamePrefix);
+ Context context = new IsolatedContext(resolver, targetContextWrapper);
+ DatabaseUtils.createDbFromSqlStatements(context, databaseName, databaseVersion, sql);
+
+ T provider = providerClass.newInstance();
+ provider.attachInfo(context, null);
+ resolver.addProvider(authority, provider);
+
+ return resolver;
+ }
+} \ No newline at end of file
diff --git a/test-runner/android/test/mock/MockContentResolver.java b/test-runner/android/test/mock/MockContentResolver.java
index 66840a1..3a1dc36c 100644
--- a/test-runner/android/test/mock/MockContentResolver.java
+++ b/test-runner/android/test/mock/MockContentResolver.java
@@ -50,7 +50,12 @@ public class MockContentResolver extends ContentResolver {
/** @hide */
@Override
protected IContentProvider acquireProvider(Context context, String name) {
- return mProviders.get(name).getIContentProvider();
+ final ContentProvider provider = mProviders.get(name);
+ if (provider != null) {
+ return provider.getIContentProvider();
+ } else {
+ return null;
+ }
}
/** @hide */
diff --git a/test-runner/android/test/mock/MockPackageManager.java b/test-runner/android/test/mock/MockPackageManager.java
index 4f7745b..34ebd78 100644
--- a/test-runner/android/test/mock/MockPackageManager.java
+++ b/test-runner/android/test/mock/MockPackageManager.java
@@ -140,6 +140,14 @@ public class MockPackageManager extends PackageManager {
public String getNameForUid(int uid) {
throw new UnsupportedOperationException();
}
+
+ /**
+ * @hide - to match hiding in superclass
+ */
+ @Override
+ public int getUidForSharedUser(String sharedUserName) {
+ throw new UnsupportedOperationException();
+ }
@Override
public List<ApplicationInfo> getInstalledApplications(int flags) {
diff --git a/test-runner/android/test/suitebuilder/TestMethod.java b/test-runner/android/test/suitebuilder/TestMethod.java
index 3a936c2..08568d5 100644
--- a/test-runner/android/test/suitebuilder/TestMethod.java
+++ b/test-runner/android/test/suitebuilder/TestMethod.java
@@ -26,8 +26,6 @@ import java.lang.reflect.Method;
/**
* Represents a test to be run. Can be constructed without instantiating the TestCase or even
* loading the class.
- *
- * {@hide} Not needed for 1.0 SDK.
*/
public class TestMethod {
@@ -36,9 +34,17 @@ public class TestMethod {
private final Class<? extends TestCase> enclosingClass;
public TestMethod(Method method, Class<? extends TestCase> enclosingClass) {
+ this(method.getName(), enclosingClass);
+ }
+
+ public TestMethod(String methodName, Class<? extends TestCase> enclosingClass) {
this.enclosingClass = enclosingClass;
this.enclosingClassname = enclosingClass.getName();
- this.testMethodName = method.getName();
+ this.testMethodName = methodName;
+ }
+
+ public TestMethod(TestCase testCase) {
+ this(testCase.getName(), testCase.getClass());
}
public String getName() {
@@ -53,7 +59,7 @@ public class TestMethod {
try {
return getEnclosingClass().getMethod(getName()).getAnnotation(annotationClass);
} catch (NoSuchMethodException e) {
- throw new RuntimeException(e);
+ return null;
}
}
diff --git a/test-runner/android/test/suitebuilder/TestPredicates.java b/test-runner/android/test/suitebuilder/TestPredicates.java
index ff75217..d814e0b 100644
--- a/test-runner/android/test/suitebuilder/TestPredicates.java
+++ b/test-runner/android/test/suitebuilder/TestPredicates.java
@@ -20,6 +20,9 @@ import android.test.InstrumentationTestCase;
import android.test.PerformanceTestBase;
import android.test.suitebuilder.annotation.HasAnnotation;
import android.test.suitebuilder.annotation.Suppress;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.SmallTest;
import android.test.suitebuilder.annotation.Smoke;
import com.android.internal.util.Predicate;
import com.android.internal.util.Predicates;
@@ -35,6 +38,9 @@ public class TestPredicates {
Predicates.not(SELECT_INSTRUMENTATION);
public static final Predicate<TestMethod> SELECT_SMOKE = new HasAnnotation(Smoke.class);
+ public static final Predicate<TestMethod> SELECT_SMALL = new HasAnnotation(SmallTest.class);
+ public static final Predicate<TestMethod> SELECT_MEDIUM = new HasAnnotation(MediumTest.class);
+ public static final Predicate<TestMethod> SELECT_LARGE = new HasAnnotation(LargeTest.class);
public static final Predicate<TestMethod> REJECT_SUPPRESSED =
Predicates.not(new HasAnnotation(Suppress.class));
public static final Predicate<TestMethod> REJECT_PERFORMANCE =
diff --git a/test-runner/android/test/suitebuilder/TestSuiteBuilder.java b/test-runner/android/test/suitebuilder/TestSuiteBuilder.java
index 99d0eb9..428905e 100644
--- a/test-runner/android/test/suitebuilder/TestSuiteBuilder.java
+++ b/test-runner/android/test/suitebuilder/TestSuiteBuilder.java
@@ -16,15 +16,22 @@
package android.test.suitebuilder;
+import android.content.Context;
+import android.test.AndroidTestRunner;
+import android.test.TestCaseUtil;
import android.util.Log;
import com.android.internal.util.Predicate;
+import com.google.android.collect.Lists;
import static android.test.suitebuilder.TestGrouping.SORT_BY_FULLY_QUALIFIED_NAME;
import static android.test.suitebuilder.TestPredicates.REJECT_SUPPRESSED;
import static android.test.suitebuilder.TestPredicates.REJECT_PERFORMANCE;
+import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Enumeration;
import java.util.List;
import java.util.Set;
import java.util.HashSet;
@@ -37,8 +44,10 @@ import java.util.Collections;
*/
public class TestSuiteBuilder {
+ private Context context;
private final TestGrouping testGrouping = new TestGrouping(SORT_BY_FULLY_QUALIFIED_NAME);
private final Set<Predicate<TestMethod>> predicates = new HashSet<Predicate<TestMethod>>();
+ private List<TestCase> testCases;
private TestSuite rootSuite;
private TestSuite suiteForCurrentClass;
private String currentClassname;
@@ -59,8 +68,28 @@ public class TestSuiteBuilder {
public TestSuiteBuilder(String name, ClassLoader classLoader) {
this.suiteName = name;
this.testGrouping.setClassLoader(classLoader);
+ this.testCases = Lists.newArrayList();
addRequirements(REJECT_SUPPRESSED);
- addRequirements(REJECT_PERFORMANCE);
+ }
+
+ /** @hide pending API Council approval */
+ public TestSuiteBuilder addTestClassByName(String testClassName, String testMethodName,
+ Context context) {
+
+ AndroidTestRunner atr = new AndroidTestRunner();
+ atr.setContext(context);
+ atr.setTestClassName(testClassName, testMethodName);
+
+ this.testCases.addAll(atr.getTestCases());
+ return this;
+ }
+
+ /** @hide pending API Council approval */
+ public TestSuiteBuilder addTestSuite(TestSuite testSuite) {
+ for (TestCase testCase : (List<TestCase>) TestCaseUtil.getTests(testSuite, true)) {
+ this.testCases.add(testCase);
+ }
+ return this;
}
/**
@@ -156,6 +185,13 @@ public class TestSuiteBuilder {
addTest(test);
}
}
+ if (testCases.size() > 0) {
+ for (TestCase testCase : testCases) {
+ if (satisfiesAllPredicates(new TestMethod(testCase))) {
+ addTest(testCase);
+ }
+ }
+ }
} catch (Exception exception) {
Log.i("TestSuiteBuilder", "Failed to create test.", exception);
TestSuite suite = new TestSuite(getSuiteName());
@@ -223,12 +259,16 @@ public class TestSuiteBuilder {
}
private void addTest(TestMethod testMethod) throws Exception {
- addSuiteIfNecessary(testMethod);
+ addSuiteIfNecessary(testMethod.getEnclosingClassname());
suiteForCurrentClass.addTest(testMethod.createTest());
}
+
+ private void addTest(Test test) {
+ addSuiteIfNecessary(test.getClass().getName());
+ suiteForCurrentClass.addTest(test);
+ }
- private void addSuiteIfNecessary(TestMethod test) {
- String parentClassname = test.getEnclosingClassname();
+ private void addSuiteIfNecessary(String parentClassname) {
if (!parentClassname.equals(currentClassname)) {
currentClassname = parentClassname;
suiteForCurrentClass = new TestSuite(parentClassname);