Improve basal schedule validation and add profile to basal schedule mapping tests

This commit is contained in:
Bart Sopers 2019-12-20 23:07:38 +01:00
parent a399c24ccd
commit 0bcdf30a7e
5 changed files with 198 additions and 8 deletions

View file

@ -10,8 +10,10 @@ public class BasalSchedule {
private final List<BasalScheduleEntry> entries; private final List<BasalScheduleEntry> entries;
public BasalSchedule(List<BasalScheduleEntry> entries) { public BasalSchedule(List<BasalScheduleEntry> entries) {
if (entries == null) { if (entries == null || entries.size() == 0) {
throw new IllegalArgumentException("Entries cannot be null"); throw new IllegalArgumentException("Entries can not be empty");
} else if (!entries.get(0).getStartTime().isEqual(Duration.ZERO)) {
throw new IllegalArgumentException("First basal schedule entry should have 0 offset");
} }
this.entries = entries; this.entries = entries;
} }

View file

@ -9,10 +9,14 @@ public class BasalScheduleEntry {
private final Duration startTime; private final Duration startTime;
public BasalScheduleEntry(double rate, Duration startTime) { public BasalScheduleEntry(double rate, Duration startTime) {
if (rate < 0D) { if (startTime.isLongerThan(Duration.standardHours(24).minus(Duration.standardSeconds(1))) || startTime.isShorterThan(Duration.ZERO)) {
throw new IllegalArgumentException("Invalid start time");
} else if (rate < 0D) {
throw new IllegalArgumentException("Rate should be >= 0"); throw new IllegalArgumentException("Rate should be >= 0");
} else if (rate > OmnipodConst.MAX_BASAL_RATE) { } else if (rate > OmnipodConst.MAX_BASAL_RATE) {
throw new IllegalArgumentException("Rate exceeds max basal rate"); throw new IllegalArgumentException("Rate exceeds max basal rate");
} else if (rate % OmnipodConst.POD_PULSE_SIZE > 0.000001 && rate % OmnipodConst.POD_PULSE_SIZE - OmnipodConst.POD_PULSE_SIZE < -0.000001) {
throw new IllegalArgumentException("Unsupported basal rate precision");
} }
this.rate = rate; this.rate = rate;
this.startTime = startTime; this.startTime = startTime;

View file

@ -173,7 +173,13 @@ public class AapsOmnipodManager implements OmnipodCommunicationManagerInterface
} }
} else if (PodInitActionType.FillCannulaSetBasalProfileWizardStep.equals(podInitActionType)) { } else if (PodInitActionType.FillCannulaSetBasalProfileWizardStep.equals(podInitActionType)) {
try { try {
Disposable disposable = delegate.insertCannula(mapProfileToBasalSchedule(profile)).subscribe(res -> // BasalSchedule basalSchedule;
try {
basalSchedule = mapProfileToBasalSchedule(profile);
} catch (Exception ex) {
throw new CommandInitializationException("Basal profile mapping failed", ex);
}
Disposable disposable = delegate.insertCannula(basalSchedule).subscribe(res -> //
handleSetupActionResult(podInitActionType, podInitReceiver, res, time)); handleSetupActionResult(podInitActionType, podInitReceiver, res, time));
return new PumpEnactResult().success(true).enacted(true); return new PumpEnactResult().success(true).enacted(true);
} catch (Exception ex) { } catch (Exception ex) {
@ -222,11 +228,17 @@ public class AapsOmnipodManager implements OmnipodCommunicationManagerInterface
} }
@Override @Override
public PumpEnactResult setBasalProfile(Profile basalProfile) { public PumpEnactResult setBasalProfile(Profile profile) {
long time = System.currentTimeMillis(); long time = System.currentTimeMillis();
try { try {
delegate.setBasalSchedule(mapProfileToBasalSchedule(basalProfile), isBasalBeepsEnabled()); BasalSchedule basalSchedule;
addSuccessToHistory(time, PodHistoryEntryType.SetBasalSchedule, basalProfile); try {
basalSchedule = mapProfileToBasalSchedule(profile);
} catch (Exception ex) {
throw new CommandInitializationException("Basal profile mapping failed", ex);
}
delegate.setBasalSchedule(basalSchedule, isBasalBeepsEnabled());
addSuccessToHistory(time, PodHistoryEntryType.SetBasalSchedule, profile);
} catch (Exception ex) { } catch (Exception ex) {
if ((ex instanceof OmnipodException) && !((OmnipodException) ex).isCertainFailure()) { if ((ex instanceof OmnipodException) && !((OmnipodException) ex).isCertainFailure()) {
addToHistory(time, PodHistoryEntryType.SetBasalSchedule, "Uncertain failure", false); addToHistory(time, PodHistoryEntryType.SetBasalSchedule, "Uncertain failure", false);
@ -623,9 +635,14 @@ public class AapsOmnipodManager implements OmnipodCommunicationManagerInterface
return L.isEnabled(L.PUMP); return L.isEnabled(L.PUMP);
} }
// TODO add tests
static BasalSchedule mapProfileToBasalSchedule(Profile profile) { static BasalSchedule mapProfileToBasalSchedule(Profile profile) {
if (profile == null) {
throw new IllegalArgumentException("Profile can not be null");
}
Profile.ProfileValue[] basalValues = profile.getBasalValues(); Profile.ProfileValue[] basalValues = profile.getBasalValues();
if(basalValues == null) {
throw new IllegalArgumentException("Basal values can not be null");
}
List<BasalScheduleEntry> entries = new ArrayList<>(); List<BasalScheduleEntry> entries = new ArrayList<>();
for (Profile.ProfileValue basalValue : basalValues) { for (Profile.ProfileValue basalValue : basalValues) {
entries.add(new BasalScheduleEntry(basalValue.value, Duration.standardSeconds(basalValue.timeAsSeconds))); entries.add(new BasalScheduleEntry(basalValue.value, Duration.standardSeconds(basalValue.timeAsSeconds)));

View file

@ -4,4 +4,8 @@ public class CommandInitializationException extends OmnipodException {
public CommandInitializationException(String message) { public CommandInitializationException(String message) {
super(message, true); super(message, true);
} }
public CommandInitializationException(String message, Throwable cause) {
super(message, cause, true);
}
} }

View file

@ -0,0 +1,163 @@
package info.nightscout.androidaps.plugins.pump.omnipod.driver.comm;
import org.joda.time.Duration;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import java.util.List;
import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.plugins.pump.omnipod.defs.schedule.BasalSchedule;
import info.nightscout.androidaps.plugins.pump.omnipod.defs.schedule.BasalScheduleEntry;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.powermock.api.mockito.PowerMockito.when;
public class AapsOmnipodManagerTest {
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void validProfile() {
Profile profile = mock(Profile.class);
Profile.ProfileValue value1 = mock(Profile.ProfileValue.class);
value1.timeAsSeconds = 0;
value1.value = 0.5D;
Profile.ProfileValue value2 = mock(Profile.ProfileValue.class);
value2.timeAsSeconds = 18000;
value2.value = 1.0D;
Profile.ProfileValue value3 = mock(Profile.ProfileValue.class);
value3.timeAsSeconds = 50400;
value3.value = 3.05D;
when(profile.getBasalValues()).thenReturn(new Profile.ProfileValue[]{
value1,
value2,
value3
});
BasalSchedule basalSchedule = AapsOmnipodManager.mapProfileToBasalSchedule(profile);
List<BasalScheduleEntry> entries = basalSchedule.getEntries();
assertEquals(3, entries.size());
BasalScheduleEntry entry1 = entries.get(0);
assertEquals(Duration.standardSeconds(0), entry1.getStartTime());
assertEquals(0.5D, entry1.getRate(), 0.000001);
BasalScheduleEntry entry2 = entries.get(1);
assertEquals(Duration.standardSeconds(18000), entry2.getStartTime());
assertEquals(1.0D, entry2.getRate(), 0.000001);
BasalScheduleEntry entry3 = entries.get(2);
assertEquals(Duration.standardSeconds(50400), entry3.getStartTime());
assertEquals(3.05D, entry3.getRate(), 0.000001);
}
@Test
public void invalidProfileNullProfile() {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("Profile can not be null");
AapsOmnipodManager.mapProfileToBasalSchedule(null);
}
@Test
public void invalidProfileNullEntries() {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("Basal values can not be null");
AapsOmnipodManager.mapProfileToBasalSchedule(mock(Profile.class));
}
@Test
public void invalidProfileZeroEntries() {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("Entries can not be empty");
Profile profile = mock(Profile.class);
when(profile.getBasalValues()).thenReturn(new Profile.ProfileValue[0]);
AapsOmnipodManager.mapProfileToBasalSchedule(profile);
}
@Test
public void invalidProfileNonZeroOffset() {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("First basal schedule entry should have 0 offset");
Profile profile = mock(Profile.class);
Profile.ProfileValue value = mock(Profile.ProfileValue.class);
value.timeAsSeconds = 500;
value.value = 0.5D;
when(profile.getBasalValues()).thenReturn(new Profile.ProfileValue[]{
value,
});
AapsOmnipodManager.mapProfileToBasalSchedule(profile);
}
@Test
public void invalidProfileMoreThan24Hours() {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("Invalid start time");
Profile profile = mock(Profile.class);
Profile.ProfileValue value1 = mock(Profile.ProfileValue.class);
value1.timeAsSeconds = 0;
value1.value = 0.5D;
Profile.ProfileValue value2 = mock(Profile.ProfileValue.class);
value2.timeAsSeconds = 86400;
value2.value = 0.5D;
when(profile.getBasalValues()).thenReturn(new Profile.ProfileValue[]{
value1,
value2
});
AapsOmnipodManager.mapProfileToBasalSchedule(profile);
}
@Test
public void invalidProfileNegativeOffset() {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("Invalid start time");
Profile profile = mock(Profile.class);
Profile.ProfileValue value = mock(Profile.ProfileValue.class);
value.timeAsSeconds = -1;
value.value = 0.5D;
when(profile.getBasalValues()).thenReturn(new Profile.ProfileValue[]{
value,
});
AapsOmnipodManager.mapProfileToBasalSchedule(profile);
}
@Test
public void invalidProfileUnsupportedPrecision() {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("Unsupported basal rate precision");
Profile profile = mock(Profile.class);
Profile.ProfileValue value = mock(Profile.ProfileValue.class);
value.timeAsSeconds = 500;
value.value = 0.04D;
when(profile.getBasalValues()).thenReturn(new Profile.ProfileValue[]{
value,
});
AapsOmnipodManager.mapProfileToBasalSchedule(profile);
}
}