BolusExtensionKtTest

This commit is contained in:
Milos Kozak 2023-09-02 14:09:33 +02:00
parent 50818c2d4c
commit 2538378c0c
5 changed files with 262 additions and 0 deletions

View file

@ -24,4 +24,6 @@ dependencies {
implementation project(':core:utils')
implementation project(':core:validators')
implementation project(':database:entities')
testImplementation project(':core:main')
}

View file

@ -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
}
}

View file

@ -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)
}
}

View file

@ -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) {}
}

View file

@ -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)
}
}