![JAR search and dependency download from the Maven repository](/logo.png)
com.codeslap.robolectric.RobolectricSimpleRunner Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of robolectric-sqlite Show documentation
Show all versions of robolectric-sqlite Show documentation
Library to use robolectric with Sqlite instead of H2
The newest version!
package com.codeslap.robolectric;
import android.app.Application;
import android.net.Uri__FromAndroid;
import com.seventheye.robolectric.sqlite.util.SQLiteMap;
import com.xtremelabs.robolectric.ApplicationResolver;
import com.xtremelabs.robolectric.Robolectric;
import com.xtremelabs.robolectric.RobolectricConfig;
import com.xtremelabs.robolectric.RobolectricTestRunner;
import com.xtremelabs.robolectric.bytecode.ClassHandler;
import com.xtremelabs.robolectric.bytecode.RobolectricClassLoader;
import com.xtremelabs.robolectric.bytecode.ShadowWrangler;
import com.xtremelabs.robolectric.internal.RealObject;
import com.xtremelabs.robolectric.internal.RobolectricTestRunnerInterface;
import com.xtremelabs.robolectric.res.ResourceLoader;
import com.xtremelabs.robolectric.shadows.ShadowApplication;
import com.xtremelabs.robolectric.util.DatabaseConfig;
import javassist.Loader;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.Statement;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
/**
* This is a simple Robolectric runner that do not break when there is no AndroidManifest.xml nor res folder.
* This is usefule when you are testing a library which will be used in Android but that is not an Android
* application per se.
*
* @author cristian
*/
public class RobolectricSimpleRunner extends BlockJUnit4ClassRunner implements RobolectricTestRunnerInterface {
private static RobolectricClassLoader defaultLoader;
private static Map resourceLoaderForRootAndDirectory = new HashMap();
// fields in the RobolectricTestRunner in the original ClassLoader
private RobolectricClassLoader classLoader;
private ClassHandler classHandler;
private RobolectricTestRunnerInterface delegate;
private DatabaseConfig.DatabaseMap databaseMap;
// fields in the RobolectricTestRunner in the instrumented ClassLoader
protected RobolectricConfig robolectricConfig;
private static RobolectricClassLoader getDefaultLoader() {
if (defaultLoader == null) {
defaultLoader = new RobolectricClassLoader(ShadowWrangler.getInstance());
}
return defaultLoader;
}
public static void setDefaultLoader(Loader robolectricClassLoader) {
//used by the RoboSpecs project to allow for mixed scala\java tests to be run with Maven Surefire (see the RoboSpecs project on github)
if (defaultLoader == null) {
defaultLoader = (RobolectricClassLoader) robolectricClassLoader;
} else throw new RuntimeException("You may not set the default robolectricClassLoader unless it is null!");
}
/**
* Creates a runner to run {@code testClass}. Looks in your working directory for your AndroidManifest.xml file
* and res directory.
*
* @param testClass the test class to be run
* @throws org.junit.runners.model.InitializationError
* if junit says so
*/
public RobolectricSimpleRunner(final Class> testClass) throws InitializationError {
this(testClass, new RobolectricConfig(new File(".")));
}
/**
* Call this constructor in subclasses in order to specify non-default configuration (e.g. location of the
* AndroidManifest.xml file and resource directory).
*
* @param testClass the test class to be run
* @param robolectricConfig the configuration data
* @throws org.junit.runners.model.InitializationError
* if junit says so
*/
protected RobolectricSimpleRunner(final Class> testClass, final RobolectricConfig robolectricConfig)
throws InitializationError {
this(testClass,
isInstrumented() ? null : ShadowWrangler.getInstance(),
isInstrumented() ? null : getDefaultLoader(),
robolectricConfig, new SQLiteMap());
}
/**
* Call this constructor in subclasses in order to specify non-default configuration (e.g. location of the
* AndroidManifest.xml file, resource directory, and DatabaseMap).
*
* @param testClass the test class to be run
* @param robolectricConfig the configuration data
* @param databaseMap the database mapping
* @throws org.junit.runners.model.InitializationError
* if junit says so
*/
protected RobolectricSimpleRunner(Class> testClass, RobolectricConfig robolectricConfig, DatabaseConfig.DatabaseMap databaseMap)
throws InitializationError {
this(testClass,
isInstrumented() ? null : ShadowWrangler.getInstance(),
isInstrumented() ? null : getDefaultLoader(),
robolectricConfig, databaseMap);
}
/**
* Call this constructor in subclasses in order to specify the project root directory.
*
* @param testClass the test class to be run
* @param androidProjectRoot the directory containing your AndroidManifest.xml file and res dir
* @throws org.junit.runners.model.InitializationError
* if the test class is malformed
*/
public RobolectricSimpleRunner(final Class> testClass, final File androidProjectRoot) throws InitializationError {
this(testClass, new RobolectricConfig(androidProjectRoot));
}
/**
* Call this constructor in subclasses in order to specify the project root directory.
*
* @param testClass the test class to be run
* @param androidProjectRoot the directory containing your AndroidManifest.xml file and res dir
* @throws org.junit.runners.model.InitializationError
* if junit says so
* @deprecated Use {@link #RobolectricSimpleRunner(Class, java.io.File)} instead.
*/
@Deprecated
public RobolectricSimpleRunner(final Class> testClass, final String androidProjectRoot) throws InitializationError {
this(testClass, new RobolectricConfig(new File(androidProjectRoot)));
}
/**
* Call this constructor in subclasses in order to specify the location of the AndroidManifest.xml file and the
* resource directory. The #androidManifestPath is used to locate the AndroidManifest.xml file which, in turn,
* contains package name for the {@code R} class which contains the identifiers for all of the resources. The
* resource directory is where the resource loader will look for resources to load.
*
* @param testClass the test class to be run
* @param androidManifestPath the AndroidManifest.xml file
* @param resourceDirectory the directory containing the project's resources
* @throws org.junit.runners.model.InitializationError
* if junit says so
*/
protected RobolectricSimpleRunner(final Class> testClass, final File androidManifestPath, final File resourceDirectory)
throws InitializationError {
this(testClass, new RobolectricConfig(androidManifestPath, resourceDirectory));
}
/**
* Call this constructor in subclasses in order to specify the location of the AndroidManifest.xml file and the
* resource directory. The #androidManifestPath is used to locate the AndroidManifest.xml file which, in turn,
* contains package name for the {@code R} class which contains the identifiers for all of the resources. The
* resource directory is where the resource loader will look for resources to load.
*
* @param testClass the test class to be run
* @param androidManifestPath the relative path to the AndroidManifest.xml file
* @param resourceDirectory the relative path to the directory containing the project's resources
* @throws org.junit.runners.model.InitializationError
* if junit says so
* @deprecated Use {@link #RobolectricSimpleRunner(Class, java.io.File, java.io.File)} instead.
*/
@Deprecated
protected RobolectricSimpleRunner(final Class> testClass, final String androidManifestPath, final String resourceDirectory)
throws InitializationError {
this(testClass, new RobolectricConfig(new File(androidManifestPath), new File(resourceDirectory)));
}
protected RobolectricSimpleRunner(Class> testClass, ClassHandler classHandler, RobolectricClassLoader classLoader, RobolectricConfig robolectricConfig) throws InitializationError {
this(testClass, classHandler, classLoader, robolectricConfig, new SQLiteMap());
}
/**
* This is not the constructor you are looking for... probably. This constructor creates a bridge between the test
* runner called by JUnit and a second instance of the test runner that is loaded via the instrumenting class
* loader. This instrumented instance of the test runner, along with the instrumented instance of the actual test,
* provides access to Robolectric's features and the un-instrumented instance of the test runner delegates most of
* the interesting test runner behavior to it. Providing your own class handler and class loader here in order to
* get different functionality is a difficult and dangerous project. If you need to customize the project root and
* resource directory, use {@link #RobolectricSimpleRunner(Class, String, String)}. For other extensions, consider
* creating a subclass and overriding the documented methods of this class.
*
* @param testClass the test class to be run
* @param classHandler the {@link com.xtremelabs.robolectric.bytecode.ClassHandler} to use to in shadow delegation
* @param classLoader the {@link com.xtremelabs.robolectric.bytecode.RobolectricClassLoader}
* @param robolectricConfig the configuration
* @throws org.junit.runners.model.InitializationError
* if junit says so
*/
protected RobolectricSimpleRunner(final Class> testClass, final ClassHandler classHandler, final RobolectricClassLoader classLoader, final RobolectricConfig robolectricConfig, final DatabaseConfig.DatabaseMap map) throws InitializationError {
super(isInstrumented() ? testClass : classLoader.bootstrap(testClass));
if (!isInstrumented()) {
this.classHandler = classHandler;
this.classLoader = classLoader;
this.robolectricConfig = robolectricConfig;
this.databaseMap = setupDatabaseMap(testClass, map);
Thread.currentThread().setContextClassLoader(classLoader);
delegateLoadingOf(Uri__FromAndroid.class.getName());
delegateLoadingOf(RobolectricTestRunnerInterface.class.getName());
delegateLoadingOf(RealObject.class.getName());
delegateLoadingOf(ShadowWrangler.class.getName());
delegateLoadingOf(RobolectricConfig.class.getName());
delegateLoadingOf(DatabaseConfig.DatabaseMap.class.getName());
delegateLoadingOf(android.R.class.getName());
Class> delegateClass = classLoader.bootstrap(this.getClass());
try {
Constructor> constructorForDelegate = delegateClass.getConstructor(Class.class);
this.delegate = (RobolectricTestRunnerInterface) constructorForDelegate.newInstance(classLoader.bootstrap(testClass));
this.delegate.setRobolectricConfig(robolectricConfig);
this.delegate.setDatabaseMap(databaseMap);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
protected static boolean isInstrumented() {
return RobolectricTestRunner.class.getClassLoader().getClass().getName().contains(RobolectricClassLoader.class.getName());
}
/**
* Only used when creating the delegate instance within the instrumented ClassLoader.
*
* This is not the constructor you are looking for.
*/
@SuppressWarnings({"UnusedDeclaration", "JavaDoc"})
protected RobolectricSimpleRunner(final Class> testClass, final ClassHandler classHandler, final RobolectricConfig robolectricConfig) throws InitializationError {
super(testClass);
this.classHandler = classHandler;
this.robolectricConfig = robolectricConfig;
}
public static void setStaticValue(Class> clazz, String fieldName, Object value) {
try {
Field field = clazz.getField(fieldName);
field.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(null, value);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
protected void delegateLoadingOf(final String className) {
classLoader.delegateLoadingOf(className);
}
@Override
protected Statement methodBlock(final FrameworkMethod method) {
setupI18nStrictState(method.getMethod(), robolectricConfig);
if (classHandler != null) {
classHandler.configure(robolectricConfig);
classHandler.beforeTest();
}
delegate.internalBeforeTest(method.getMethod());
final Statement statement = super.methodBlock(method);
return new Statement() {
@Override
public void evaluate() throws Throwable {
// todo: this try/finally probably isn't right -- should mimic RunAfters? [xw]
try {
statement.evaluate();
} finally {
delegate.internalAfterTest(method.getMethod());
if (classHandler != null) {
classHandler.afterTest();
}
}
}
};
}
/*
* Called before each test method is run. Sets up the simulation of the Android runtime environment.
*/
@Override
public void internalBeforeTest(final Method method) {
setupApplicationState(robolectricConfig);
beforeTest(method);
}
@Override
public void internalAfterTest(final Method method) {
afterTest(method);
}
@Override
public void setRobolectricConfig(final RobolectricConfig robolectricConfig) {
this.robolectricConfig = robolectricConfig;
}
/**
* Called before each test method is run.
*
* @param method the test method about to be run
*/
public void beforeTest(final Method method) {
}
/**
* Called after each test method is run.
*
* @param method the test method that just ran.
*/
public void afterTest(final Method method) {
}
/**
* You probably don't want to override this method. Override #prepareTest(Object) instead.
*
* @see org.junit.runners.BlockJUnit4ClassRunner#createTest()
*/
@Override
public Object createTest() throws Exception {
if (delegate != null) {
return delegate.createTest();
} else {
Object test = super.createTest();
prepareTest(test);
return test;
}
}
public void prepareTest(final Object test) {
}
public void setupApplicationState(final RobolectricConfig robolectricConfig) {
ResourceLoader resourceLoader = createResourceLoader(robolectricConfig);
PrintStream currentOut = System.out;
System.setOut(new PrintStream(new ByteArrayOutputStream()));
Robolectric.bindDefaultShadowClasses();
bindShadowClasses();
System.setOut(currentOut);
Robolectric.resetStaticState();
resetStaticState();
DatabaseConfig.setDatabaseMap(this.databaseMap);//Set static DatabaseMap in DBConfig
Robolectric.application = ShadowApplication.bind(createApplication(), resourceLoader);
}
/**
* Override this method to bind your own shadow classes
*/
protected void bindShadowClasses() {
}
/**
* Override this method to reset the state of static members before each test.
*/
protected void resetStaticState() {
}
/**
* Sets Robolectric config to determine if Robolectric should blacklist API calls that are not
* I18N/L10N-safe.
*
* I18n-strict mode affects suitably annotated shadow methods. Robolectric will throw exceptions
* if these methods are invoked by application code. Additionally, Robolectric's ResourceLoader
* will throw exceptions if layout resources use bare string literals instead of string resource IDs.
*
* To enable or disable i18n-strict mode for specific test cases, annotate them with
* {@link com.xtremelabs.robolectric.annotation.EnableStrictI18n} or
* {@link com.xtremelabs.robolectric.annotation.DisableStrictI18n}.
*
*
* By default, I18n-strict mode is disabled.
*
* @param method
* @param robolectricConfig
*/
private void setupI18nStrictState(Method method, RobolectricConfig robolectricConfig) {
// Global
boolean strictI18n = globalI18nStrictEnabled();
// Test case class
Annotation[] annos = method.getDeclaringClass().getAnnotations();
strictI18n = lookForI18nAnnotations(strictI18n, annos);
// Test case methods
annos = method.getAnnotations();
strictI18n = lookForI18nAnnotations(strictI18n, annos);
robolectricConfig.setStrictI18n(strictI18n);
}
/**
* Default implementation of global switch for i18n-strict mode.
* To enable i18n-strict mode globally, set the system property
* "robolectric.strictI18n" to true. This can be done via java
* system properties in either Ant or Maven.
*
* Subclasses can override this method and establish their own policy
* for enabling i18n-strict mode.
*
* @return
*/
protected boolean globalI18nStrictEnabled() {
return Boolean.valueOf(System.getProperty("robolectric.strictI18n"));
}
/**
* As test methods are loaded by the delegate's class loader, the normal
* method#isAnnotationPresent test fails. Look at string versions of the
* annotation names to test for their presence.
*
* @param strictI18n
* @param annos
* @return
*/
private boolean lookForI18nAnnotations(boolean strictI18n, Annotation[] annos) {
for (int i = 0; i < annos.length; i++) {
String name = annos[i].annotationType().getName();
if (name.equals("com.xtremelabs.robolectric.annotation.EnableStrictI18n")) {
strictI18n = true;
break;
}
if (name.equals("com.xtremelabs.robolectric.annotation.DisableStrictI18n")) {
strictI18n = false;
break;
}
}
return strictI18n;
}
/**
* Override this method if you want to provide your own implementation of Application.
*
* This method attempts to instantiate an application instance as specified by the AndroidManifest.xml.
*
* @return An instance of the Application class specified by the ApplicationManifest.xml or an instance of
* Application if not specified.
*/
protected Application createApplication() {
return new ApplicationResolver(robolectricConfig).resolveApplication();
}
private ResourceLoader createResourceLoader(final RobolectricConfig robolectricConfig) {
ResourceLoader resourceLoader = resourceLoaderForRootAndDirectory.get(robolectricConfig);
if (resourceLoader == null) {
try {
robolectricConfig.validate();
String rClassName = robolectricConfig.getRClassName();
Class rClass = Class.forName(rClassName);
resourceLoader = new ResourceLoader(robolectricConfig.getRealSdkVersion(), rClass, robolectricConfig.getResourceDirectory(), robolectricConfig.getAssetsDirectory());
resourceLoaderForRootAndDirectory.put(robolectricConfig, resourceLoader);
} catch (Exception e) {
//throw new RuntimeException(e);
}
}
//resourceLoader.setStrictI18n(robolectricConfig.getStrictI18n());
return resourceLoader;
}
private String findResourcePackageName(final File projectManifestFile) throws ParserConfigurationException, IOException, SAXException {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(projectManifestFile);
String projectPackage = doc.getElementsByTagName("manifest").item(0).getAttributes().getNamedItem("package").getTextContent();
return projectPackage + ".R";
}
/*
* Specifies what database to use for testing (ex: H2 or Sqlite),
* this will load H2 by default, the SQLite TestRunner version will override this.
*/
protected DatabaseConfig.DatabaseMap setupDatabaseMap(Class> testClass, DatabaseConfig.DatabaseMap map) {
DatabaseConfig.DatabaseMap dbMap = map;
if (testClass.isAnnotationPresent(DatabaseConfig.UsingDatabaseMap.class)) {
DatabaseConfig.UsingDatabaseMap usingMap = testClass.getAnnotation(DatabaseConfig.UsingDatabaseMap.class);
if (usingMap.value() != null) {
dbMap = Robolectric.newInstanceOf(usingMap.value());
} else {
if (dbMap == null)
throw new RuntimeException("UsingDatabaseMap annotation value must provide a class implementing DatabaseMap");
}
}
return dbMap;
}
public DatabaseConfig.DatabaseMap getDatabaseMap() {
return databaseMap;
}
public void setDatabaseMap(DatabaseConfig.DatabaseMap databaseMap) {
this.databaseMap = databaseMap;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy