diff --git a/app/src/main/java/info/nightscout/androidaps/data/ConstraintChecker.java b/app/src/main/java/info/nightscout/androidaps/data/ConstraintChecker.java index a4cccd772c..358110cc7c 100644 --- a/app/src/main/java/info/nightscout/androidaps/data/ConstraintChecker.java +++ b/app/src/main/java/info/nightscout/androidaps/data/ConstraintChecker.java @@ -3,9 +3,9 @@ package info.nightscout.androidaps.data; import java.util.ArrayList; import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.interfaces.Constraint; import info.nightscout.androidaps.interfaces.ConstraintsInterface; import info.nightscout.androidaps.interfaces.PluginBase; -import info.nightscout.androidaps.interfaces.Constraint; import info.nightscout.utils.SP; /** diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/ObjectivesPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/ObjectivesPlugin.java index d9e60bc61c..1a93c15c1b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/ObjectivesPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/ObjectivesPlugin.java @@ -15,9 +15,9 @@ import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.db.DatabaseHelper; import info.nightscout.androidaps.interfaces.APSInterface; +import info.nightscout.androidaps.interfaces.Constraint; import info.nightscout.androidaps.interfaces.ConstraintsInterface; import info.nightscout.androidaps.interfaces.PluginBase; -import info.nightscout.androidaps.interfaces.Constraint; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.ConstraintsSafety.SafetyPlugin; import info.nightscout.androidaps.plugins.Loop.LoopPlugin; @@ -67,7 +67,7 @@ public class ObjectivesPlugin implements PluginBase, ConstraintsInterface { @Override public String getNameShort() { - String name = MainApp.sResources.getString(R.string.objectives_shortname); + String name = MainApp.gs(R.string.objectives_shortname); if (!name.trim().isEmpty()) { //only if translation exists return name; @@ -115,7 +115,7 @@ public class ObjectivesPlugin implements PluginBase, ConstraintsInterface { return -1; } - class Objective { + public class Objective { Integer num; String objective; String gate; @@ -131,6 +131,10 @@ public class ObjectivesPlugin implements PluginBase, ConstraintsInterface { this.durationInDays = durationInDays; this.accomplished = accomplished; } + + public void setStarted(Date started) { + this.started = started; + } } // Objective 0 @@ -169,27 +173,27 @@ public class ObjectivesPlugin implements PluginBase, ConstraintsInterface { apsEnabled = true; return new RequirementResult(hasBGData && bgIsAvailableInNS && pumpStatusIsAvailableInNS && NSClientInternalPlugin.getPlugin().hasWritePermission() && LoopPlugin.getPlugin().isEnabled(PluginBase.LOOP) && apsEnabled && vpUploadNeeded, - MainApp.sResources.getString(R.string.objectives_bgavailableinns) + ": " + yesOrNo(bgIsAvailableInNS) - + "\n" + MainApp.sResources.getString(R.string.nsclienthaswritepermission) + ": " + yesOrNo(NSClientInternalPlugin.getPlugin().hasWritePermission()) - + (isVirtualPump ? "\n" + MainApp.sResources.getString(R.string.virtualpump_uploadstatus_title) + ": " + yesOrNo(vpUploadEnabled) : "") - + "\n" + MainApp.sResources.getString(R.string.objectives_pumpstatusavailableinns) + ": " + yesOrNo(pumpStatusIsAvailableInNS) - + "\n" + MainApp.sResources.getString(R.string.hasbgdata) + ": " + yesOrNo(hasBGData) - + "\n" + MainApp.sResources.getString(R.string.loopenabled) + ": " + yesOrNo(LoopPlugin.getPlugin().isEnabled(PluginBase.LOOP)) - + "\n" + MainApp.sResources.getString(R.string.apsselected) + ": " + yesOrNo(apsEnabled) + MainApp.gs(R.string.objectives_bgavailableinns) + ": " + yesOrNo(bgIsAvailableInNS) + + "\n" + MainApp.gs(R.string.nsclienthaswritepermission) + ": " + yesOrNo(NSClientInternalPlugin.getPlugin().hasWritePermission()) + + (isVirtualPump ? "\n" + MainApp.gs(R.string.virtualpump_uploadstatus_title) + ": " + yesOrNo(vpUploadEnabled) : "") + + "\n" + MainApp.gs(R.string.objectives_pumpstatusavailableinns) + ": " + yesOrNo(pumpStatusIsAvailableInNS) + + "\n" + MainApp.gs(R.string.hasbgdata) + ": " + yesOrNo(hasBGData) + + "\n" + MainApp.gs(R.string.loopenabled) + ": " + yesOrNo(LoopPlugin.getPlugin().isEnabled(PluginBase.LOOP)) + + "\n" + MainApp.gs(R.string.apsselected) + ": " + yesOrNo(apsEnabled) ); case 1: return new RequirementResult(manualEnacts >= manualEnactsNeeded, - MainApp.sResources.getString(R.string.objectives_manualenacts) + ": " + manualEnacts + "/" + manualEnactsNeeded); + MainApp.gs(R.string.objectives_manualenacts) + ": " + manualEnacts + "/" + manualEnactsNeeded); case 2: return new RequirementResult(true, ""); case 3: Constraint closedLoopEnabled = new Constraint<>(true); SafetyPlugin.getPlugin().isClosedLoopAllowed(closedLoopEnabled); - return new RequirementResult(closedLoopEnabled.get(), MainApp.sResources.getString(R.string.closedmodeenabled) + ": " + yesOrNo(closedLoopEnabled.get())); + return new RequirementResult(closedLoopEnabled.get(), MainApp.gs(R.string.closedmodeenabled) + ": " + yesOrNo(closedLoopEnabled.get())); case 4: double maxIOB = MainApp.getConstraintChecker().applyMaxIOBConstraints(1000d); boolean maxIobSet = maxIOB > 0; - return new RequirementResult(maxIobSet, MainApp.sResources.getString(R.string.maxiobset) + ": " + yesOrNo(maxIobSet)); + return new RequirementResult(maxIobSet, MainApp.gs(R.string.maxiobset) + ": " + yesOrNo(maxIobSet)); default: return new RequirementResult(true, ""); } @@ -203,49 +207,49 @@ public class ObjectivesPlugin implements PluginBase, ConstraintsInterface { objectives = new ArrayList<>(); objectives.add(new Objective(0, - MainApp.sResources.getString(R.string.objectives_0_objective), - MainApp.sResources.getString(R.string.objectives_0_gate), + MainApp.gs(R.string.objectives_0_objective), + MainApp.gs(R.string.objectives_0_gate), new Date(0), 0, // 0 day new Date(0))); objectives.add(new Objective(1, - MainApp.sResources.getString(R.string.objectives_1_objective), - MainApp.sResources.getString(R.string.objectives_1_gate), + MainApp.gs(R.string.objectives_1_objective), + MainApp.gs(R.string.objectives_1_gate), new Date(0), 7, // 7 days new Date(0))); objectives.add(new Objective(2, - MainApp.sResources.getString(R.string.objectives_2_objective), - MainApp.sResources.getString(R.string.objectives_2_gate), + MainApp.gs(R.string.objectives_2_objective), + MainApp.gs(R.string.objectives_2_gate), new Date(0), 0, // 0 days new Date(0))); objectives.add(new Objective(3, - MainApp.sResources.getString(R.string.objectives_3_objective), - MainApp.sResources.getString(R.string.objectives_3_gate), + MainApp.gs(R.string.objectives_3_objective), + MainApp.gs(R.string.objectives_3_gate), new Date(0), 5, // 5 days new Date(0))); objectives.add(new Objective(4, - MainApp.sResources.getString(R.string.objectives_4_objective), - MainApp.sResources.getString(R.string.objectives_4_gate), + MainApp.gs(R.string.objectives_4_objective), + MainApp.gs(R.string.objectives_4_gate), new Date(0), 1, new Date(0))); objectives.add(new Objective(5, - MainApp.sResources.getString(R.string.objectives_5_objective), - MainApp.sResources.getString(R.string.objectives_5_gate), + MainApp.gs(R.string.objectives_5_objective), + MainApp.gs(R.string.objectives_5_gate), new Date(0), 7, new Date(0))); objectives.add(new Objective(6, - MainApp.sResources.getString(R.string.objectives_6_objective), + MainApp.gs(R.string.objectives_6_objective), "", new Date(0), 28, new Date(0))); objectives.add(new Objective(7, - MainApp.sResources.getString(R.string.objectives_7_objective), + MainApp.gs(R.string.objectives_7_objective), "", new Date(0), 28, diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ComboPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ComboPlugin.java index c68815e2fa..a10752df47 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ComboPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ComboPlugin.java @@ -424,7 +424,7 @@ public class ComboPlugin implements PluginBase, PumpInterface, ConstraintsInterf return; } pump.basalProfile = readBasalResult.basalProfile; - validBasalRateProfileSelectedOnPump = true; + setValidBasalRateProfileSelectedOnPump(true); pump.initialized = true; MainApp.bus().post(new EventInitializationChanged()); @@ -925,7 +925,7 @@ public class ComboPlugin implements PluginBase, PumpInterface, ConstraintsInterf if (commandResult.success) { pump.lastSuccessfulCmdTime = System.currentTimeMillis(); if (validBasalRateProfileSelectedOnPump && commandResult.state.unsafeUsageDetected == PumpState.UNSUPPORTED_BASAL_RATE_PROFILE) { - validBasalRateProfileSelectedOnPump = false; + setValidBasalRateProfileSelectedOnPump(false); Notification n = new Notification(Notification.COMBO_PUMP_ALARM, MainApp.gs(R.string.combo_force_disabled_notification), Notification.URGENT); @@ -945,6 +945,10 @@ public class ComboPlugin implements PluginBase, PumpInterface, ConstraintsInterf return commandResult; } + public void setValidBasalRateProfileSelectedOnPump(boolean value) { + validBasalRateProfileSelectedOnPump = value; + } + /** * Returns the command result of running ReadPumpState if it wasn't successful, indicating * an error condition. Returns null otherwise. diff --git a/app/src/test/java/info/nightscout/androidaps/interfaces/ConstraintsCheckerTest.java b/app/src/test/java/info/nightscout/androidaps/interfaces/ConstraintsCheckerTest.java new file mode 100644 index 0000000000..7ec2eaeabd --- /dev/null +++ b/app/src/test/java/info/nightscout/androidaps/interfaces/ConstraintsCheckerTest.java @@ -0,0 +1,151 @@ +package info.nightscout.androidaps.interfaces; + +import com.squareup.otto.Bus; + +import junit.framework.Assert; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import java.util.ArrayList; +import java.util.Date; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.data.ConstraintChecker; +import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; +import info.nightscout.androidaps.plugins.ConstraintsObjectives.ObjectivesPlugin; +import info.nightscout.androidaps.plugins.ConstraintsSafety.SafetyPlugin; +import info.nightscout.androidaps.plugins.PumpCombo.ComboPlugin; +import info.nightscout.androidaps.plugins.PumpVirtual.VirtualPumpPlugin; +import info.nightscout.utils.FabricPrivacy; +import info.nightscout.utils.SP; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * Created by mike on 18.03.2018. + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({MainApp.class, ConfigBuilderPlugin.class, FabricPrivacy.class, SP.class}) +public class ConstraintsCheckerTest { + + PumpInterface pump = new VirtualPumpPlugin(); + ConstraintChecker constraintChecker; + + ConfigBuilderPlugin configBuilderPlugin = mock(ConfigBuilderPlugin.class); + MainApp mainApp = mock(MainApp.class); + MockedBus bus = new MockedBus(); + //PumpDescription pumpDescription = new PumpDescription(); + + SafetyPlugin safetyPlugin; + ObjectivesPlugin objectivesPlugin; + ComboPlugin comboPlugin; + + boolean notificationSent = false; + + // isLoopInvokationAllowed tests + @Test + public void pumpDescriptionShouldLimitLoopInvokation() throws Exception { + pump.getPumpDescription().isTempBasalCapable = false; + + Constraint c = constraintChecker.isLoopInvokationAllowed(); + Assert.assertEquals(true, c.getReasons().contains("Pump is not temp basal capable")); + Assert.assertEquals(Boolean.FALSE, c.get()); + } + + @Test + public void notStartedObjectivesShouldLimitLoopInvokation() throws Exception { + objectivesPlugin.objectives.get(0).setStarted(new Date(0)); + + Constraint c = constraintChecker.isLoopInvokationAllowed(); + Assert.assertEquals(true, c.getReasons().contains("Objective 1 not started")); + Assert.assertEquals(Boolean.FALSE, c.get()); + } + + @Test + public void invalidBasalRateOnComboPumpShouldLimitLoopInvokation() throws Exception { + comboPlugin.setFragmentEnabled(PluginBase.PUMP, true); + comboPlugin.setValidBasalRateProfileSelectedOnPump(false); + + Constraint c = constraintChecker.isLoopInvokationAllowed(); + Assert.assertEquals(true, c.getReasons().contains("No valid basal rate read from pump")); + Assert.assertEquals(Boolean.FALSE, c.get()); + } + + // isClosedLoopAllowed tests + @Test + public void disabledEngineeringModeShouldLimitClosedLoop() throws Exception { + when(SP.getString("aps_mode", "open")).thenReturn("closed"); + when(MainApp.isEngineeringModeOrRelease()).thenReturn(false); + + Constraint c = constraintChecker.isClosedLoopAllowed(); + Assert.assertEquals(true, c.getReasons().contains("Running dev version. Closed loop is disabled.")); + Assert.assertEquals(Boolean.FALSE, c.get()); + } + + @Test + public void setOpenLoopInPreferencesShouldLimitClosedLoop() throws Exception { + when(SP.getString("aps_mode", "open")).thenReturn("open"); + + Constraint c = constraintChecker.isClosedLoopAllowed(); + Assert.assertEquals(true, c.getReasons().contains("Closed loop mode disabled in preferences")); + Assert.assertEquals(Boolean.FALSE, c.get()); + } + + @Test + public void notStartedObjective4ShouldLimitClosedLoop() throws Exception { + when(SP.getString("aps_mode", "open")).thenReturn("closed"); + objectivesPlugin.objectives.get(3).setStarted(new Date(0)); + + Constraint c = constraintChecker.isClosedLoopAllowed(); + Assert.assertEquals(true, c.getReasons().contains("Objective 4 not started")); + Assert.assertEquals(Boolean.FALSE, c.get()); + } + + @Before + public void prepareMock() throws Exception { + PowerMockito.mockStatic(ConfigBuilderPlugin.class); + + PowerMockito.mockStatic(MainApp.class); + when(MainApp.instance()).thenReturn(mainApp); + when(MainApp.getConfigBuilder()).thenReturn(configBuilderPlugin); + when(MainApp.getConfigBuilder().getActivePump()).thenReturn(pump); + + constraintChecker = new ConstraintChecker(mainApp); + + PowerMockito.mockStatic(FabricPrivacy.class); + + when(MainApp.bus()).thenReturn(bus); + + when(MainApp.gs(R.string.pumpisnottempbasalcapable)).thenReturn("Pump is not temp basal capable"); + when(MainApp.gs(R.string.closed_loop_disabled_on_dev_branch)).thenReturn("Running dev version. Closed loop is disabled."); + when(MainApp.gs(R.string.closedmodedisabledinpreferences)).thenReturn("Closed loop mode disabled in preferences"); + when(MainApp.gs(R.string.objectivenotstarted)).thenReturn("Objective %d not started"); + when(MainApp.gs(R.string.novalidbasalrate)).thenReturn("No valid basal rate read from pump"); + + safetyPlugin = SafetyPlugin.getPlugin(); + objectivesPlugin = ObjectivesPlugin.getPlugin(); + comboPlugin = ComboPlugin.getPlugin(); + ArrayList constraintsPluginsList = new ArrayList<>(); + constraintsPluginsList.add(safetyPlugin); + constraintsPluginsList.add(objectivesPlugin); + constraintsPluginsList.add(comboPlugin); + when(mainApp.getSpecificPluginsListByInterface(ConstraintsInterface.class)).thenReturn(constraintsPluginsList); + + PowerMockito.mockStatic(SP.class); + } + + class MockedBus extends Bus { + @Override + public void post(Object event) { + notificationSent = true; + } + } + +}