package org.nightscout.lasso; import android.app.Activity; import android.app.NotificationManager; import android.content.Context; import android.content.SharedPreferences; import android.media.AudioManager; import android.preference.PreferenceManager; import android.util.Log; import com.nightscout.core.dexcom.TrendArrow; import com.nightscout.core.dexcom.records.EGVRecord; import com.nightscout.core.model.G4Noise; import com.nightscout.core.model.GlucoseUnit; import com.nightscout.core.utils.GlucoseReading; import net.tribe7.common.base.Optional; import org.joda.time.DateTime; import org.joda.time.Minutes; import org.junit.Before; import org.junit.Test; import org.nightscout.lasso.alarm.Alarm; import org.nightscout.lasso.alarm.AlarmResults; import org.nightscout.lasso.alarm.AlarmSeverity; import org.nightscout.lasso.alarm.AlarmStrategy; import org.nightscout.lasso.alarm.SimpleAlarm; import org.nightscout.lasso.alarm.ar2.Ar2; import org.nightscout.lasso.test.RobolectricTestBase; import org.robolectric.Robolectric; import org.robolectric.RuntimeEnvironment; import org.robolectric.shadows.ShadowApplication; import org.robolectric.shadows.ShadowAudioManager; import org.robolectric.shadows.ShadowLog; import org.robolectric.shadows.ShadowMediaPlayer; import org.robolectric.shadows.ShadowNotificationManager; import org.robolectric.util.ActivityController; import java.util.ArrayList; import java.util.List; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; import static org.robolectric.Shadows.shadowOf; public class AlarmTest extends RobolectricTestBase { ActivityController<MainActivity> activityController; SharedPreferences preferences; Activity activity; private ShadowNotificationManager shadowNotificationManager; private ShadowMediaPlayer shadowMediaPlayer; private ShadowAudioManager shadowAudioManager; @Before public void setUp() { activity = Robolectric.buildActivity(MainActivity.class).create().get(); shadowNotificationManager = shadowOf((NotificationManager) RuntimeEnvironment.application .getSystemService(Context.NOTIFICATION_SERVICE)); shadowMediaPlayer = new ShadowMediaPlayer(); shadowAudioManager = shadowOf((AudioManager) RuntimeEnvironment.application.getSystemService(Context.AUDIO_SERVICE)); //(AudioManager) RuntimeEnvironment.application.getSystemService(Context.AUDIO_SERVICE); ShadowLog.stream = System.out; preferences = PreferenceManager.getDefaultSharedPreferences(activity.getApplicationContext()); } private void setSimpleAlarm() { preferences.edit().putString(getContext().getString(R.string.alarm_model), "0").apply(); } private void setAr2Alarm() { preferences.edit().putString(getContext().getString(R.string.alarm_model), "1").apply(); } private List<EGVRecord> normalEgvList() { List<EGVRecord> egvRecordList = new ArrayList<>(); EGVRecord egvRecord = new EGVRecord(100, TrendArrow.FLAT, new DateTime().minus(Minutes.minutes(5)), new DateTime().minus(Minutes.minutes(5)), G4Noise.CLEAN, new DateTime().minus(Minutes.minutes(5))); egvRecordList.add(egvRecord); egvRecord = new EGVRecord(100, TrendArrow.FLAT, new DateTime(), new DateTime(), G4Noise.CLEAN, new DateTime()); egvRecordList.add(egvRecord); return egvRecordList; } private List<EGVRecord> urgentHighEgvList() { List<EGVRecord> egvRecordList = new ArrayList<>(); EGVRecord egvRecord = new EGVRecord(250, TrendArrow.UP_45, new DateTime().minus(Minutes.minutes(5)), new DateTime().minus(Minutes.minutes(5)), G4Noise.CLEAN, new DateTime().minus(Minutes.minutes(5))); egvRecordList.add(egvRecord); egvRecord = new EGVRecord(265, TrendArrow.FLAT, new DateTime(), new DateTime(), G4Noise.CLEAN, new DateTime()); egvRecordList.add(egvRecord); return egvRecordList; } private List<EGVRecord> urgentLowEgvList() { List<EGVRecord> egvRecordList = new ArrayList<>(); EGVRecord egvRecord = new EGVRecord(55, TrendArrow.DOWN_45, new DateTime().minus(Minutes.minutes(5)), new DateTime().minus(Minutes.minutes(5)), G4Noise.CLEAN, new DateTime().minus(Minutes.minutes(5))); egvRecordList.add(egvRecord); egvRecord = new EGVRecord(40, TrendArrow.FLAT, new DateTime(), new DateTime(), G4Noise.CLEAN, new DateTime()); egvRecordList.add(egvRecord); return egvRecordList; } private List<EGVRecord> warnHighEgvList() { List<EGVRecord> egvRecordList = new ArrayList<>(); EGVRecord egvRecord = new EGVRecord(190, TrendArrow.UP_45, new DateTime().minus(Minutes.minutes(5)), new DateTime().minus(Minutes.minutes(5)), G4Noise.CLEAN, new DateTime().minus(Minutes.minutes(5))); egvRecordList.add(egvRecord); egvRecord = new EGVRecord(195, TrendArrow.FLAT, new DateTime(), new DateTime(), G4Noise.CLEAN, new DateTime()); egvRecordList.add(egvRecord); return egvRecordList; } private List<EGVRecord> warnLowEgvList() { List<EGVRecord> egvRecordList = new ArrayList<>(); EGVRecord egvRecord = new EGVRecord(70, TrendArrow.DOWN_45, new DateTime().minus(Minutes.minutes(5)), new DateTime().minus(Minutes.minutes(5)), G4Noise.CLEAN, new DateTime().minus(Minutes.minutes(5))); egvRecordList.add(egvRecord); egvRecord = new EGVRecord(68, TrendArrow.FLAT, new DateTime(), new DateTime(), G4Noise.CLEAN, new DateTime()); egvRecordList.add(egvRecord); return egvRecordList; } @Test public void testAlarmResultSeverityTakesHighestServerity() { AlarmResults alarmResults = new AlarmResults(); alarmResults.severity = AlarmSeverity.NONE; alarmResults.setSeverityAtHighest(AlarmSeverity.URGENT); assertThat(alarmResults.severity, is(AlarmSeverity.URGENT)); } @Test public void testAlarmResultAppendsMessage() { String expectedResults = "Test1\nTest2"; AlarmResults alarmResults = new AlarmResults(); alarmResults.message = "Test1"; alarmResults.appendMessage("Test2"); assertThat(alarmResults.message, is(expectedResults)); } @Test public void testAlarmResultMergeAlarmResultMergesProperly() { AlarmResults expectedResults = new AlarmResults(); expectedResults.severity = AlarmSeverity.URGENT; expectedResults.message = "Test1\nTest2\nTest3\nTest4"; AlarmResults alarmResults = new AlarmResults(); alarmResults.severity = AlarmSeverity.NONE; alarmResults.message = "Test1\nTest2"; AlarmResults alarmResults2 = new AlarmResults(); alarmResults2.severity = AlarmSeverity.URGENT; alarmResults2.message = "Test3\nTest4"; alarmResults.mergeAlarmResults(alarmResults2); assertThat(alarmResults, is(expectedResults)); } @Test public void testBatteryAbsentDoesNotAlarm() { Optional<Integer> battery = Optional.absent(); Alarm alarm = new Alarm(getContext()); AlarmResults alarmResults = alarm.analyzeBattery(battery); AlarmResults emptyResults = new AlarmResults(); assertThat(alarmResults, is(emptyResults)); } @Test public void testNormalBatteryDoesNotAlarm() { Optional<Integer> battery = Optional.of(95); Alarm alarm = new Alarm(getContext()); AlarmResults alarmResults = alarm.analyzeBattery(battery); AlarmResults emptyResults = new AlarmResults(); assertThat(alarmResults, is(emptyResults)); } @Test public void testWarnBatteryAlarms() { Optional<Integer> battery = Optional.of(11); Alarm alarm = new Alarm(getContext()); AlarmResults alarmResults = alarm.analyzeBattery(battery); assertThat(alarmResults.severity, is(AlarmSeverity.WARNING)); } @Test public void testUrgentBatteryAlarms() { Optional<Integer> battery = Optional.of(5); Alarm alarm = new Alarm(getContext()); AlarmResults alarmResults = alarm.analyzeBattery(battery); assertThat(alarmResults.severity, is(AlarmSeverity.URGENT)); } private List<EGVRecord> generateStaleEgvRecordList(int minutes) { DateTime staleTime = new DateTime().minus(Minutes.minutes(minutes)); List<EGVRecord> egvRecords = new ArrayList<>(); egvRecords.add(new EGVRecord(100, TrendArrow.FLAT, staleTime, staleTime, G4Noise.CLEAN, staleTime)); return egvRecords; } @Test public void testStaleAlarmDisabledWithStaleDataDoesNotAlarm() { preferences.edit().putBoolean(getContext().getString(R.string.stale_alarm_enabled), false).apply(); Alarm alarm = new Alarm(getContext()); List<EGVRecord> egvRecords = generateStaleEgvRecordList(16); AlarmResults alarmResults = alarm.analyzeTime(egvRecords, GlucoseUnit.MGDL, new DateTime().minus(Minutes.minutes(16))); AlarmResults emptyResults = new AlarmResults(); assertThat(alarmResults, is(emptyResults)); } @Test public void testStaleAlarmWithWarnStaleDataAlarms() { preferences.edit().putBoolean(getContext().getString(R.string.stale_alarm_enabled), true).apply(); Alarm alarm = new Alarm(getContext()); List<EGVRecord> egvRecords = generateStaleEgvRecordList(16); AlarmResults alarmResults = alarm.analyzeTime(egvRecords, GlucoseUnit.MGDL, new DateTime()); assertThat(alarmResults.severity, is(AlarmSeverity.WARNING)); } @Test public void testStaleAlarmWithUrgentStaleDataAlarms() { preferences.edit().putBoolean(getContext().getString(R.string.stale_alarm_enabled), true).apply(); Alarm alarm = new Alarm(getContext()); List<EGVRecord> egvRecords = generateStaleEgvRecordList(45); AlarmResults alarmResults = alarm.analyzeTime(egvRecords, GlucoseUnit.MGDL, new DateTime()); assertThat(alarmResults.severity, is(AlarmSeverity.URGENT)); } @Test public void testOutOfRangeSetsAlarmResultsMessage() { preferences.edit().putBoolean(getContext().getString(R.string.stale_alarm_enabled), true).apply(); Alarm alarm = new Alarm(getContext()); String expectedMessage = getContext().getString(R.string.alarm_out_of_range_message, 45); List<EGVRecord> egvRecords = generateStaleEgvRecordList(45); AlarmResults alarmResults = alarm.analyzeTime(egvRecords, GlucoseUnit.MGDL, new DateTime()); assertThat(alarmResults.message, containsString(expectedMessage)); } @Test public void testSimpleAlarmStrategyNoAlarm() { AlarmStrategy strategy = new SimpleAlarm(getContext()); List<EGVRecord> egvRecords = normalEgvList(); AlarmResults alarmResults = strategy.analyze(egvRecords, GlucoseUnit.MGDL); AlarmResults expectedResults = new AlarmResults(); expectedResults.severity = AlarmSeverity.NONE; expectedResults.message = getContext().getString(R.string.alarm_notification_standard_body, 100, TrendArrow.FLAT.symbol()); assertThat(alarmResults, is(expectedResults)); } @Test public void testSimpleAlarmStrategyLowWarnAlarm() { AlarmStrategy strategy = new SimpleAlarm(getContext()); List<EGVRecord> egvRecords = warnLowEgvList(); AlarmResults alarmResults = strategy.analyze(egvRecords, GlucoseUnit.MGDL); AlarmResults expectedResults = new AlarmResults(); expectedResults.severity = AlarmSeverity.WARNING; expectedResults.title = getContext().getString(R.string.alarm_notification_warning_title, getContext().getString(R.string.app_name)); expectedResults.message = getContext().getString(R.string.alarm_notification_standard_body, 68, TrendArrow.FLAT.symbol()); assertThat(alarmResults, is(expectedResults)); } @Test public void testSimpleAlarmStrategyLowUrgentAlarm() { AlarmStrategy strategy = new SimpleAlarm(getContext()); List<EGVRecord> egvRecords = urgentLowEgvList(); AlarmResults alarmResults = strategy.analyze(egvRecords, GlucoseUnit.MGDL); AlarmResults expectedResults = new AlarmResults(); expectedResults.severity = AlarmSeverity.URGENT; expectedResults.title = getContext().getString(R.string.alarm_notification_urgent_title, getContext().getString(R.string.app_name)); expectedResults.message = getContext().getString(R.string.alarm_notification_standard_body, 40, TrendArrow.FLAT.symbol()); assertThat(alarmResults, is(expectedResults)); } @Test public void testSimpleAlarmStrategyHighWarnAlarm() { AlarmStrategy strategy = new SimpleAlarm(getContext()); List<EGVRecord> egvRecords = warnHighEgvList(); AlarmResults alarmResults = strategy.analyze(egvRecords, GlucoseUnit.MGDL); AlarmResults expectedResults = new AlarmResults(); expectedResults.severity = AlarmSeverity.WARNING; expectedResults.title = getContext().getString(R.string.alarm_notification_warning_title, getContext().getString(R.string.app_name)); expectedResults.message = getContext().getString(R.string.alarm_notification_standard_body, 195, TrendArrow.FLAT.symbol()); assertThat(alarmResults, is(expectedResults)); } @Test public void testSimpleHighUrgentAlarm() { AlarmStrategy strategy = new SimpleAlarm(getContext()); List<EGVRecord> egvRecords = urgentHighEgvList(); AlarmResults alarmResults = strategy.analyze(egvRecords, GlucoseUnit.MGDL); AlarmResults expectedResults = new AlarmResults(); expectedResults.severity = AlarmSeverity.URGENT; expectedResults.title = getContext().getString(R.string.alarm_notification_urgent_title, getContext().getString(R.string.app_name)); expectedResults.message = getContext().getString(R.string.alarm_notification_standard_body, 265, TrendArrow.FLAT.symbol()); Log.d("Actual", "Title: " + alarmResults.title); Log.d("Expected", "Title: " + expectedResults.title); Log.d("Actual", "message: " + alarmResults.message); Log.d("Expected", "message: " + expectedResults.message); Log.d("Actual", "severity: " + alarmResults.severity.name()); Log.d("Expected", "severity: " + expectedResults.severity.name()); assertThat(alarmResults, is(expectedResults)); } @Test public void testAr2AlarmStrategyNoAlarm() { AlarmStrategy strategy = new Ar2(getContext()); List<EGVRecord> egvRecords = normalEgvList(); AlarmResults alarmResults = strategy.analyze(egvRecords, GlucoseUnit.MGDL); AlarmResults expectedResults = new AlarmResults(); expectedResults.severity = AlarmSeverity.NONE; GlucoseReading glucoseReading = new GlucoseReading(100, GlucoseUnit.MGDL); expectedResults.title = getContext().getString(R.string.alarm_notification_normal_title, getContext().getString(R.string.app_name)); expectedResults.message = getContext().getString(R.string.alarm_notification_standard_body, 100, TrendArrow.FLAT.symbol()); assertThat(alarmResults, is(expectedResults)); } @Test public void testAr2AlarmStrategyLowWarnAlarm() { AlarmStrategy strategy = new Ar2(getContext()); List<EGVRecord> egvRecords = warnLowEgvList(); AlarmResults alarmResults = strategy.analyze(egvRecords, GlucoseUnit.MGDL); AlarmResults expectedResults = new AlarmResults(); expectedResults.severity = AlarmSeverity.WARNING; expectedResults.title = getContext().getString(R.string.alarm_notification_warning_title, getContext().getString(R.string.app_name)); expectedResults.message = getContext().getString(R.string.alarm_notification_standard_body, 68, TrendArrow.FLAT.symbol()); assertThat(alarmResults, is(expectedResults)); } @Test public void testAr2AlarmStrategyLowUrgentAlarm() { AlarmStrategy strategy = new Ar2(getContext()); List<EGVRecord> egvRecords = urgentLowEgvList(); AlarmResults alarmResults = strategy.analyze(egvRecords, GlucoseUnit.MGDL); AlarmResults expectedResults = new AlarmResults(); expectedResults.severity = AlarmSeverity.URGENT; expectedResults.title = getContext().getString(R.string.alarm_notification_urgent_title, getContext().getString(R.string.app_name)); expectedResults.message = getContext().getString(R.string.alarm_notification_standard_body, 40, TrendArrow.FLAT.symbol()); assertThat(alarmResults, is(expectedResults)); } @Test public void testAr2AlarmStrategyHighWarnAlarm() { AlarmStrategy strategy = new Ar2(getContext()); List<EGVRecord> egvRecords = warnHighEgvList(); AlarmResults alarmResults = strategy.analyze(egvRecords, GlucoseUnit.MGDL); AlarmResults expectedResults = new AlarmResults(); expectedResults.severity = AlarmSeverity.WARNING; expectedResults.title = getContext().getString(R.string.alarm_notification_warning_title, getContext().getString(R.string.app_name)); expectedResults.message = getContext().getString(R.string.alarm_notification_standard_body, 195, TrendArrow.FLAT.symbol()); assertThat(alarmResults, is(expectedResults)); } @Test public void testAr2HighUrgentAlarm() { AlarmStrategy strategy = new Ar2(getContext()); List<EGVRecord> egvRecords = urgentHighEgvList(); AlarmResults alarmResults = strategy.analyze(egvRecords, GlucoseUnit.MGDL); AlarmResults expectedResults = new AlarmResults(); expectedResults.severity = AlarmSeverity.URGENT; expectedResults.title = getContext().getString(R.string.alarm_notification_urgent_title, getContext().getString(R.string.app_name)); expectedResults.message = getContext().getString(R.string.alarm_notification_standard_body, 265, TrendArrow.FLAT.symbol()); assertThat(alarmResults, is(expectedResults)); } @Test public void testSnooze() { Alarm alarm = new Alarm(getContext()); alarm.alarmSnooze(5000); assertThat(alarm.isSnoozed(), is(true)); } @Test public void testExpiredSnooze() { preferences.edit().putLong("snooze_" + AlarmSeverity.NONE.name(), new DateTime().minus(Minutes.minutes(5)).getMillis()).apply(); Alarm alarm = new Alarm(getContext()); assertThat(alarm.isSnoozed(), is(false)); } @Test public void testAlarmCreatesNotification() { List<EGVRecord> egvRecords = warnHighEgvList(); Alarm alarm = new Alarm(getContext()); alarm.analyze(egvRecords, GlucoseUnit.MGDL, Optional.of(100), new DateTime()); // FIXME - have to disable the ringer due to Robolectric NPE with mediaplayer shadowAudioManager.setRingerMode(AudioManager.RINGER_MODE_VIBRATE); Log.d("MediaState", "State: " + shadowMediaPlayer.getState().name()); alarm.alarm(); assertThat(shadowNotificationManager.size(), is(1)); } @Test public void testAlarmClearsNotificationWhenTransitioningFromNotNoneToNone() { List<EGVRecord> egvRecords = warnHighEgvList(); Alarm alarm = new Alarm(getContext()); alarm.analyze(egvRecords, GlucoseUnit.MGDL, Optional.of(100), new DateTime()); preferences.edit().putBoolean(getContext().getString(R.string.show_all_notifications_enabled), false).apply(); // FIXME - have to disable the ringer due to Robolectric NPE with mediaplayer shadowAudioManager.setRingerMode(AudioManager.RINGER_MODE_VIBRATE); Log.d("MediaState", "State: " + shadowMediaPlayer.getState().name()); alarm.alarm(); egvRecords = normalEgvList(); alarm.analyze(egvRecords, GlucoseUnit.MGDL, Optional.of(100), new DateTime()); alarm.alarm(); assertThat(shadowNotificationManager.size(), is(0)); } // TODO - test that phone vibrates // TODO - test that media plays // TODO - test that media snoozes properly // TODO - test that media stops playing when transitioning from ! None alarm to None public Context getContext() { return getShadowApplication().getApplicationContext(); } public ShadowApplication getShadowApplication() { return ShadowApplication.getInstance(); } }