mirror of https://github.com/tasks/tasks
Inject tests with dagger
parent
06ba4e9441
commit
1cc6698ad7
@ -1,61 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2012 Todoroo Inc
|
||||
*
|
||||
* See the file "LICENSE" for the full license governing this code.
|
||||
*/
|
||||
package com.todoroo.andlib.service;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* A Dependency Injector knows how to inject certain dependencies based
|
||||
* on the field that is passed in. You will need to write your own initialization
|
||||
* code to insert this dependency injector into the DI chain.
|
||||
*
|
||||
* @author Tim Su <tim@todoroo.com>
|
||||
*
|
||||
*/
|
||||
abstract public class AbstractDependencyInjector {
|
||||
|
||||
/**
|
||||
* Initialize list of injection variables. Special care must used when
|
||||
* instantiating classes that themselves depend on dependency injection
|
||||
*/
|
||||
protected void addInjectables(Context context) {
|
||||
// your injectables here
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
protected AbstractDependencyInjector(Context context) {
|
||||
addInjectables(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dependencies this class knows how to handle
|
||||
*/
|
||||
protected final HashMap<String, Object> injectables = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Gets the injected object for this field. If implementing class does not
|
||||
* know how to handle this dependency, it should return null
|
||||
*
|
||||
* @param field
|
||||
* field tagged with {link Autowired} annotation
|
||||
* @return object to assign to this field, or null
|
||||
*/
|
||||
public Object getInjection(Field field) {
|
||||
return injectables.get(field.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName();
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2012 Todoroo Inc
|
||||
*
|
||||
* See the file "LICENSE" for the full license governing this code.
|
||||
*/
|
||||
package com.todoroo.andlib.service;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Autowired is an annotation that tells the dependency injector to
|
||||
* set up the class as appropriate.
|
||||
*
|
||||
* @author Tim Su <tim@todoroo.com>
|
||||
*
|
||||
*/
|
||||
@Target({ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Autowired {
|
||||
//
|
||||
}
|
@ -1,149 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2012 Todoroo Inc
|
||||
*
|
||||
* See the file "LICENSE" for the full license governing this code.
|
||||
*/
|
||||
package com.todoroo.andlib.service;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.LinkedList;
|
||||
|
||||
/**
|
||||
* Simple Dependency Injection Service for Android.
|
||||
* <p>
|
||||
* Add dependency injectors to the injector chain, then invoke this method
|
||||
* against classes you wish to perform dependency injection for.
|
||||
* <p>
|
||||
* All errors encountered are handled as warnings, so if dependency injection
|
||||
* seems to be failing, check the logs for more information.
|
||||
*
|
||||
* @author Tim Su <tim@todoroo.com>
|
||||
*
|
||||
*/
|
||||
public class DependencyInjectionService {
|
||||
|
||||
/**
|
||||
* Dependency injectors. Use getters and setters to modify this list
|
||||
*/
|
||||
private final LinkedList<AbstractDependencyInjector> injectors = new LinkedList<>();
|
||||
|
||||
/**
|
||||
* Perform dependency injection in the caller object
|
||||
*
|
||||
* @param caller
|
||||
* object to perform DI on
|
||||
*/
|
||||
public void inject(Object caller) {
|
||||
// Traverse through class and all parent classes, looking for
|
||||
// fields declared with the @Autowired annotation and using
|
||||
// dependency injection to set them as appropriate
|
||||
|
||||
Class<?> cls = caller.getClass();
|
||||
while(cls != null) {
|
||||
String packageName = cls.getPackage().getName();
|
||||
if(!isQualifiedPackage(packageName)) {
|
||||
break;
|
||||
}
|
||||
|
||||
for(Field field : cls.getDeclaredFields()) {
|
||||
if(field.getAnnotation(Autowired.class) != null) {
|
||||
field.setAccessible(true);
|
||||
try {
|
||||
handleField(caller, field);
|
||||
} catch (IllegalStateException | IllegalAccessException | IllegalArgumentException e) {
|
||||
throw new RuntimeException(String.format("Unable to set field '%s' of type '%s'",
|
||||
field.getName(), field.getType()), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cls = cls.getSuperclass();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isQualifiedPackage(String packageName) {
|
||||
if(packageName.startsWith("com.todoroo")) {
|
||||
return true;
|
||||
}
|
||||
if(packageName.startsWith("org.weloveastrid")) {
|
||||
return true;
|
||||
}
|
||||
if(packageName.startsWith("org.tasks")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the appropriate dependency object based on the type
|
||||
* that this autowired field accepts
|
||||
*
|
||||
* @param caller
|
||||
* calling object
|
||||
* @param field
|
||||
* field to inject
|
||||
*/
|
||||
private synchronized void handleField(Object caller, Field field)
|
||||
throws IllegalStateException, IllegalArgumentException,
|
||||
IllegalAccessException {
|
||||
|
||||
if(field.getType().isPrimitive()) {
|
||||
throw new IllegalStateException(String.format(
|
||||
"Tried to dependency-inject primative field '%s' of type '%s'",
|
||||
field.getName(), field.getType()));
|
||||
}
|
||||
|
||||
// field has already been processed, ignore
|
||||
if (field.get(caller) != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (AbstractDependencyInjector injector : injectors) {
|
||||
Object injection = injector.getInjection(field);
|
||||
if (injection != null) {
|
||||
field.set(caller, injection);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalStateException(
|
||||
String.format("No dependency injector found for autowired " +
|
||||
"field '%s' in class '%s'. Injectors: %s",
|
||||
field.getName(), caller.getClass().getName(),
|
||||
injectors));
|
||||
}
|
||||
|
||||
// --- static methods
|
||||
|
||||
private static DependencyInjectionService instance = null;
|
||||
|
||||
DependencyInjectionService() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the singleton instance of the dependency injection service.
|
||||
*/
|
||||
public synchronized static DependencyInjectionService getInstance() {
|
||||
if(instance == null) {
|
||||
instance = new DependencyInjectionService();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the supplied injector
|
||||
*/
|
||||
public synchronized void removeInjector(AbstractDependencyInjector injector) {
|
||||
injectors.remove(injector);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a Dependency Injector to the front of the list
|
||||
*/
|
||||
public synchronized void addInjector(AbstractDependencyInjector injector) {
|
||||
removeInjector(injector);
|
||||
|
||||
this.injectors.addFirst(injector);
|
||||
}
|
||||
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2012 Todoroo Inc
|
||||
*
|
||||
* See the file "LICENSE" for the full license governing this code.
|
||||
*/
|
||||
package com.todoroo.andlib.service;
|
||||
|
||||
|
||||
public class TestDependencyInjector extends AbstractDependencyInjector {
|
||||
|
||||
private String name;
|
||||
|
||||
public TestDependencyInjector(String name) {
|
||||
super(null);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void addInjectable(String field, Object injection) {
|
||||
injectables.put(field, injection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TestDI:" + name;
|
||||
}
|
||||
|
||||
// --- static stuff
|
||||
|
||||
/**
|
||||
* Install TestDependencyInjector above other injectors
|
||||
*/
|
||||
public synchronized static TestDependencyInjector initialize(String name) {
|
||||
TestDependencyInjector instance = new TestDependencyInjector(name);
|
||||
DependencyInjectionService.getInstance().addInjector(instance);
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an installed TestDependencyInjector
|
||||
*/
|
||||
public static void deinitialize(TestDependencyInjector instance) {
|
||||
DependencyInjectionService.getInstance().removeInjector(instance);
|
||||
}
|
||||
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2012 Todoroo Inc
|
||||
*
|
||||
* See the file "LICENSE" for the full license governing this code.
|
||||
*/
|
||||
package com.todoroo.andlib.test;
|
||||
|
||||
import com.todoroo.andlib.service.TestDependencyInjector;
|
||||
|
||||
/**
|
||||
* Base test case for Astrid tests that need a separate injection context
|
||||
*
|
||||
* @author Tim Su <tim@todoroo.com>
|
||||
*
|
||||
*/
|
||||
abstract public class TodorooTestCaseWithInjector extends TodorooTestCase {
|
||||
|
||||
protected TestDependencyInjector testInjector;
|
||||
|
||||
abstract protected void addInjectables();
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
testInjector = TestDependencyInjector.initialize("test");
|
||||
addInjectables();
|
||||
|
||||
super.setUp();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
super.tearDown();
|
||||
|
||||
TestDependencyInjector.deinitialize(testInjector);
|
||||
}
|
||||
|
||||
}
|
@ -1,186 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2012 Todoroo Inc
|
||||
*
|
||||
* See the file "LICENSE" for the full license governing this code.
|
||||
*/
|
||||
package com.todoroo.astrid.service;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.todoroo.andlib.service.AbstractDependencyInjector;
|
||||
import com.todoroo.andlib.service.DependencyInjectionService;
|
||||
import com.todoroo.astrid.dao.Database;
|
||||
import com.todoroo.astrid.dao.MetadataDao;
|
||||
import com.todoroo.astrid.dao.StoreObjectDao;
|
||||
import com.todoroo.astrid.dao.TagDataDao;
|
||||
import com.todoroo.astrid.dao.TagMetadataDao;
|
||||
import com.todoroo.astrid.dao.TaskAttachmentDao;
|
||||
import com.todoroo.astrid.dao.TaskDao;
|
||||
import com.todoroo.astrid.dao.TaskListMetadataDao;
|
||||
import com.todoroo.astrid.dao.UserActivityDao;
|
||||
import com.todoroo.astrid.gtasks.GtasksListService;
|
||||
import com.todoroo.astrid.gtasks.GtasksMetadataService;
|
||||
import com.todoroo.astrid.gtasks.GtasksPreferenceService;
|
||||
import com.todoroo.astrid.gtasks.GtasksScheduler;
|
||||
import com.todoroo.astrid.gtasks.GtasksTaskListUpdater;
|
||||
import com.todoroo.astrid.gtasks.sync.GtasksSyncService;
|
||||
import com.todoroo.astrid.gtasks.sync.GtasksSyncV2Provider;
|
||||
import com.todoroo.astrid.reminders.ReminderService;
|
||||
import com.todoroo.astrid.tags.TagService;
|
||||
|
||||
import org.tasks.Broadcaster;
|
||||
import org.tasks.filters.FilterCounter;
|
||||
import org.tasks.injection.TestInjector;
|
||||
import org.tasks.preferences.Preferences;
|
||||
import org.tasks.scheduling.RefreshScheduler;
|
||||
import org.tasks.widget.WidgetHelper;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
import static org.tasks.injection.TasksModule.ForApplication;
|
||||
|
||||
/**
|
||||
* Astrid application dependency injector loads classes in Astrid with the
|
||||
* appropriate instantiated objects necessary for their operation. For
|
||||
* more information on Dependency Injection, see {@link com.todoroo.andlib.service.DependencyInjectionService}
|
||||
* and {@link com.todoroo.andlib.service.AbstractDependencyInjector}.
|
||||
*
|
||||
* @author Tim Su <tim@todoroo.com>
|
||||
*
|
||||
*/
|
||||
public class AstridDependencyInjector extends AbstractDependencyInjector {
|
||||
|
||||
@Module(
|
||||
injects = {
|
||||
AstridDependencyInjector.class
|
||||
}
|
||||
)
|
||||
public static class TestModule {
|
||||
private Context context;
|
||||
|
||||
public TestModule(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
@ForApplication
|
||||
public Context getContext() {
|
||||
return context;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Boolean bit to prevent multiple copies of this injector to be loaded
|
||||
*/
|
||||
private static AstridDependencyInjector instance = null;
|
||||
|
||||
@Inject Database database;
|
||||
@Inject MetadataDao metadataDao;
|
||||
@Inject TagDataDao tagDataDao;
|
||||
@Inject Broadcaster broadcaster;
|
||||
@Inject TaskDao taskDao;
|
||||
@Inject TagMetadataDao tagMetadataDao;
|
||||
@Inject StoreObjectDao storeObjectDao;
|
||||
@Inject UserActivityDao userActivityDao;
|
||||
@Inject TaskAttachmentDao taskAttachmentDao;
|
||||
@Inject TaskListMetadataDao taskListMetadataDao;
|
||||
@Inject TagDataService tagDataService;
|
||||
@Inject MetadataService metadataService;
|
||||
@Inject SyncV2Service syncV2Service;
|
||||
@Inject FilterCounter filterCounter;
|
||||
@Inject RefreshScheduler refreshScheduler;
|
||||
@Inject TaskService taskService;
|
||||
@Inject TagService tagService;
|
||||
@Inject UpgradeService upgradeService;
|
||||
@Inject GtasksPreferenceService gtasksPreferenceService;
|
||||
@Inject GtasksListService gtasksListService;
|
||||
@Inject GtasksMetadataService gtasksMetadataService;
|
||||
@Inject GtasksSyncService gtasksSyncService;
|
||||
@Inject GtasksTaskListUpdater gtasksTaskListUpdater;
|
||||
@Inject GtasksSyncV2Provider gtasksSyncV2Provider;
|
||||
@Inject WidgetHelper widgetHelper;
|
||||
@Inject GtasksScheduler gtasksScheduler;
|
||||
@Inject ReminderService reminderService;
|
||||
@Inject Preferences preferences;
|
||||
|
||||
/**
|
||||
* Initialize list of injectables. Special care must used when
|
||||
* instantiating classes that themselves depend on dependency injection
|
||||
*/
|
||||
@Override
|
||||
protected void addInjectables(Context context) {
|
||||
new TestInjector(context)
|
||||
.inject(this, new TestModule(context));
|
||||
|
||||
// com.todoroo.astrid.dao
|
||||
injectables.put("database", database);
|
||||
injectables.put("taskDao", taskDao);
|
||||
injectables.put("metadataDao", metadataDao);
|
||||
injectables.put("tagMetadataDao", tagMetadataDao);
|
||||
injectables.put("tagDataDao", tagDataDao);
|
||||
injectables.put("storeObjectDao", storeObjectDao);
|
||||
injectables.put("userActivityDao", userActivityDao);
|
||||
injectables.put("taskAttachmentDao", taskAttachmentDao);
|
||||
injectables.put("taskListMetadataDao", taskListMetadataDao);
|
||||
|
||||
// com.todoroo.astrid.service
|
||||
injectables.put("taskService", taskService);
|
||||
injectables.put("metadataService", metadataService);
|
||||
injectables.put("tagDataService", tagDataService);
|
||||
injectables.put("upgradeService", upgradeService);
|
||||
injectables.put("syncService", syncV2Service);
|
||||
|
||||
// com.todoroo.astrid.gtasks
|
||||
injectables.put("gtasksPreferenceService", gtasksPreferenceService);
|
||||
injectables.put("gtasksListService", gtasksListService);
|
||||
injectables.put("gtasksMetadataService", gtasksMetadataService);
|
||||
injectables.put("gtasksTaskListUpdater", gtasksTaskListUpdater);
|
||||
injectables.put("gtasksSyncService", gtasksSyncService);
|
||||
injectables.put("gtasksSyncV2Provider", gtasksSyncV2Provider);
|
||||
|
||||
// com.todoroo.astrid.tags
|
||||
injectables.put("tagService", tagService);
|
||||
|
||||
injectables.put("broadcaster", broadcaster);
|
||||
|
||||
injectables.put("filterCounter", filterCounter);
|
||||
injectables.put("refreshScheduler", refreshScheduler);
|
||||
injectables.put("widgetHelper", widgetHelper);
|
||||
injectables.put("gtasksScheduler", gtasksScheduler);
|
||||
injectables.put("reminderService", reminderService);
|
||||
injectables.put("preferences", preferences);
|
||||
}
|
||||
|
||||
/**
|
||||
* Install this service as the default Dependency Injector
|
||||
*/
|
||||
private static void initialize(Context context) {
|
||||
if(instance != null) {
|
||||
return;
|
||||
}
|
||||
synchronized(AstridDependencyInjector.class) {
|
||||
if(instance == null) {
|
||||
instance = new AstridDependencyInjector(context);
|
||||
}
|
||||
DependencyInjectionService.getInstance().addInjector(instance);
|
||||
}
|
||||
}
|
||||
|
||||
AstridDependencyInjector(Context context) {
|
||||
// prevent instantiation
|
||||
super(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush dependency injection cache. Useful for unit tests.
|
||||
*/
|
||||
public synchronized static void reset(Context context) {
|
||||
instance = null;
|
||||
initialize(context);
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package org.tasks.injection;
|
||||
|
||||
import com.todoroo.andlib.test.TodorooTestCase;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
|
||||
public abstract class InjectingTestCase extends TodorooTestCase {
|
||||
|
||||
@Override
|
||||
protected void setUp() {
|
||||
super.setUp();
|
||||
|
||||
new TestInjector(getContext()).inject(this, getModules().toArray());
|
||||
}
|
||||
|
||||
protected List<Object> getModules() {
|
||||
return emptyList();
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
package org.tasks.injection;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.todoroo.astrid.backup.BackupServiceTests;
|
||||
import com.todoroo.astrid.dao.Database;
|
||||
import com.todoroo.astrid.dao.MetadataDaoTests;
|
||||
import com.todoroo.astrid.dao.TaskDaoTests;
|
||||
import com.todoroo.astrid.gtasks.GtasksIndentActionTest;
|
||||
import com.todoroo.astrid.gtasks.GtasksTaskListUpdaterTest;
|
||||
import com.todoroo.astrid.gtasks.GtasksTaskMovingTest;
|
||||
import com.todoroo.astrid.model.TaskTest;
|
||||
import com.todoroo.astrid.provider.Astrid3ProviderTests;
|
||||
import com.todoroo.astrid.reminders.NotificationTests;
|
||||
import com.todoroo.astrid.repeats.NewRepeatTests;
|
||||
import com.todoroo.astrid.service.QuickAddMarkupTest;
|
||||
import com.todoroo.astrid.service.TitleParserTest;
|
||||
import com.todoroo.astrid.subtasks.SubtasksTestCase;
|
||||
import com.todoroo.astrid.sync.NewSyncTestCase;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
@Module(injects = {
|
||||
BackupServiceTests.class,
|
||||
MetadataDaoTests.class,
|
||||
TaskDaoTests.class,
|
||||
GtasksIndentActionTest.class,
|
||||
GtasksTaskListUpdaterTest.class,
|
||||
GtasksTaskMovingTest.class,
|
||||
Astrid3ProviderTests.class,
|
||||
NotificationTests.class,
|
||||
NewRepeatTests.class,
|
||||
QuickAddMarkupTest.class,
|
||||
TitleParserTest.class,
|
||||
SubtasksTestCase.class,
|
||||
NewSyncTestCase.class,
|
||||
TaskTest.class
|
||||
})
|
||||
public class TestModule {
|
||||
private Context context;
|
||||
|
||||
public TestModule(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
public Database getDatabase() {
|
||||
return new Database() {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "databasetest";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
@TasksModule.ForApplication
|
||||
public Context getContext() {
|
||||
return context;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue