From f7621c4cc57e593a68f8d3ac925f5c08486734fb Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Sun, 18 Mar 2018 15:02:21 +0100 Subject: [PATCH] Profile tests --- app/build.gradle | 1 + .../nightscout/androidaps/data/Profile.java | 158 ++++++++++------- .../IobCobCalculator/IobCobThread.java | 5 - .../DetermineBasalAdapterAMAJS.java | 2 +- .../plugins/OpenAPSAMA/OpenAPSAMAPlugin.java | 4 +- .../OpenAPSMA/DetermineBasalAdapterMAJS.java | 2 +- .../plugins/OpenAPSMA/OpenAPSMAPlugin.java | 4 +- .../DetermineBasalAdapterSMBJS.java | 2 +- .../plugins/OpenAPSSMB/OpenAPSSMBPlugin.java | 4 +- .../PumpDanaR/AbstractDanaRPlugin.java | 2 +- .../plugins/PumpDanaR/DanaRPump.java | 2 +- .../plugins/PumpDanaRS/DanaRSPlugin.java | 2 +- .../PumpVirtual/VirtualPumpPlugin.java | 8 +- .../Treatments/fragments/ProfileGraph.java | 4 +- .../plugins/Wear/ActionStringHandler.java | 2 +- .../receivers/KeepAliveReceiver.java | 2 +- .../nightscout/utils/LocalAlertUtils.java | 2 +- app/src/main/res/values/strings.xml | 1 + .../androidaps/data/ProfileTest.java | 161 ++++++++++++++++++ .../androidaps/queue/CommandQueueTest.java | 2 +- 20 files changed, 277 insertions(+), 93 deletions(-) create mode 100644 app/src/test/java/info/nightscout/androidaps/data/ProfileTest.java diff --git a/app/build.gradle b/app/build.gradle index 215ec44c8c..00c78fe077 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -238,6 +238,7 @@ dependencies { testCompile "org.powermock:powermock-module-junit4:${powermockVersion}" testCompile "joda-time:joda-time:2.9.4.2" testCompile "com.google.truth:truth:0.39" + testCompile "org.skyscreamer:jsonassert:1.5.0" androidTestCompile "org.mockito:mockito-core:2.7.22" androidTestCompile "com.google.dexmaker:dexmaker:${dexmakerVersion}" diff --git a/app/src/main/java/info/nightscout/androidaps/data/Profile.java b/app/src/main/java/info/nightscout/androidaps/data/Profile.java index be06258d76..e966c4d11d 100644 --- a/app/src/main/java/info/nightscout/androidaps/data/Profile.java +++ b/app/src/main/java/info/nightscout/androidaps/data/Profile.java @@ -2,8 +2,6 @@ package info.nightscout.androidaps.data; import android.support.v4.util.LongSparseArray; - - import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -19,41 +17,43 @@ import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.interfaces.PumpDescription; import info.nightscout.androidaps.interfaces.PumpInterface; -import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; -import info.nightscout.androidaps.plugins.Overview.events.EventDismissNotification; import info.nightscout.androidaps.plugins.Overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.Overview.notifications.Notification; import info.nightscout.utils.DateUtil; import info.nightscout.utils.DecimalFormatter; import info.nightscout.utils.FabricPrivacy; -import info.nightscout.utils.ToastUtils; public class Profile { private static Logger log = LoggerFactory.getLogger(Profile.class); private JSONObject json; - private String units = null; - private double dia = Constants.defaultDIA; - private TimeZone timeZone = TimeZone.getDefault(); + private String units; + private double dia; + private TimeZone timeZone; private JSONArray isf; - private LongSparseArray isf_v = null; // oldest at index 0 + private LongSparseArray isf_v; // oldest at index 0 private JSONArray ic; - private LongSparseArray ic_v = null; // oldest at index 0 + private LongSparseArray ic_v; // oldest at index 0 private JSONArray basal; - private LongSparseArray basal_v = null; // oldest at index 0 + private LongSparseArray basal_v; // oldest at index 0 private JSONArray targetLow; - private LongSparseArray targetLow_v = null; // oldest at index 0 + private LongSparseArray targetLow_v; // oldest at index 0 private JSONArray targetHigh; - private LongSparseArray targetHigh_v = null; // oldest at index 0 + private LongSparseArray targetHigh_v; // oldest at index 0 - private int percentage = 100; - private int timeshift = 0; + private int percentage; + private int timeshift; - private boolean isValid = true; - private boolean isValidated = false; + protected boolean isValid; + protected boolean isValidated; + // Default constructor for tests + protected Profile() { + } + + // Constructor from profileStore JSON public Profile(JSONObject json, String units) { - this(json, 100, 0); + init(json, 100, 0); if (this.units == null) { if (units != null) this.units = units; @@ -65,6 +65,22 @@ public class Profile { } public Profile(JSONObject json, int percentage, int timeshift) { + init(json, percentage, timeshift); + } + + protected void init(JSONObject json, int percentage, int timeshift) { + units = null; + dia = Constants.defaultDIA; + timeZone = TimeZone.getDefault(); + isf_v = null; + ic_v = null; + basal_v = null; + targetLow_v = null; + targetHigh_v = null; + + isValid = true; + isValidated = false; + this.percentage = percentage; this.timeshift = timeshift; this.json = json; @@ -92,7 +108,7 @@ public class Profile { public String log() { String ret = "\n"; for (Integer hour = 0; hour < 24; hour++) { - double value = getBasal((Integer) (hour * 60 * 60)); + double value = getBasalTimeFromMidnight((Integer) (hour * 60 * 60)); ret += "NS basal value for " + hour + ":00 is " + value + "\n"; } ret += "NS units: " + getUnits(); @@ -114,6 +130,10 @@ public class Profile { } // mmol or mg/dl + public void setUnits(String units) { + this.units = units; + } + public String getUnits() { return units; } @@ -199,7 +219,7 @@ public class Profile { for (int i = 0; i < basal_v.size(); i++) { if (basal_v.valueAt(i) < description.basalMinimumRate) { basal_v.setValueAt(i, description.basalMinimumRate); - MainApp.bus().post(new EventNewNotification(new Notification(Notification.MINIMAL_BASAL_VALUE_REPLACED, String.format(MainApp.gs(R.string.minimalbasalvaluereplaced), from), Notification.NORMAL))); + sendBelowMinimumNotification(from); } } } else { @@ -213,6 +233,10 @@ public class Profile { return isValid; } + protected void sendBelowMinimumNotification(String from) { + MainApp.bus().post(new EventNewNotification(new Notification(Notification.MINIMAL_BASAL_VALUE_REPLACED, String.format(MainApp.gs(R.string.minimalbasalvaluereplaced), from), Notification.NORMAL))); + } + private void validate(LongSparseArray array) { if (array.size() == 0) { isValid = false; @@ -226,6 +250,7 @@ public class Profile { } } + /* private Double getValueToTime(JSONArray array, Integer timeAsSeconds) { Double lastValue = null; @@ -245,6 +270,7 @@ public class Profile { } return lastValue; } + */ Integer getShitfTimeSecs(Integer originalTime) { Integer shiftedTime = originalTime + timeshift * 60 * 60; @@ -286,7 +312,7 @@ public class Profile { return multiplier; } - private Double getValueToTime(LongSparseArray array, Integer timeAsSeconds) { + private double getValueToTime(LongSparseArray array, Integer timeAsSeconds) { Double lastValue = null; for (Integer index = 0; index < array.size(); index++) { @@ -301,7 +327,7 @@ public class Profile { return lastValue; } - private String format_HH_MM(Integer timeAsSeconds) { + protected String format_HH_MM(Integer timeAsSeconds) { String time; int hour = timeAsSeconds / 60 / 60; int minutes = (timeAsSeconds - hour * 60 * 60) / 60; @@ -328,15 +354,15 @@ public class Profile { return retValue; } - public Double getIsf() { - return getIsf(secondsFromMidnight(System.currentTimeMillis())); + public double getIsf() { + return getIsfTimeFromMidnight(secondsFromMidnight(System.currentTimeMillis())); } - public Double getIsf(long time) { - return getIsf(secondsFromMidnight(time)); + public double getIsf(long time) { + return getIsfTimeFromMidnight(secondsFromMidnight(time)); } - public Double getIsf(Integer timeAsSeconds) { + double getIsfTimeFromMidnight(int timeAsSeconds) { if (isf_v == null) isf_v = convertToSparseArray(isf); return getValueToTime(isf_v, timeAsSeconds); @@ -348,15 +374,15 @@ public class Profile { return getValuesList(isf_v, null, new DecimalFormat("0.0"), getUnits() + "/U"); } - public Double getIc() { - return getIc(secondsFromMidnight(System.currentTimeMillis())); + public double getIc() { + return getIcTimeFromMidnight(secondsFromMidnight(System.currentTimeMillis())); } - public Double getIc(long time) { - return getIc(secondsFromMidnight(time)); + public double getIc(long time) { + return getIcTimeFromMidnight(secondsFromMidnight(time)); } - public Double getIc(Integer timeAsSeconds) { + public double getIcTimeFromMidnight(int timeAsSeconds) { if (ic_v == null) ic_v = convertToSparseArray(ic); return getValueToTime(ic_v, timeAsSeconds); @@ -365,18 +391,18 @@ public class Profile { public String getIcList() { if (ic_v == null) ic_v = convertToSparseArray(ic); - return getValuesList(ic_v, null, new DecimalFormat("0.0"), " g/U"); + return getValuesList(ic_v, null, new DecimalFormat("0.0"), "g/U"); } - public Double getBasal() { - return getBasal(secondsFromMidnight(System.currentTimeMillis())); + public double getBasal() { + return getBasalTimeFromMidnight(secondsFromMidnight(System.currentTimeMillis())); } - public Double getBasal(long time) { - return getBasal(secondsFromMidnight(time)); + public double getBasal(long time) { + return getBasalTimeFromMidnight(secondsFromMidnight(time)); } - public synchronized Double getBasal(Integer timeAsSeconds) { + public synchronized double getBasalTimeFromMidnight(int timeAsSeconds) { if (basal_v == null) { basal_v = convertToSparseArray(basal); } @@ -386,17 +412,17 @@ public class Profile { public String getBasalList() { if (basal_v == null) basal_v = convertToSparseArray(basal); - return getValuesList(basal_v, null, new DecimalFormat("0.00"), "U"); + return getValuesList(basal_v, null, new DecimalFormat("0.00"), "U/h"); } public class BasalValue { - public BasalValue(Integer timeAsSeconds, Double value) { + public BasalValue(int timeAsSeconds, double value) { this.timeAsSeconds = timeAsSeconds; this.value = value; } - public Integer timeAsSeconds; - public Double value; + public int timeAsSeconds; + public double value; } public synchronized BasalValue[] getBasalValues() { @@ -406,7 +432,7 @@ public class Profile { for (Integer index = 0; index < basal_v.size(); index++) { Integer tas = (int) basal_v.keyAt(index); - Double value = basal_v.valueAt(index); + double value = basal_v.valueAt(index); ret[index] = new BasalValue(tas, value); } return ret; @@ -416,33 +442,33 @@ public class Profile { return getTarget(secondsFromMidnight(System.currentTimeMillis())); } - private double getTarget(Integer time) { - return (getTargetLow(time) + getTargetHigh(time))/2; + protected double getTarget(int timeAsSeconds) { + return (getTargetLowTimeFromMidnight(timeAsSeconds) + getTargetHighTimeFromMidnight(timeAsSeconds))/2; } - public Double getTargetLow() { - return getTargetLow(secondsFromMidnight(System.currentTimeMillis())); + public double getTargetLow() { + return getTargetLowTimeFromMidnight(secondsFromMidnight(System.currentTimeMillis())); } - public Double getTargetLow(long time) { - return getTargetLow(secondsFromMidnight(time)); + public double getTargetLow(long time) { + return getTargetLowTimeFromMidnight(secondsFromMidnight(time)); } - public Double getTargetLow(Integer timeAsSeconds) { + public double getTargetLowTimeFromMidnight(int timeAsSeconds) { if (targetLow_v == null) targetLow_v = convertToSparseArray(targetLow); return getValueToTime(targetLow_v, timeAsSeconds); } - public Double getTargetHigh() { - return getTargetHigh(secondsFromMidnight(System.currentTimeMillis())); + public double getTargetHigh() { + return getTargetHighTimeFromMidnight(secondsFromMidnight(System.currentTimeMillis())); } - public Double getTargetHigh(long time) { - return getTargetHigh(secondsFromMidnight(time)); + public double getTargetHigh(long time) { + return getTargetHighTimeFromMidnight(secondsFromMidnight(time)); } - public Double getTargetHigh(Integer timeAsSeconds) { + public double getTargetHighTimeFromMidnight(int timeAsSeconds) { if (targetHigh_v == null) targetHigh_v = convertToSparseArray(targetHigh); return getValueToTime(targetHigh_v, timeAsSeconds); @@ -457,15 +483,15 @@ public class Profile { } public double getMaxDailyBasal() { - Double max = 0d; - for (Integer hour = 0; hour < 24; hour++) { - double value = getBasal((Integer) (hour * 60 * 60)); + double max = 0d; + for (int hour = 0; hour < 24; hour++) { + double value = getBasalTimeFromMidnight((Integer) (hour * 60 * 60)); if (value > max) max = value; } return max; } - public static Integer secondsFromMidnight() { + public static int secondsFromMidnight() { Calendar c = Calendar.getInstance(); long now = c.getTimeInMillis(); c.set(Calendar.HOUR_OF_DAY, 0); @@ -476,7 +502,7 @@ public class Profile { return (int) (passed / 1000); } - public static Integer secondsFromMidnight(long date) { + public static int secondsFromMidnight(long date) { Calendar c = Calendar.getInstance(); c.setTimeInMillis(date); c.set(Calendar.HOUR_OF_DAY, 0); @@ -487,22 +513,22 @@ public class Profile { return (int) (passed / 1000); } - public static Double toMgdl(Double value, String units) { + public static double toMgdl(double value, String units) { if (units.equals(Constants.MGDL)) return value; else return value * Constants.MMOLL_TO_MGDL; } - public static Double toMmol(Double value, String units) { + public static double toMmol(double value, String units) { if (units.equals(Constants.MGDL)) return value * Constants.MGDL_TO_MMOLL; else return value; } - public static Double fromMgdlToUnits(Double value, String units) { + public static double fromMgdlToUnits(double value, String units) { if (units.equals(Constants.MGDL)) return value; else return value * Constants.MGDL_TO_MMOLL; } - public static Double toUnits(Double valueInMgdl, Double valueInMmol, String units) { + public static double toUnits(Double valueInMgdl, Double valueInMmol, String units) { if (units.equals(Constants.MGDL)) return valueInMgdl; else return valueInMmol; } @@ -528,7 +554,7 @@ public class Profile { public double percentageBasalSum() { double result = 0d; for (int i = 0; i < 24; i++) { - result += getBasal((Integer) (i * 60 * 60)); + result += getBasalTimeFromMidnight(i * 60 * 60); } return result; } @@ -537,7 +563,7 @@ public class Profile { public double baseBasalSum() { double result = 0d; for (int i = 0; i < 24; i++) { - result += getBasal((Integer) (i * 60 * 60)) / getMultiplier(basal_v); + result += getBasalTimeFromMidnight(i * 60 * 60) / getMultiplier(basal_v); } return result; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobThread.java b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobThread.java index c492aae400..1a4c499c59 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobThread.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobThread.java @@ -110,11 +110,6 @@ public class IobCobThread extends Thread { return; // profile not set yet } - if (profile.getIsf(bgTime) == null) { - log.debug("Aborting calculation thread (no ISF): " + from); - return; // profile not set yet - } - if (Config.logAutosensData) log.debug("Processing calculation thread: " + from + " (" + i + "/" + bucketed_data.size() + ")"); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/DetermineBasalAdapterAMAJS.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/DetermineBasalAdapterAMAJS.java index 5372f66b03..5fc7809f4e 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/DetermineBasalAdapterAMAJS.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/DetermineBasalAdapterAMAJS.java @@ -204,7 +204,7 @@ public class DetermineBasalAdapterAMAJS { mProfile.put("max_bg", maxBg); mProfile.put("target_bg", targetBg); mProfile.put("carb_ratio", profile.getIc()); - mProfile.put("sens", Profile.toMgdl(profile.getIsf().doubleValue(), units)); + mProfile.put("sens", Profile.toMgdl(profile.getIsf(), units)); mProfile.put("max_daily_safety_multiplier", SP.getInt("openapsama_max_daily_safety_multiplier", 3)); mProfile.put("current_basal_safety_multiplier", SP.getDouble("openapsama_current_basal_safety_multiplier", 4d)); mProfile.put("skip_neutral_temps", true); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/OpenAPSAMAPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/OpenAPSAMAPlugin.java index 6ac207ab80..dfab195125 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/OpenAPSAMAPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/OpenAPSAMAPlugin.java @@ -212,9 +212,9 @@ public class OpenAPSAMAPlugin implements PluginBase, APSInterface { if (!HardLimits.checkOnlyHardLimits(profile.getDia(), "dia", HardLimits.MINDIA, HardLimits.MAXDIA)) return; - if (!HardLimits.checkOnlyHardLimits(profile.getIc(profile.secondsFromMidnight()), "carbratio", HardLimits.MINIC, HardLimits.MAXIC)) + if (!HardLimits.checkOnlyHardLimits(profile.getIcTimeFromMidnight(profile.secondsFromMidnight()), "carbratio", HardLimits.MINIC, HardLimits.MAXIC)) return; - if (!HardLimits.checkOnlyHardLimits(Profile.toMgdl(profile.getIsf().doubleValue(), units), "sens", HardLimits.MINISF, HardLimits.MAXISF)) + if (!HardLimits.checkOnlyHardLimits(Profile.toMgdl(profile.getIsf(), units), "sens", HardLimits.MINISF, HardLimits.MAXISF)) return; if (!HardLimits.checkOnlyHardLimits(profile.getMaxDailyBasal(), "max_daily_basal", 0.05, HardLimits.maxBasal())) return; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/DetermineBasalAdapterMAJS.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/DetermineBasalAdapterMAJS.java index e7bd0c86fd..1ffa682fc5 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/DetermineBasalAdapterMAJS.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/DetermineBasalAdapterMAJS.java @@ -172,7 +172,7 @@ public class DetermineBasalAdapterMAJS { mProfile.put("max_bg", maxBg); mProfile.put("target_bg", targetBg); mProfile.put("carb_ratio", profile.getIc()); - mProfile.put("sens", Profile.toMgdl(profile.getIsf().doubleValue(), units)); + mProfile.put("sens", Profile.toMgdl(profile.getIsf(), units)); mProfile.put("current_basal", basalRate); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/OpenAPSMAPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/OpenAPSMAPlugin.java index 7f8b56f021..76df63e752 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/OpenAPSMAPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/OpenAPSMAPlugin.java @@ -212,9 +212,9 @@ public class OpenAPSMAPlugin implements PluginBase, APSInterface { if (!checkOnlyHardLimits(profile.getDia(), "dia", HardLimits.MINDIA, HardLimits.MAXDIA)) return; - if (!checkOnlyHardLimits(profile.getIc(profile.secondsFromMidnight()), "carbratio", HardLimits.MINIC, HardLimits.MAXIC)) + if (!checkOnlyHardLimits(profile.getIcTimeFromMidnight(profile.secondsFromMidnight()), "carbratio", HardLimits.MINIC, HardLimits.MAXIC)) return; - if (!checkOnlyHardLimits(Profile.toMgdl(profile.getIsf().doubleValue(), units), "sens", HardLimits.MINISF, HardLimits.MAXISF)) + if (!checkOnlyHardLimits(Profile.toMgdl(profile.getIsf(), units), "sens", HardLimits.MINISF, HardLimits.MAXISF)) return; if (!checkOnlyHardLimits(profile.getMaxDailyBasal(), "max_daily_basal", 0.05, HardLimits.maxBasal())) return; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java index 64f8a155fd..9df54b34d3 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java @@ -228,7 +228,7 @@ public class DetermineBasalAdapterSMBJS { mProfile.put("max_bg", maxBg); mProfile.put("target_bg", targetBg); mProfile.put("carb_ratio", profile.getIc()); - mProfile.put("sens", Profile.toMgdl(profile.getIsf().doubleValue(), units)); + mProfile.put("sens", Profile.toMgdl(profile.getIsf(), units)); mProfile.put("max_daily_safety_multiplier", SP.getInt("openapsama_max_daily_safety_multiplier", 3)); mProfile.put("current_basal_safety_multiplier", SP.getDouble("openapsama_current_basal_safety_multiplier", 4d)); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/OpenAPSSMBPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/OpenAPSSMBPlugin.java index 91aa4ef515..f10e6cb0be 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/OpenAPSSMBPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/OpenAPSSMBPlugin.java @@ -216,9 +216,9 @@ public class OpenAPSSMBPlugin implements PluginBase, APSInterface { maxBasal = verifyHardLimits(maxBasal, "max_basal", 0.1, HardLimits.maxBasal()); if (!checkOnlyHardLimits(profile.getDia(), "dia", HardLimits.MINDIA, HardLimits.MAXDIA)) return; - if (!checkOnlyHardLimits(profile.getIc(profile.secondsFromMidnight()), "carbratio", HardLimits.MINIC, HardLimits.MAXIC)) + if (!checkOnlyHardLimits(profile.getIcTimeFromMidnight(profile.secondsFromMidnight()), "carbratio", HardLimits.MINIC, HardLimits.MAXIC)) return; - if (!checkOnlyHardLimits(Profile.toMgdl(profile.getIsf().doubleValue(), units), "sens", HardLimits.MINISF, HardLimits.MAXISF)) + if (!checkOnlyHardLimits(Profile.toMgdl(profile.getIsf(), units), "sens", HardLimits.MINISF, HardLimits.MAXISF)) return; if (!checkOnlyHardLimits(profile.getMaxDailyBasal(), "max_daily_basal", 0.05, HardLimits.maxBasal())) return; if (!checkOnlyHardLimits(pump.getBaseBasalRate(), "current_basal", 0.01, HardLimits.maxBasal())) return; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/AbstractDanaRPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/AbstractDanaRPlugin.java index 758f872ffe..b57b8543f4 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/AbstractDanaRPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/AbstractDanaRPlugin.java @@ -184,7 +184,7 @@ public abstract class AbstractDanaRPlugin implements PluginBase, PumpInterface, int basalIncrement = pump.basal48Enable ? 30 * 60 : 60 * 60; for (int h = 0; h < basalValues; h++) { Double pumpValue = pump.pumpProfiles[pump.activeProfile][h]; - Double profileValue = profile.getBasal((Integer) (h * basalIncrement)); + Double profileValue = profile.getBasalTimeFromMidnight((Integer) (h * basalIncrement)); if (profileValue == null) return true; if (Math.abs(pumpValue - profileValue) > getPumpDescription().basalStep) { log.debug("Diff found. Hour: " + h + " Pump: " + pumpValue + " Profile: " + profileValue); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPump.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPump.java index 520e12d454..1e346695e3 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPump.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPump.java @@ -241,7 +241,7 @@ public class DanaRPump { for (Integer hour = 0; hour < 24; hour++) { //Some values get truncated to the next lower one. // -> round them to two decimals and make sure we are a small delta larger (that will get truncated) - double value = Math.round(100d * nsProfile.getBasal((Integer) (hour * 60 * 60)))/100d + 0.00001; + double value = Math.round(100d * nsProfile.getBasalTimeFromMidnight((Integer) (hour * 60 * 60)))/100d + 0.00001; if (Config.logDanaMessageDetail) log.debug("NS basal value for " + hour + ":00 is " + value); record[hour] = value; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/DanaRSPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/DanaRSPlugin.java index 437e528441..48838117c6 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/DanaRSPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/DanaRSPlugin.java @@ -428,7 +428,7 @@ public class DanaRSPlugin implements PluginBase, PumpInterface, DanaRInterface, int basalIncrement = pump.basal48Enable ? 30 * 60 : 60 * 60; for (int h = 0; h < basalValues; h++) { Double pumpValue = pump.pumpProfiles[pump.activeProfile][h]; - Double profileValue = profile.getBasal((Integer) (h * basalIncrement)); + Double profileValue = profile.getBasalTimeFromMidnight((Integer) (h * basalIncrement)); if (profileValue == null) return true; if (Math.abs(pumpValue - profileValue) > getPumpDescription().basalStep) { log.debug("Diff found. Hour: " + h + " Pump: " + pumpValue + " Profile: " + profileValue); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpVirtual/VirtualPumpPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpVirtual/VirtualPumpPlugin.java index 46e90184fc..0a38477808 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpVirtual/VirtualPumpPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpVirtual/VirtualPumpPlugin.java @@ -52,12 +52,12 @@ public class VirtualPumpPlugin implements PluginBase, PumpInterface { private PumpDescription pumpDescription = new PumpDescription(); private static void loadFakingStatus() { - fromNSAreCommingFakedExtendedBoluses = SP.getBoolean("fromNSAreCommingFakedExtendedBoluses", false); + fromNSAreCommingFakedExtendedBoluses = SP.getBoolean(R.string.key_fromNSAreCommingFakedExtendedBoluses, false); } public static void setFakingStatus(boolean newStatus) { fromNSAreCommingFakedExtendedBoluses = newStatus; - SP.putBoolean("fromNSAreCommingFakedExtendedBoluses", fromNSAreCommingFakedExtendedBoluses); + SP.putBoolean(R.string.key_fromNSAreCommingFakedExtendedBoluses, fromNSAreCommingFakedExtendedBoluses); } public static boolean getFakingStatus() { @@ -73,7 +73,7 @@ public class VirtualPumpPlugin implements PluginBase, PumpInterface { return plugin; } - private VirtualPumpPlugin() { + public VirtualPumpPlugin() { pumpDescription.isBolusCapable = true; pumpDescription.bolusStep = 0.1d; @@ -245,7 +245,7 @@ public class VirtualPumpPlugin implements PluginBase, PumpInterface { public double getBaseBasalRate() { Profile profile = MainApp.getConfigBuilder().getProfile(); if (profile != null) - return profile.getBasal() != null ? profile.getBasal() : 0d; + return profile.getBasal(); else return 0d; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/fragments/ProfileGraph.java b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/fragments/ProfileGraph.java index b38fe4001e..61f4877ccc 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/fragments/ProfileGraph.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/fragments/ProfileGraph.java @@ -33,8 +33,8 @@ public class ProfileGraph extends GraphView { List basalArray = new ArrayList<>(); for (int hour = 0; hour < 24; hour++) { - basalArray.add(new DataPoint(hour, profile.getBasal(new Integer(hour*60*60)))); - basalArray.add(new DataPoint(hour+1, profile.getBasal(new Integer(hour*60*60)))); + basalArray.add(new DataPoint(hour, profile.getBasalTimeFromMidnight(new Integer(hour*60*60)))); + basalArray.add(new DataPoint(hour+1, profile.getBasalTimeFromMidnight(new Integer(hour*60*60)))); } DataPoint[] basalDataPoints = new DataPoint[basalArray.size()]; basalDataPoints = basalArray.toArray(basalDataPoints); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Wear/ActionStringHandler.java b/app/src/main/java/info/nightscout/androidaps/plugins/Wear/ActionStringHandler.java index 30d0060682..b304490e22 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Wear/ActionStringHandler.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Wear/ActionStringHandler.java @@ -586,7 +586,7 @@ public class ActionStringHandler { } final Profile profile = MainApp.getConfigBuilder().getProfile(); - if (profile == null || profile.getBasal() == null) { + if (profile == null) { msg += MainApp.sResources.getString(R.string.notloadedplugins) + "\n"; } if (!"".equals(msg)) { diff --git a/app/src/main/java/info/nightscout/androidaps/receivers/KeepAliveReceiver.java b/app/src/main/java/info/nightscout/androidaps/receivers/KeepAliveReceiver.java index 3d3258c614..810bf124c9 100644 --- a/app/src/main/java/info/nightscout/androidaps/receivers/KeepAliveReceiver.java +++ b/app/src/main/java/info/nightscout/androidaps/receivers/KeepAliveReceiver.java @@ -52,7 +52,7 @@ public class KeepAliveReceiver extends BroadcastReceiver { private void checkPump() { final PumpInterface pump = ConfigBuilderPlugin.getActivePump(); final Profile profile = MainApp.getConfigBuilder().getProfile(); - if (pump != null && profile != null && profile.getBasal() != null) { + if (pump != null && profile != null) { Date lastConnection = pump.lastDataTime(); boolean isStatusOutdated = lastConnection.getTime() + STATUS_UPDATE_FREQUENCY < System.currentTimeMillis(); boolean isBasalOutdated = Math.abs(profile.getBasal() - pump.getBaseBasalRate()) > pump.getPumpDescription().basalStep; diff --git a/app/src/main/java/info/nightscout/utils/LocalAlertUtils.java b/app/src/main/java/info/nightscout/utils/LocalAlertUtils.java index 915d8bfb27..66cabd9db6 100644 --- a/app/src/main/java/info/nightscout/utils/LocalAlertUtils.java +++ b/app/src/main/java/info/nightscout/utils/LocalAlertUtils.java @@ -76,7 +76,7 @@ public class LocalAlertUtils { final PumpInterface pump = ConfigBuilderPlugin.getActivePump(); final Profile profile = MainApp.getConfigBuilder().getProfile(); - if (pump != null && profile != null && profile.getBasal() != null) { + if (pump != null && profile != null) { Date lastConnection = pump.lastDataTime(); long earliestAlarmTime = lastConnection.getTime() + pumpUnreachableThreshold(); if (SP.getLong("nextPumpDisconnectedAlarm", 0l) < earliestAlarmTime) { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 19b4167231..3d4f03b59d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -980,5 +980,6 @@ Carbs On Board Insulin On Board Basals + fromNSAreCommingFakedExtendedBoluses diff --git a/app/src/test/java/info/nightscout/androidaps/data/ProfileTest.java b/app/src/test/java/info/nightscout/androidaps/data/ProfileTest.java new file mode 100644 index 0000000000..0a6cdf9602 --- /dev/null +++ b/app/src/test/java/info/nightscout/androidaps/data/ProfileTest.java @@ -0,0 +1,161 @@ +package info.nightscout.androidaps.data; + +import com.squareup.otto.Bus; +import com.squareup.otto.ThreadEnforcer; + +import junit.framework.Assert; + +import org.json.JSONObject; +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 org.skyscreamer.jsonassert.JSONAssert; + +import java.util.Calendar; +import java.util.TimeZone; + +import info.nightscout.androidaps.Constants; +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.interfaces.PumpInterface; +import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; +import info.nightscout.androidaps.plugins.PumpVirtual.VirtualPumpPlugin; +import info.nightscout.utils.FabricPrivacy; + +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, VirtualPumpPlugin.class, FabricPrivacy.class}) +public class ProfileTest extends Profile { + + PumpInterface pump = new VirtualPumpPlugin(); + String validProfile = "{\"dia\":\"3\",\"carbratio\":[{\"time\":\"00:00\",\"value\":\"30\"}],\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"100\"},{\"time\":\"2:00\",\"value\":\"110\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"0.1\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"5\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}"; + String belowLimitValidProfile = "{\"dia\":\"3\",\"carbratio\":[{\"time\":\"00:00\",\"value\":\"30\"}],\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"100\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"0.001\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"5\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}"; + String notStartingAtZeroValidProfile = "{\"dia\":\"3\",\"carbratio\":[{\"time\":\"00:30\",\"value\":\"30\"}],\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"100\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"0.1\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"5\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}"; + String noUnitsValidProfile = "{\"dia\":\"3\",\"carbratio\":[{\"time\":\"00:00\",\"value\":\"30\"}],\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"100\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"0.1\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"5\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\"}"; + String wrongProfile = "{\"dia\":\"3\",\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"100\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"0.1\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"5\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}"; + + //String profileStore = "{\"defaultProfile\":\"Default\",\"store\":{\"Default\":" + validProfile + "}}"; + + boolean notificationBelowLimitSent = false; + + @Test + public void doTests() throws Exception { + prepareMock(); + + + // Test valid profile + init(new JSONObject(validProfile), 100, 0); + Assert.assertEquals(true, isValid("Test")); + Assert.assertEquals(true, log().contains("NS units: mmol")); + JSONAssert.assertEquals(validProfile, getData(), false); + Assert.assertEquals(3.0d, getDia(), 0.01d); + Assert.assertEquals(TimeZone.getTimeZone("UTC"), getTimeZone()); + Assert.assertEquals("00:30", format_HH_MM(30 * 60)); + Calendar c = Calendar.getInstance(); + c.set(Calendar.HOUR_OF_DAY, 1); + c.set(Calendar.MINUTE, 0); + c.set(Calendar.SECOND, 0); + c.set(Calendar.MILLISECOND, 0); + Assert.assertEquals(100d, getIsf(c.getTimeInMillis()), 0.01d); + c.set(Calendar.HOUR_OF_DAY, 2); + Assert.assertEquals(110d, getIsf(c.getTimeInMillis()), 0.01d); + Assert.assertEquals(110d, getIsfTimeFromMidnight(2 * 60 * 60), 0.01d); + Assert.assertEquals("00:00 100,0 mmol/U\n" + "02:00 110,0 mmol/U", getIsfList()); + Assert.assertEquals(30d, getIc(c.getTimeInMillis()), 0.01d); + Assert.assertEquals(30d, getIcTimeFromMidnight(2 * 60 * 60), 0.01d); + Assert.assertEquals("00:00 30,0 g/U", getIcList()); + Assert.assertEquals(0.1d, getBasal(c.getTimeInMillis()), 0.01d); + Assert.assertEquals(0.1d, getBasalTimeFromMidnight(2 * 60 * 60), 0.01d); + Assert.assertEquals("00:00 0,10 U/h", getBasalList()); + Assert.assertEquals(0.1d, getBasalValues()[0].value); + Assert.assertEquals(0.1d, getMaxDailyBasal()); + Assert.assertEquals(2.4d, percentageBasalSum(), 0.01d); + Assert.assertEquals(2.4d, baseBasalSum(), 0.01d); + Assert.assertEquals(4.5d, getTarget(2 * 60 * 60), 0.01d); + Assert.assertEquals(4d, getTargetLow(c.getTimeInMillis()), 0.01d); + Assert.assertEquals(4d, getTargetLowTimeFromMidnight(2 * 60 * 60), 0.01d); + Assert.assertEquals(5d, getTargetHigh(c.getTimeInMillis()), 0.01d); + Assert.assertEquals(5d, getTargetHighTimeFromMidnight(2 * 60 * 60), 0.01d); + Assert.assertEquals("00:00 4,0 - 5,0 mmol", getTargetList()); + Assert.assertEquals(100, getPercentage()); + Assert.assertEquals(0, getTimeshift()); + + Assert.assertEquals(0.1d, toMgdl(0.1d, Constants.MGDL)); + Assert.assertEquals(18d, toMgdl(1d, Constants.MMOL)); + Assert.assertEquals(1d, toMmol(18d, Constants.MGDL)); + Assert.assertEquals(18d, toMmol(18d, Constants.MMOL)); + Assert.assertEquals(18d, fromMgdlToUnits(18d, Constants.MGDL)); + Assert.assertEquals(1d, fromMgdlToUnits(18d, Constants.MMOL)); + Assert.assertEquals(18d, toUnits(18d, 1d, Constants.MGDL)); + Assert.assertEquals(1d, toUnits(18d, 1d, Constants.MMOL)); + Assert.assertEquals("18", toUnitsString(18d, 1d, Constants.MGDL)); + Assert.assertEquals("1,0", toUnitsString(18d, 1d, Constants.MMOL)); + Assert.assertEquals("5 - 6", toTargetRangeString(5d, 6d, Constants.MGDL, Constants.MGDL)); + + //Test basal profile below limit + Assert.assertEquals(false, notificationBelowLimitSent); + init(new JSONObject(belowLimitValidProfile), 100, 0); + isValid("Test"); + Assert.assertEquals(true, notificationBelowLimitSent); + + + // Test profile w/o units + init(new JSONObject(noUnitsValidProfile), 100, 0); + Assert.assertEquals(null, getUnits()); + Profile nup = new Profile(new JSONObject(noUnitsValidProfile), Constants.MMOL); + Assert.assertEquals(Constants.MMOL, nup.getUnits()); + // failover to MGDL + nup = new Profile(new JSONObject(noUnitsValidProfile), null); + Assert.assertEquals(Constants.MGDL, nup.getUnits()); + + //Test profile not starting at midnight + init(new JSONObject(notStartingAtZeroValidProfile), 100, 0); + Assert.assertEquals(30.0d, getIc(0), 0.01d); + + // Test wrong profile + init(new JSONObject(wrongProfile), 100, 0); + Assert.assertEquals(false, isValid("Test")); + + // Test percentage functionality + init(new JSONObject(validProfile), 50, 0); + Assert.assertEquals(0.05d, getBasal(c.getTimeInMillis()), 0.01d); + Assert.assertEquals(1.2d, percentageBasalSum(), 0.01d); + Assert.assertEquals(60d, getIc(c.getTimeInMillis()), 0.01d); + Assert.assertEquals(220d, getIsf(c.getTimeInMillis()), 0.01d); + + // Test timeshift functionality + init(new JSONObject(validProfile), 100, 1); + Assert.assertEquals( + "00:00 110,0 mmol/U\n" + + "01:00 100,0 mmol/U\n" + + "03:00 110,0 mmol/U", getIsfList()); + } + + private void prepareMock() throws Exception { + ConfigBuilderPlugin configBuilderPlugin = mock(ConfigBuilderPlugin.class); + PowerMockito.mockStatic(ConfigBuilderPlugin.class); + + MainApp mainApp = mock(MainApp.class); + PowerMockito.mockStatic(MainApp.class); + when(MainApp.instance()).thenReturn(mainApp); + when(MainApp.getConfigBuilder()).thenReturn(configBuilderPlugin); + when(MainApp.getConfigBuilder().getActivePump()).thenReturn(pump); + + PowerMockito.mockStatic(FabricPrivacy.class); +// PowerMockito.doNothing().when(FabricPrivacy.log("")); + + Bus bus = new Bus(ThreadEnforcer.ANY); + when(MainApp.bus()).thenReturn(bus); + } + + @Override + protected void sendBelowMinimumNotification(String from) { + notificationBelowLimitSent = true; + } +} diff --git a/app/src/test/java/info/nightscout/androidaps/queue/CommandQueueTest.java b/app/src/test/java/info/nightscout/androidaps/queue/CommandQueueTest.java index 31d8636de2..e6e7a4c97f 100644 --- a/app/src/test/java/info/nightscout/androidaps/queue/CommandQueueTest.java +++ b/app/src/test/java/info/nightscout/androidaps/queue/CommandQueueTest.java @@ -33,7 +33,7 @@ import static org.mockito.Mockito.when; */ @RunWith(PowerMockRunner.class) -@PrepareForTest({MainApp.class, ConfigBuilderPlugin.class, ConfigBuilderPlugin.class, ToastUtils.class, Context.class}) +@PrepareForTest({MainApp.class, ConfigBuilderPlugin.class, ToastUtils.class, Context.class}) public class CommandQueueTest extends CommandQueue { String profileJson = "{\"dia\":\"3\",\"carbratio\":[{\"time\":\"00:00\",\"value\":\"30\"}],\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"100\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"0.1\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"5\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}";