BolusExtensionKtTest
This commit is contained in:
parent
50818c2d4c
commit
2538378c0c
5 changed files with 262 additions and 0 deletions
|
@ -24,4 +24,6 @@ dependencies {
|
||||||
implementation project(':core:utils')
|
implementation project(':core:utils')
|
||||||
implementation project(':core:validators')
|
implementation project(':core:validators')
|
||||||
implementation project(':database:entities')
|
implementation project(':database:entities')
|
||||||
|
|
||||||
|
testImplementation project(':core:main')
|
||||||
}
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
package info.nightscout.androidaps
|
||||||
|
|
||||||
|
import info.nightscout.interfaces.utils.HardLimits
|
||||||
|
import info.nightscout.shared.interfaces.ResourceHelper
|
||||||
|
import info.nightscout.shared.sharedPreferences.SP
|
||||||
|
import javax.inject.Inject
|
||||||
|
import kotlin.math.max
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
|
class HardLimitsMock @Inject constructor(
|
||||||
|
private val sp: SP,
|
||||||
|
private val rh: ResourceHelper
|
||||||
|
) : HardLimits {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
private const val CHILD = 0
|
||||||
|
private const val TEENAGE = 1
|
||||||
|
private const val ADULT = 2
|
||||||
|
private const val RESISTANT_ADULT = 3
|
||||||
|
private const val PREGNANT = 4
|
||||||
|
private val MAX_BOLUS = doubleArrayOf(5.0, 10.0, 17.0, 25.0, 60.0)
|
||||||
|
|
||||||
|
// Very Hard Limits Ranges
|
||||||
|
// First value is the Lowest and second value is the Highest a Limit can define
|
||||||
|
val VERY_HARD_LIMIT_MIN_BG = doubleArrayOf(80.0, 180.0)
|
||||||
|
val VERY_HARD_LIMIT_MAX_BG = doubleArrayOf(90.0, 200.0)
|
||||||
|
val VERY_HARD_LIMIT_TARGET_BG = doubleArrayOf(80.0, 200.0)
|
||||||
|
|
||||||
|
// Very Hard Limits Ranges for Temp Targets
|
||||||
|
val VERY_HARD_LIMIT_TEMP_MIN_BG = intArrayOf(72, 180)
|
||||||
|
val VERY_HARD_LIMIT_TEMP_MAX_BG = intArrayOf(72, 270)
|
||||||
|
val VERY_HARD_LIMIT_TEMP_TARGET_BG = intArrayOf(72, 200)
|
||||||
|
val MIN_DIA = doubleArrayOf(5.0, 5.0, 5.0, 5.0, 5.0)
|
||||||
|
val MAX_DIA = doubleArrayOf(9.0, 9.0, 9.0, 9.0, 10.0)
|
||||||
|
val MIN_IC = doubleArrayOf(2.0, 2.0, 2.0, 2.0, 0.3)
|
||||||
|
val MAX_IC = doubleArrayOf(100.0, 100.0, 100.0, 100.0, 100.0)
|
||||||
|
const val MIN_ISF = 2.0 // mgdl
|
||||||
|
const val MAX_ISF = 1000.0 // mgdl
|
||||||
|
val MAX_IOB_AMA = doubleArrayOf(3.0, 5.0, 7.0, 12.0, 25.0)
|
||||||
|
val MAX_IOB_SMB = doubleArrayOf(7.0, 13.0, 22.0, 30.0, 70.0)
|
||||||
|
val MAX_BASAL = doubleArrayOf(2.0, 5.0, 10.0, 12.0, 25.0)
|
||||||
|
|
||||||
|
//LGS Hard limits
|
||||||
|
//No IOB at all
|
||||||
|
const val MAX_IOB_LGS = 0.0
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadAge(): Int = when (sp.getString(info.nightscout.core.utils.R.string.key_age, "")) {
|
||||||
|
rh.gs(info.nightscout.core.utils.R.string.key_child) -> CHILD
|
||||||
|
rh.gs(info.nightscout.core.utils.R.string.key_teenage) -> TEENAGE
|
||||||
|
rh.gs(info.nightscout.core.utils.R.string.key_adult) -> ADULT
|
||||||
|
rh.gs(info.nightscout.core.utils.R.string.key_resistantadult) -> RESISTANT_ADULT
|
||||||
|
rh.gs(info.nightscout.core.utils.R.string.key_pregnant) -> PREGNANT
|
||||||
|
else -> ADULT
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun maxBolus(): Double = MAX_BOLUS[loadAge()]
|
||||||
|
override fun maxIobAMA(): Double = MAX_IOB_AMA[loadAge()]
|
||||||
|
override fun maxIobSMB(): Double = MAX_IOB_SMB[loadAge()]
|
||||||
|
override fun maxBasal(): Double = MAX_BASAL[loadAge()]
|
||||||
|
override fun minDia(): Double = MIN_DIA[loadAge()]
|
||||||
|
override fun maxDia(): Double = MAX_DIA[loadAge()]
|
||||||
|
override fun minIC(): Double = MIN_IC[loadAge()]
|
||||||
|
override fun maxIC(): Double = MAX_IC[loadAge()]
|
||||||
|
|
||||||
|
// safety checks
|
||||||
|
override fun checkHardLimits(value: Double, valueName: Int, lowLimit: Double, highLimit: Double): Boolean =
|
||||||
|
value == verifyHardLimits(value, valueName, lowLimit, highLimit)
|
||||||
|
|
||||||
|
override fun isInRange(value: Double, lowLimit: Double, highLimit: Double): Boolean =
|
||||||
|
value in lowLimit..highLimit
|
||||||
|
|
||||||
|
override fun verifyHardLimits(value: Double, valueName: Int, lowLimit: Double, highLimit: Double): Double {
|
||||||
|
var newValue = value
|
||||||
|
if (newValue < lowLimit || newValue > highLimit) {
|
||||||
|
newValue = max(newValue, lowLimit)
|
||||||
|
newValue = min(newValue, highLimit)
|
||||||
|
}
|
||||||
|
return newValue
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
package info.nightscout.androidaps
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import dagger.android.AndroidInjector
|
||||||
|
import dagger.android.HasAndroidInjector
|
||||||
|
import info.nightscout.core.extensions.pureProfileFromJson
|
||||||
|
import info.nightscout.core.profile.ProfileSealed
|
||||||
|
import info.nightscout.interfaces.Config
|
||||||
|
import info.nightscout.interfaces.plugin.ActivePlugin
|
||||||
|
import info.nightscout.interfaces.profile.DefaultValueHelper
|
||||||
|
import info.nightscout.interfaces.profile.Profile
|
||||||
|
import info.nightscout.interfaces.profile.ProfileFunction
|
||||||
|
import info.nightscout.interfaces.utils.HardLimits
|
||||||
|
import info.nightscout.rx.bus.RxBus
|
||||||
|
import info.nightscout.shared.interfaces.ResourceHelper
|
||||||
|
import info.nightscout.shared.sharedPreferences.SP
|
||||||
|
import info.nightscout.shared.utils.DateUtil
|
||||||
|
import org.json.JSONObject
|
||||||
|
import org.junit.jupiter.api.BeforeEach
|
||||||
|
import org.mockito.Mock
|
||||||
|
import org.mockito.Mockito.`when`
|
||||||
|
|
||||||
|
@Suppress("SpellCheckingInspection")
|
||||||
|
open class TestBaseWithProfile : TestBase() {
|
||||||
|
|
||||||
|
@Mock lateinit var activePluginProvider: ActivePlugin
|
||||||
|
@Mock lateinit var rh: ResourceHelper
|
||||||
|
@Mock lateinit var profileFunction: ProfileFunction
|
||||||
|
@Mock lateinit var defaultValueHelper: DefaultValueHelper
|
||||||
|
@Mock lateinit var dateUtil: DateUtil
|
||||||
|
@Mock lateinit var config: Config
|
||||||
|
@Mock lateinit var sp: SP
|
||||||
|
@Mock lateinit var context: Context
|
||||||
|
|
||||||
|
lateinit var hardLimits: HardLimits
|
||||||
|
lateinit var testPumpPlugin: TestPumpPlugin
|
||||||
|
|
||||||
|
val rxBus = RxBus(aapsSchedulers, aapsLogger)
|
||||||
|
|
||||||
|
val profileInjector = HasAndroidInjector { AndroidInjector { } }
|
||||||
|
|
||||||
|
private lateinit var invalidProfileJSON: String
|
||||||
|
private lateinit var validProfileJSON: String
|
||||||
|
lateinit var validProfile: Profile
|
||||||
|
lateinit var invalidProfile: Profile
|
||||||
|
@Suppress("PropertyName") val TESTPROFILENAME = "someProfile"
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
fun prepareMock() {
|
||||||
|
invalidProfileJSON = "{\"dia\":\"1\",\"carbratio\":[{\"time\":\"00:00\",\"value\":\"30\"}],\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"3\"}," +
|
||||||
|
"{\"time\":\"2:00\",\"value\":\"3.4\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"1\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4.5\"}]," +
|
||||||
|
"\"target_high\":[{\"time\":\"00:00\",\"value\":\"7\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}"
|
||||||
|
validProfileJSON = "{\"dia\":\"5\",\"carbratio\":[{\"time\":\"00:00\",\"value\":\"30\"}],\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"3\"}," +
|
||||||
|
"{\"time\":\"2:00\",\"value\":\"3.4\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"1\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4.5\"}]," +
|
||||||
|
"\"target_high\":[{\"time\":\"00:00\",\"value\":\"7\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}"
|
||||||
|
validProfile = ProfileSealed.Pure(pureProfileFromJson(JSONObject(validProfileJSON), dateUtil)!!)
|
||||||
|
testPumpPlugin = TestPumpPlugin(profileInjector)
|
||||||
|
`when`(activePluginProvider.activePump).thenReturn(testPumpPlugin)
|
||||||
|
hardLimits = HardLimitsMock(sp, rh)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
package info.nightscout.androidaps
|
||||||
|
|
||||||
|
import dagger.android.HasAndroidInjector
|
||||||
|
import info.nightscout.interfaces.profile.Profile
|
||||||
|
import info.nightscout.interfaces.pump.DetailedBolusInfo
|
||||||
|
import info.nightscout.interfaces.pump.Pump
|
||||||
|
import info.nightscout.interfaces.pump.PumpEnactResult
|
||||||
|
import info.nightscout.interfaces.pump.PumpSync
|
||||||
|
import info.nightscout.interfaces.pump.defs.ManufacturerType
|
||||||
|
import info.nightscout.interfaces.pump.defs.PumpDescription
|
||||||
|
import info.nightscout.interfaces.pump.defs.PumpType
|
||||||
|
import info.nightscout.interfaces.utils.TimeChangeType
|
||||||
|
import org.json.JSONObject
|
||||||
|
|
||||||
|
@Suppress("MemberVisibilityCanBePrivate")
|
||||||
|
class TestPumpPlugin(val injector: HasAndroidInjector) : Pump {
|
||||||
|
|
||||||
|
var connected = false
|
||||||
|
var isProfileSet = true
|
||||||
|
|
||||||
|
override fun isConnected() = connected
|
||||||
|
override fun isConnecting() = false
|
||||||
|
override fun isHandshakeInProgress() = false
|
||||||
|
val lastData = 0L
|
||||||
|
|
||||||
|
val baseBasal = 0.0
|
||||||
|
override var pumpDescription = PumpDescription()
|
||||||
|
|
||||||
|
override fun isInitialized(): Boolean = true
|
||||||
|
override fun isSuspended(): Boolean = false
|
||||||
|
override fun isBusy(): Boolean = false
|
||||||
|
override fun connect(reason: String) {
|
||||||
|
connected = true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun disconnect(reason: String) {
|
||||||
|
connected = false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun stopConnecting() {
|
||||||
|
connected = false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun waitForDisconnectionInSeconds(): Int = 0
|
||||||
|
override fun getPumpStatus(reason: String) {}
|
||||||
|
override fun setNewBasalProfile(profile: Profile): PumpEnactResult = PumpEnactResult(injector)
|
||||||
|
override fun isThisProfileSet(profile: Profile): Boolean = isProfileSet
|
||||||
|
override fun lastDataTime(): Long = lastData
|
||||||
|
override val baseBasalRate: Double = baseBasal
|
||||||
|
override val reservoirLevel: Double = 0.0
|
||||||
|
override val batteryLevel: Int = 0
|
||||||
|
override fun deliverTreatment(detailedBolusInfo: DetailedBolusInfo): PumpEnactResult = PumpEnactResult(injector).success(true)
|
||||||
|
override fun stopBolusDelivering() {}
|
||||||
|
override fun setTempBasalAbsolute(absoluteRate: Double, durationInMinutes: Int, profile: Profile, enforceNew: Boolean, tbrType: PumpSync.TemporaryBasalType): PumpEnactResult =
|
||||||
|
PumpEnactResult(injector).success(true)
|
||||||
|
|
||||||
|
override fun setTempBasalPercent(percent: Int, durationInMinutes: Int, profile: Profile, enforceNew: Boolean, tbrType: PumpSync.TemporaryBasalType): PumpEnactResult =
|
||||||
|
PumpEnactResult(injector).success(true)
|
||||||
|
|
||||||
|
override fun setExtendedBolus(insulin: Double, durationInMinutes: Int): PumpEnactResult = PumpEnactResult(injector).success(true)
|
||||||
|
override fun cancelTempBasal(enforceNew: Boolean): PumpEnactResult = PumpEnactResult(injector).success(true)
|
||||||
|
override fun cancelExtendedBolus(): PumpEnactResult = PumpEnactResult(injector).success(true)
|
||||||
|
override fun getJSONStatus(profile: Profile, profileName: String, version: String): JSONObject = JSONObject()
|
||||||
|
override fun manufacturer(): ManufacturerType = ManufacturerType.AAPS
|
||||||
|
override fun model(): PumpType = PumpType.GENERIC_AAPS
|
||||||
|
override fun serialNumber(): String = "1"
|
||||||
|
override fun shortStatus(veryShort: Boolean): String = ""
|
||||||
|
override val isFakingTempsByExtendedBoluses: Boolean = false
|
||||||
|
override fun loadTDDs(): PumpEnactResult = PumpEnactResult(injector).success(true)
|
||||||
|
override fun canHandleDST(): Boolean = true
|
||||||
|
override fun timezoneOrDSTChanged(timeChangeType: TimeChangeType) {}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
package info.nightscout.core.extensions
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.TestBaseWithProfile
|
||||||
|
import info.nightscout.database.entities.Bolus
|
||||||
|
import info.nightscout.insulin.InsulinLyumjevPlugin
|
||||||
|
import info.nightscout.interfaces.insulin.Insulin
|
||||||
|
import info.nightscout.interfaces.plugin.ActivePlugin
|
||||||
|
import info.nightscout.interfaces.profile.ProfileFunction
|
||||||
|
import info.nightscout.interfaces.ui.UiInteraction
|
||||||
|
import info.nightscout.shared.utils.T
|
||||||
|
import org.junit.jupiter.api.Assertions
|
||||||
|
import org.junit.jupiter.api.BeforeEach
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.mockito.Mock
|
||||||
|
import org.mockito.Mockito
|
||||||
|
|
||||||
|
class BolusExtensionKtTest : TestBaseWithProfile() {
|
||||||
|
|
||||||
|
@Mock lateinit var activePlugin: ActivePlugin
|
||||||
|
@Mock lateinit var profileFunctions: ProfileFunction
|
||||||
|
@Mock lateinit var uiInteraction: UiInteraction
|
||||||
|
|
||||||
|
private lateinit var insulin: Insulin
|
||||||
|
|
||||||
|
private val now = 1000000L
|
||||||
|
private val dia = 7.0
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
fun setup() {
|
||||||
|
insulin = InsulinLyumjevPlugin(profileInjector, rh, profileFunctions, rxBus, aapsLogger, config, hardLimits, uiInteraction)
|
||||||
|
Mockito.`when`(activePlugin.activeInsulin).thenReturn(insulin)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun iobCalc() {
|
||||||
|
val bolus = Bolus(timestamp = now - 1, amount = 1.0, type = Bolus.Type.NORMAL)
|
||||||
|
// there should be almost full IOB after now
|
||||||
|
Assertions.assertEquals(1.0, bolus.iobCalc(activePlugin, now, dia).iobContrib, 0.01)
|
||||||
|
// there should be less that 5% after DIA -1
|
||||||
|
Assertions.assertTrue(0.05 > bolus.iobCalc(activePlugin, now + T.hours(dia.toLong() - 1).msecs(), dia).iobContrib)
|
||||||
|
// there should be zero after DIA
|
||||||
|
Assertions.assertEquals(0.0, bolus.iobCalc(activePlugin, now + T.hours(dia.toLong() + 1).msecs(), dia).iobContrib)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue