Adjust test case setup and fix failing tests

- add missing call super.setUp() in LoggingTest
- make use ofGBApplication's logger and db support instead
  of adding specific test things. Avoids differences between
  the local test things and the global GBApplication instances.
master
cpfeiffer 2017-04-25 21:51:53 +02:00
parent 534eb385f7
commit d9b0d639b8
9 changed files with 139 additions and 104 deletions

View File

@ -88,11 +88,18 @@ public class GBApplication extends Application {
public static final String ACTION_QUIT
= "nodomain.freeyourgadget.gadgetbridge.gbapplication.action.quit";
private static GBApplication app;
private static Logging logging = new Logging() {
@Override
protected String createLogDirectory() throws IOException {
File dir = FileUtils.getExternalFilesDir();
return dir.getAbsolutePath();
if (GBEnvironment.env().isLocalTest()) {
return System.getProperty(Logging.PROP_LOGFILES_DIR);
} else {
File dir = FileUtils.getExternalFilesDir();
return dir.getAbsolutePath();
}
}
};
@ -110,12 +117,17 @@ public class GBApplication extends Application {
// don't do anything here, add it to onCreate instead
}
public static Logging getLogging() {
return logging;
}
protected DeviceService createDeviceService() {
return new GBDeviceService(this);
}
@Override
public void onCreate() {
app = this;
super.onCreate();
if (lockHandler != null) {
@ -137,9 +149,12 @@ public class GBApplication extends Application {
setupExceptionHandler();
GB.environment = GBEnvironment.createDeviceEnvironment();
setupDatabase(this);
if (!GBEnvironment.isEnvironmentSetup()) {
GBEnvironment.setupEnvironment(GBEnvironment.createDeviceEnvironment());
// setup db after the environment is set up, but don't do it in test mode
// in test mode, it's done individually, see TestBase
setupDatabase();
}
deviceManager = new DeviceManager(this);
@ -195,8 +210,14 @@ public class GBApplication extends Application {
return prefs.getBoolean("minimize_priority", false);
}
static void setupDatabase(Context context) {
DBOpenHelper helper = new DBOpenHelper(context, DATABASE_NAME, null);
public void setupDatabase() {
DaoMaster.OpenHelper helper;
GBEnvironment env = GBEnvironment.env();
if (env.isTest()) {
helper = new DaoMaster.DevOpenHelper(this, null, null);
} else {
helper = new DBOpenHelper(this, DATABASE_NAME, null);
}
SQLiteDatabase db = helper.getWritableDatabase();
DaoMaster daoMaster = new DaoMaster(db);
if (lockHandler == null) {
@ -471,4 +492,8 @@ public class GBApplication extends Application {
public DeviceManager getDeviceManager() {
return deviceManager;
}
public static GBApplication app() {
return app;
}
}

View File

@ -20,6 +20,10 @@ package nodomain.freeyourgadget.gadgetbridge;
* Some more or less useful utility methods to aid local (non-device) testing.
*/
public class GBEnvironment {
// DO NOT USE A LOGGER HERE. Will break LoggingTest!
// private static final Logger LOG = LoggerFactory.getLogger(GBEnvironment.class);
private static GBEnvironment environment;
private boolean localTest;
private boolean deviceTest;
@ -41,4 +45,15 @@ public class GBEnvironment {
return localTest;
}
public static synchronized GBEnvironment env() {
return environment;
}
static synchronized boolean isEnvironmentSetup() {
return environment != null;
}
public synchronized static void setupEnvironment(GBEnvironment env) {
environment = env;
}
}

View File

@ -20,7 +20,6 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.database.DBOpenHelper;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoMaster;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
@ -36,7 +35,7 @@ public class LockHandler implements DBHandler {
public LockHandler() {
}
public void init(DaoMaster daoMaster, DBOpenHelper helper) {
public void init(DaoMaster daoMaster, DaoMaster.OpenHelper helper) {
if (isValid()) {
throw new IllegalStateException("DB must be closed before initializing it again");
}
@ -82,7 +81,7 @@ public class LockHandler implements DBHandler {
throw new IllegalStateException("session must be null");
}
// this will create completely new db instances and in turn update this handler through #init()
GBApplication.setupDatabase(GBApplication.getContext());
GBApplication.app().setupDatabase();
}
@Override

View File

@ -33,6 +33,7 @@ import java.util.List;
import de.greenrobot.dao.query.QueryBuilder;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.GBException;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
import nodomain.freeyourgadget.gadgetbridge.entities.CalendarSyncState;
@ -96,76 +97,76 @@ public class CalendarReceiver extends BroadcastReceiver {
}
public void syncCalendar(List<CalendarEvents.CalendarEvent> eventList) {
LOG.info("Syncing with calendar.");
Hashtable<Long, CalendarEvents.CalendarEvent> eventTable = new Hashtable<>();
try (DBHandler dbHandler = GBApplication.acquireDB()) {
DaoSession session = dbHandler.getDaoSession();
Long deviceId = DBHelper.getDevice(mGBDevice, session).getId();
QueryBuilder<CalendarSyncState> qb = session.getCalendarSyncStateDao().queryBuilder();
for (CalendarEvents.CalendarEvent e : eventList) {
long id = e.getId();
eventTable.put(id, e);
if (!eventState.containsKey(e.getId())) {
qb = session.getCalendarSyncStateDao().queryBuilder();
CalendarSyncState calendarSyncState = qb.where(qb.and(CalendarSyncStateDao.Properties.DeviceId.eq(deviceId), CalendarSyncStateDao.Properties.CalendarEntryId.eq(id)))
.build().unique();
if (calendarSyncState == null) {
eventState.put(id, new EventSyncState(e, EventState.NOT_SYNCED));
LOG.info("event id=" + id + " is yet unknown to device id=" + deviceId);
} else if (calendarSyncState.getHash() == e.hashCode()) {
eventState.put(id, new EventSyncState(e, EventState.SYNCED));
LOG.info("event id=" + id + " is up to date on device id=" + deviceId);
}
else {
eventState.put(id, new EventSyncState(e, EventState.NEEDS_UPDATE));
LOG.info("event id=" + id + " is not up to date on device id=" + deviceId);
}
}
}
// add all missing calendar ids on the device to sync status (so that they are deleted later)
List<CalendarSyncState> CalendarSyncStateList = qb.where(CalendarSyncStateDao.Properties.DeviceId.eq(deviceId)).build().list();
for (CalendarSyncState CalendarSyncState : CalendarSyncStateList) {
if (!eventState.containsKey(CalendarSyncState.getCalendarEntryId())) {
eventState.put(CalendarSyncState.getCalendarEntryId(), new EventSyncState(null, EventState.NEEDS_DELETE));
LOG.info("insert null event for orphanded calendar id=" + CalendarSyncState.getCalendarEntryId() + " for device=" + mGBDevice.getName());
}
}
Enumeration<Long> ids = eventState.keys();
while (ids.hasMoreElements()) {
qb = session.getCalendarSyncStateDao().queryBuilder();
Long i = ids.nextElement();
EventSyncState es = eventState.get(i);
if (eventTable.containsKey(i)) {
if (es.getState() == EventState.SYNCED) {
if (!es.getEvent().equals(eventTable.get(i))) {
eventState.put(i, new EventSyncState(eventTable.get(i), EventState.NEEDS_UPDATE));
}
}
} else {
if (es.getState() == EventState.NOT_SYNCED) {
// delete for current device only
qb.where(qb.and(CalendarSyncStateDao.Properties.DeviceId.eq(deviceId), CalendarSyncStateDao.Properties.CalendarEntryId.eq(i)))
.buildDelete().executeDeleteWithoutDetachingEntities();
eventState.remove(i);
} else {
es.setState(EventState.NEEDS_DELETE);
eventState.put(i, es);
}
}
updateEvents(deviceId, session);
}
} catch (Exception e) {
e.printStackTrace();
syncCalendar(eventList, session);
} catch (Exception e1) {
GB.toast("Database Error while syncing Calendar", Toast.LENGTH_SHORT, GB.ERROR);
}
}
public void syncCalendar(List<CalendarEvents.CalendarEvent> eventList, DaoSession session) {
LOG.info("Syncing with calendar.");
Hashtable<Long, CalendarEvents.CalendarEvent> eventTable = new Hashtable<>();
Long deviceId = DBHelper.getDevice(mGBDevice, session).getId();
QueryBuilder<CalendarSyncState> qb = session.getCalendarSyncStateDao().queryBuilder();
for (CalendarEvents.CalendarEvent e : eventList) {
long id = e.getId();
eventTable.put(id, e);
if (!eventState.containsKey(e.getId())) {
qb = session.getCalendarSyncStateDao().queryBuilder();
CalendarSyncState calendarSyncState = qb.where(qb.and(CalendarSyncStateDao.Properties.DeviceId.eq(deviceId), CalendarSyncStateDao.Properties.CalendarEntryId.eq(id)))
.build().unique();
if (calendarSyncState == null) {
eventState.put(id, new EventSyncState(e, EventState.NOT_SYNCED));
LOG.info("event id=" + id + " is yet unknown to device id=" + deviceId);
} else if (calendarSyncState.getHash() == e.hashCode()) {
eventState.put(id, new EventSyncState(e, EventState.SYNCED));
LOG.info("event id=" + id + " is up to date on device id=" + deviceId);
}
else {
eventState.put(id, new EventSyncState(e, EventState.NEEDS_UPDATE));
LOG.info("event id=" + id + " is not up to date on device id=" + deviceId);
}
}
}
// add all missing calendar ids on the device to sync status (so that they are deleted later)
List<CalendarSyncState> CalendarSyncStateList = qb.where(CalendarSyncStateDao.Properties.DeviceId.eq(deviceId)).build().list();
for (CalendarSyncState CalendarSyncState : CalendarSyncStateList) {
if (!eventState.containsKey(CalendarSyncState.getCalendarEntryId())) {
eventState.put(CalendarSyncState.getCalendarEntryId(), new EventSyncState(null, EventState.NEEDS_DELETE));
LOG.info("insert null event for orphanded calendar id=" + CalendarSyncState.getCalendarEntryId() + " for device=" + mGBDevice.getName());
}
}
Enumeration<Long> ids = eventState.keys();
while (ids.hasMoreElements()) {
qb = session.getCalendarSyncStateDao().queryBuilder();
Long i = ids.nextElement();
EventSyncState es = eventState.get(i);
if (eventTable.containsKey(i)) {
if (es.getState() == EventState.SYNCED) {
if (!es.getEvent().equals(eventTable.get(i))) {
eventState.put(i, new EventSyncState(eventTable.get(i), EventState.NEEDS_UPDATE));
}
}
} else {
if (es.getState() == EventState.NOT_SYNCED) {
// delete for current device only
qb.where(qb.and(CalendarSyncStateDao.Properties.DeviceId.eq(deviceId), CalendarSyncStateDao.Properties.CalendarEntryId.eq(i)))
.buildDelete().executeDeleteWithoutDetachingEntities();
eventState.remove(i);
} else {
es.setState(EventState.NEEDS_DELETE);
eventState.put(i, es);
}
}
updateEvents(deviceId, session);
}
}
private void updateEvents(Long deviceId, DaoSession session) {

View File

@ -38,6 +38,7 @@ import java.util.ArrayList;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.GBEnvironment;
public class FileUtils {
// Don't use slf4j here -- would be a bootstrapping problem
@ -209,9 +210,11 @@ public class FileUtils {
// the first directory is also the primary external storage, i.e. the same as Environment.getExternalFilesDir()
// TODO: check the mount state of *all* dirs when switching to later API level
if (i == 0 && !Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
GB.log("ignoring unmounted external storage dir: " + dir, GB.INFO, null);
continue;
if (!GBEnvironment.env().isLocalTest()) { // don't do this with robolectric
if (i == 0 && !Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
GB.log("ignoring unmounted external storage dir: " + dir, GB.INFO, null);
continue;
}
}
result.add(dir); // add last
}

View File

@ -60,10 +60,9 @@ public class GB {
public static final String DISPLAY_MESSAGE_MESSAGE = "message";
public static final String DISPLAY_MESSAGE_DURATION = "duration";
public static final String DISPLAY_MESSAGE_SEVERITY = "severity";
public static GBEnvironment environment;
public static Notification createNotification(String text, boolean connected, Context context) {
if (env().isLocalTest()) {
if (GBEnvironment.env().isLocalTest()) {
return null;
}
Intent notificationIntent = new Intent(context, ControlCenterv2.class);
@ -227,7 +226,7 @@ public class GB {
*/
public static void toast(final Context context, final String message, final int displayTime, final int severity, final Throwable ex) {
log(message, severity, ex); // log immediately, not delayed
if (env().isLocalTest()) {
if (GBEnvironment.env().isLocalTest()) {
return;
}
Looper mainLooper = Looper.getMainLooper();
@ -358,7 +357,7 @@ public class GB {
}
public static void updateBatteryNotification(String text, String bigText, Context context) {
if (env().isLocalTest()) {
if (GBEnvironment.env().isLocalTest()) {
return;
}
Notification notification = createBatteryNotification(text, bigText, context);
@ -369,10 +368,6 @@ public class GB {
removeNotification(NOTIFICATION_ID_LOW_BATTERY, context);
}
public static GBEnvironment env() {
return environment;
}
public static void assertThat(boolean condition, String errorMessage) {
if (!condition) {
throw new AssertionError(errorMessage);

View File

@ -5,7 +5,9 @@ import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
import nodomain.freeyourgadget.gadgetbridge.entities.CalendarSyncStateDao;
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
import nodomain.freeyourgadget.gadgetbridge.externalevents.CalendarReceiver;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEvents;

View File

@ -6,8 +6,8 @@ import org.junit.After;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.Logging;
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
@ -25,16 +25,12 @@ public class LoggingTest extends TestBase {
public LoggingTest() throws Exception {
}
private Logging logging = new Logging() {
@Override
protected String createLogDirectory() throws IOException {
return logFilesDir.getAbsolutePath();
}
};
private Logging logging = GBApplication.getLogging();
@Override
@After
public void tearDown() {
public void tearDown() throws Exception {
super.tearDown();
assertTrue(FileUtils.deleteRecursively(getLogFilesDir()));
}

View File

@ -1,7 +1,6 @@
package nodomain.freeyourgadget.gadgetbridge.test;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import org.junit.After;
import org.junit.Before;
@ -16,9 +15,9 @@ import java.io.File;
import ch.qos.logback.classic.util.ContextInitializer;
import nodomain.freeyourgadget.gadgetbridge.BuildConfig;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.GBEnvironment;
import nodomain.freeyourgadget.gadgetbridge.Logging;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoMaster;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
@ -46,9 +45,10 @@ public abstract class TestBase {
// Make sure logging is set up for all testcases, so that we can debug problems
@BeforeClass
public static void setupSuite() throws Exception {
GBEnvironment.setupEnvironment(GBEnvironment.createLocalTestEnvironment());
// print everything going to android.util.Log to System.out
System.setProperty("robolectric.logging", "stdout");
// ShadowLog.stream = System.out;
// properties might be preconfigured in build.gradle because of test ordering problems
String logDir = System.getProperty(Logging.PROP_LOGFILES_DIR);
@ -69,20 +69,19 @@ public abstract class TestBase {
@Before
public void setUp() throws Exception {
app = (GBApplication) RuntimeEnvironment.application;
assertNotNull(app);
assertNotNull(getContext());
// doesn't work with Robolectric yet
// dbHandler = GBApplication.acquireDB();
// daoSession = dbHandler.getDaoSession();
DaoMaster.DevOpenHelper openHelper = new DaoMaster.DevOpenHelper(app, null, null);
SQLiteDatabase db = openHelper.getWritableDatabase();
daoSession = new DaoMaster(db).newSession();
app.setupDatabase();
dbHandler = GBApplication.acquireDB();
daoSession = dbHandler.getDaoSession();
assertNotNull(daoSession);
}
@After
public void tearDown() throws Exception {
// GBApplication.releaseDB();
dbHandler.closeDb();
GBApplication.releaseDB();
}
protected GBDevice createDummyGDevice(String macAddress) {