/*
* 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.Instrumentation;
import android.content.Context;
import android.os.Bundle;
import android.test.mock.MockContext;
import android.test.suitebuilder.ListTestCaseNames;
import android.test.suitebuilder.ListTestCaseNames.TestDescriptor;
import android.test.suitebuilder.annotation.SmallTest;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import java.util.List;
/**
* Tests for {@link InstrumentationTestRunner}
*/
@SmallTest
public class InstrumentationTestRunnerTest extends TestCase {
private StubInstrumentationTestRunner mInstrumentationTestRunner;
private StubAndroidTestRunner mStubAndroidTestRunner;
private String mTargetContextPackageName;
protected void setUp() throws Exception {
super.setUp();
mStubAndroidTestRunner = new StubAndroidTestRunner();
mTargetContextPackageName = "android.test.suitebuilder.examples";
mInstrumentationTestRunner = new StubInstrumentationTestRunner(
new StubContext("com.google.foo.tests"),
new StubContext(mTargetContextPackageName), mStubAndroidTestRunner);
}
public void testOverrideTestToRunWithClassArgument() throws Exception {
String expectedTestClassName = PlaceHolderTest.class.getName();
mInstrumentationTestRunner.onCreate(createBundle(
InstrumentationTestRunner.ARGUMENT_TEST_CLASS, expectedTestClassName));
assertTestRunnerCalledWithExpectedParameters(expectedTestClassName, "testPlaceHolder");
}
public void testOverrideTestToRunWithClassAndMethodArgument() throws Exception {
String expectedTestClassName = PlaceHolderTest.class.getName();
String expectedTestMethodName = "testPlaceHolder";
String classAndMethod = expectedTestClassName + "#" + expectedTestMethodName;
mInstrumentationTestRunner.onCreate(createBundle(
InstrumentationTestRunner.ARGUMENT_TEST_CLASS, classAndMethod));
assertTestRunnerCalledWithExpectedParameters(expectedTestClassName,
expectedTestMethodName);
}
public void testUseSelfAsTestSuiteProviderWhenNoMetaDataOrClassArgument() throws Exception {
TestSuite testSuite = new TestSuite();
testSuite.addTestSuite(PlaceHolderTest.class);
mInstrumentationTestRunner.setAllTestsSuite(testSuite);
mInstrumentationTestRunner.onCreate(null);
assertTestRunnerCalledWithExpectedParameters(
PlaceHolderTest.class.getName(), "testPlaceHolder");
}
public void testMultipleTestClass() throws Exception {
String classArg = PlaceHolderTest.class.getName() + "," +
PlaceHolderTest2.class.getName();
mInstrumentationTestRunner.onCreate(createBundle(
InstrumentationTestRunner.ARGUMENT_TEST_CLASS, classArg));
Test test = mStubAndroidTestRunner.getTest();
assertContentsInOrder(ListTestCaseNames.getTestNames((TestSuite) test),
new TestDescriptor(PlaceHolderTest.class.getName(), "testPlaceHolder"),
new TestDescriptor(PlaceHolderTest2.class.getName(), "testPlaceHolder2"));
}
/**
* Test that runtime exceptions during runTest are handled gracefully
*/
public void testUnhandledException() throws Exception {
StubAndroidTestRunner stubAndroidTestRunner = new StubAndroidTestRunner() {
@Override
public void runTest() {
throw new RuntimeException();
}
};
StubInstrumentationTestRunner instrumentationTestRunner = new StubInstrumentationTestRunner(
new StubContext("com.google.foo.tests"),
new StubContext(mTargetContextPackageName), stubAndroidTestRunner);
instrumentationTestRunner.onCreate(new Bundle());
instrumentationTestRunner.onStart();
assertTrue("Instrumentation did not finish", instrumentationTestRunner.isFinished());
// ensure a meaningful error message placed in results
String resultsData = instrumentationTestRunner.mResults.getString(
Instrumentation.REPORT_KEY_STREAMRESULT);
assertTrue("Instrumentation results is missing RuntimeException",
resultsData.contains("RuntimeException"));
}
/**
* Test that specifying a method which does not exist is handled gracefully
*/
public void testBadMethodArgument() throws Exception {
String testClassName = PlaceHolderTest.class.getName();
String invalidMethodName = "testNoExist";
String classAndMethod = testClassName + "#" + invalidMethodName;
mInstrumentationTestRunner.onCreate(createBundle(
InstrumentationTestRunner.ARGUMENT_TEST_CLASS, classAndMethod));
assertTestRunnerCalledWithExpectedParameters(testClassName,
invalidMethodName);
}
public void testDelayParameter() throws Exception {
int delayMsec = 1000;
Bundle args = new Bundle();
args.putInt(InstrumentationTestRunner.ARGUMENT_DELAY_MSEC, delayMsec);
args.putString(InstrumentationTestRunner.ARGUMENT_TEST_CLASS,
PlaceHolderTest.class.getName() + "," +
PlaceHolderTest2.class.getName());
mInstrumentationTestRunner.onCreate(args);
Thread t = new Thread() { public void run() { mInstrumentationTestRunner.onStart(); } };
// Should delay three times: before, between, and after the two tests.
long beforeTest = System.currentTimeMillis();
t.start();
t.join();
assertTrue(System.currentTimeMillis() > beforeTest + delayMsec * 3);
assertTrue(mInstrumentationTestRunner.isStarted());
assertTrue(mInstrumentationTestRunner.isFinished());
assertTrue(mStubAndroidTestRunner.isRun());
}
/**
* Test that the -e {@link InstrumentationTestRunner.ARGUMENT_ANNOTATION} parameter properly
* selects tests.
*/
public void testAnnotationParameter() throws Exception {
String expectedTestClassName = AnnotationTest.class.getName();
Bundle args = new Bundle();
args.putString(InstrumentationTestRunner.ARGUMENT_TEST_CLASS, expectedTestClassName);
args.putString(InstrumentationTestRunner.ARGUMENT_ANNOTATION, FlakyTest.class.getName());
mInstrumentationTestRunner.onCreate(args);
assertTestRunnerCalledWithExpectedParameters(expectedTestClassName, "testAnnotated");
}
/**
* Test that the -e {@link InstrumentationTestRunner.ARGUMENT_NOT_ANNOTATION} parameter
* properly excludes tests.
*/
public void testNotAnnotationParameter() throws Exception {
String expectedTestClassName = AnnotationTest.class.getName();
Bundle args = new Bundle();
args.putString(InstrumentationTestRunner.ARGUMENT_TEST_CLASS, expectedTestClassName);
args.putString(InstrumentationTestRunner.ARGUMENT_NOT_ANNOTATION,
FlakyTest.class.getName());
mInstrumentationTestRunner.onCreate(args);
assertTestRunnerCalledWithExpectedParameters(expectedTestClassName, "testNotAnnotated");
}
private void assertContentsInOrder(List<TestDescriptor> actual, TestDescriptor... source) {
TestDescriptor[] clonedSource = source.clone();
assertEquals("Unexpected number of items.", clonedSource.length, actual.size());
for (int i = 0; i < actual.size(); i++) {
TestDescriptor actualItem = actual.get(i);
TestDescriptor sourceItem = clonedSource[i];
assertEquals("Unexpected item. Index: " + i, sourceItem, actualItem);
}
}
private void assertTestRunnerCalledWithExpectedParameters(
String expectedTestClassName, String expectedTestMethodName) {
Test test = mStubAndroidTestRunner.getTest();
assertContentsInOrder(ListTestCaseNames.getTestNames((TestSuite) test),
new TestDescriptor(expectedTestClassName, expectedTestMethodName));
assertTrue(mInstrumentationTestRunner.isStarted());
assertFalse(mInstrumentationTestRunner.isFinished());
}
private Bundle createBundle(String key, String value) {
Bundle bundle = new Bundle();
bundle.putString(key, value);
return bundle;
}
private static class StubInstrumentationTestRunner extends InstrumentationTestRunner {
private Context mContext;
private Context mTargetContext;
private boolean mStarted;
private boolean mFinished;
private AndroidTestRunner mAndroidTestRunner;
private TestSuite mTestSuite;
private TestSuite mDefaultTestSuite;
private String mPackageNameForDefaultTests;
private Bundle mResults;
public StubInstrumentationTestRunner(Context context, Context targetContext,
AndroidTestRunner androidTestRunner) {
this.mContext = context;
this.mTargetContext = targetContext;
this.mAndroidTestRunner = androidTestRunner;
}
public Context getContext() {
return mContext;
}
public TestSuite getAllTests() {
return mTestSuite;
}
public Context getTargetContext() {
return mTargetContext;
}
protected AndroidTestRunner getAndroidTestRunner() {
return mAndroidTestRunner;
}
public void start() {
mStarted = true;
}
public void finish(int resultCode, Bundle results) {
mFinished = true;
mResults = results;
}
public boolean isStarted() {
return mStarted;
}
public boolean isFinished() {
return mFinished;
}
public void setAllTestsSuite(TestSuite testSuite) {
mTestSuite = testSuite;
}
public void setDefaultTestsSuite(TestSuite testSuite) {
mDefaultTestSuite = testSuite;
}
public String getPackageNameForDefaultTests() {
return mPackageNameForDefaultTests;
}
@Override
void prepareLooper() {
// ignore
}
}
private static class StubContext extends MockContext {
private String mPackageName;
public StubContext(String packageName) {
this.mPackageName = packageName;
}
@Override
public String getPackageCodePath() {
return mPackageName;
}
@Override
public String getPackageName() {
return mPackageName;
}
@Override
public ClassLoader getClassLoader() {
return getClass().getClassLoader();
}
}
private static class StubAndroidTestRunner extends AndroidTestRunner {
private Test mTest;
private boolean mRun;
public boolean isRun() {
return mRun;
}
public void setTest(Test test) {
super.setTest(test);
mTest = test;
}
public Test getTest() {
return mTest;
}
public void runTest() {
super.runTest();
mRun = true;
}
}
/**
* Empty test used for validation
*/
public static class PlaceHolderTest extends TestCase {
public PlaceHolderTest() {
super("testPlaceHolder");
}
public void testPlaceHolder() throws Exception {
}
}
/**
* Empty test used for validation
*/
public static class PlaceHolderTest2 extends TestCase {
public PlaceHolderTest2() {
super("testPlaceHolder2");
}
public void testPlaceHolder2() throws Exception {
}
}
/**
* Annotated test used for validation.
*/
public static class AnnotationTest extends TestCase {
public void testNotAnnotated() throws Exception {
}
@FlakyTest
public void testAnnotated() throws Exception {
}
}
}