Преглед на файлове

Add Unit test; some fixes

Marvin Polscheit преди 1 година
родител
ревизия
834a671272

+ 19 - 0
Trio.xcodeproj/xcshareddata/xcschemes/Trio Tests.xcscheme

@@ -36,6 +36,16 @@
       debugDocumentVersioning = "YES"
       debugServiceExtension = "internal"
       allowLocationSimulation = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "388E595725AD948C0019842D"
+            BuildableName = "Trio.app"
+            BlueprintName = "Trio"
+            ReferencedContainer = "container:Trio.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
    </LaunchAction>
    <ProfileAction
       buildConfiguration = "Release"
@@ -43,6 +53,15 @@
       savedToolIdentifier = ""
       useCustomWorkingDirectory = "NO"
       debugDocumentVersioning = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "388E595725AD948C0019842D"
+            BuildableName = "Trio.app"
+            BlueprintName = "Trio"
+            ReferencedContainer = "container:Trio.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
    </ProfileAction>
    <AnalyzeAction
       buildConfiguration = "Debug">

+ 4 - 2
Trio/Sources/Modules/Treatments/View/ForecastChart.swift

@@ -50,8 +50,10 @@ struct ForecastChart: View {
     }
 
     private var forecastChartLabels: some View {
-        // Check if carbs are backdated (date is in the past)
-        let isBackdated = state.date < Date()
+        // Check if carbs are actually backdated (more than 15 minutes in the past)
+        // This ensures we only consider it backdated if the user has deliberately changed the date
+        let minutesThreshold = 15.0 // 15 minutes threshold
+        let isBackdated = state.date.timeIntervalSinceNow < -minutesThreshold * 60 && state.simulatedDetermination != nil
 
         // When backdated, display no carbs as this label is only supposed to show current entered carbs
         let displayedCarbs = isBackdated ? 0 : state.carbs

+ 4 - 2
Trio/Sources/Modules/Treatments/View/PopupView.swift

@@ -312,8 +312,10 @@ struct PopupView: View {
     /// Don't allow total carbs to exceed Max IOB setting.
     /// Formula: (Current COB + New Carbs) / Carb Ratio = COB Correction Dose
     private var cobCardContent: some View {
-        // Check if carbs are backdated (date is in the past)
-        let isBackdated = state.date < Date()
+        // Check if carbs are actually backdated (more than 15 minutes in the past)
+        // This ensures we only consider it backdated if the user has deliberately changed the date
+        let minutesThreshold = 15.0 // 15 minutes threshold
+        let isBackdated = state.date.timeIntervalSinceNow < -minutesThreshold * 60 && state.simulatedDetermination != nil
 
         // Determine COB and carbs to display based on backdating status
         let displayedCOB = isBackdated ? (state.simulatedDetermination?.cob ?? Decimal(state.cob)) : Decimal(state.cob)

+ 0 - 3
Trio/Sources/Services/BolusCalculator/BolusCalculationManager.swift

@@ -468,7 +468,6 @@ final class BaseBolusCalculationManager: BolusCalculationManager, Injectable {
             insulinCalculated: insulinCalculated,
             factoredInsulin: factoredInsulin,
             wholeCalc: wholeCalc,
-            correctionInsulin: targetDifferenceInsulin,
             iobInsulinReduction: iobInsulinReduction,
             superBolusInsulin: superBolusInsulin,
             targetDifference: targetDifference,
@@ -516,7 +515,6 @@ final class BaseBolusCalculationManager: BolusCalculationManager, Injectable {
                 insulinCalculated: 0,
                 factoredInsulin: 0,
                 wholeCalc: 0,
-                correctionInsulin: 0,
                 iobInsulinReduction: 0,
                 superBolusInsulin: 0,
                 targetDifference: 0,
@@ -557,7 +555,6 @@ struct CalculationResult: Sendable {
     let insulinCalculated: Decimal // Final calculated insulin amount which respects limits
     let factoredInsulin: Decimal // Total calculation after adjustments
     let wholeCalc: Decimal // Total calculation before adjustments
-    let correctionInsulin: Decimal // Insulin for BG correction
     let iobInsulinReduction: Decimal // IOB reduction amount
     let superBolusInsulin: Decimal // Additional insulin for super bolus
     let targetDifference: Decimal // Difference from target BG

+ 124 - 10
TrioTests/BolusCalculatorTests/BolusCalculatorTests.swift

@@ -88,10 +88,10 @@ import Testing
         // wholeCalc = round(wholeCobInsulin + correctionInsulin + fifteenMinutesInsulin - iobInsulinReduction, 3) = 11.125U
         // insulinCalculated = round(wholeCalc × fraction, 3) = 8.9U
 
-        // Calculate expected values with proper rounding using roundBolus method from the apsManager
-        let wholeCobInsulin = apsManager.roundBolus(amount: Decimal(100) / Decimal(10)) // 10U
-        let targetDifferenceInsulin = apsManager.roundBolus(amount: Decimal(80) / Decimal(40)) // 2U
-        let fifteenMinutesInsulin = apsManager.roundBolus(amount: Decimal(5) / Decimal(40)) // 0.125U
+        // Calculate expected values
+        let wholeCobInsulin = Decimal(100) / Decimal(10) // 10U
+        let targetDifferenceInsulin = Decimal(80) / Decimal(40) // 2U
+        let fifteenMinutesInsulin = Decimal(5) / Decimal(40)
         let wholeCalc = wholeCobInsulin + targetDifferenceInsulin + fifteenMinutesInsulin - Decimal(1) // 11.125U
         let expectedInsulinCalculated = apsManager.roundBolus(amount: wholeCalc * fraction) // 8.9U
 
@@ -104,7 +104,6 @@ import Testing
             Components from CalculationResult:
             - insulinCalculated: \(result.insulinCalculated)U (expected: \(expectedInsulinCalculated)U)
             - wholeCalc: \(result.wholeCalc)U (expected: \(wholeCalc)U)
-            - correctionInsulin: \(result.correctionInsulin)U (expected: \(targetDifferenceInsulin)U)
             - iobInsulinReduction: \(result.iobInsulinReduction)U (expected: 1U)
             - superBolusInsulin: \(result.superBolusInsulin)U (expected: 0U)
             - targetDifference: \(result.targetDifference) mg/dL (expected: 80 mg/dL)
@@ -121,10 +120,6 @@ import Testing
             "Final calculated insulin amount should be \(expectedInsulinCalculated)U"
         )
         #expect(result.wholeCalc == wholeCalc, "Total calculation before fraction should be \(wholeCalc)U")
-        #expect(
-            result.correctionInsulin == targetDifferenceInsulin,
-            "Insulin for BG correction should be \(targetDifferenceInsulin)U"
-        )
         #expect(result.iobInsulinReduction == -1.0, "Absolute IOB reduction amount should be 1U, hence -1U")
         #expect(result.superBolusInsulin == 0, "Additional insulin for super bolus should be 0U")
         #expect(result.targetDifference == 80, "Difference from target BG should be 80 mg/dL")
@@ -491,7 +486,8 @@ import Testing
             useFattyMealCorrection: false,
             useSuperBolus: false,
             lastLoopDate: Date(),
-            minPredBG: nil
+            minPredBG: nil,
+            simulatedCOB: nil
         )
 
         // Then
@@ -664,6 +660,124 @@ import Testing
             fileStorage.save(originalISFValues, as: OpenAPS.Settings.insulinSensitivities)
         }
     }
+
+    @Test("Calculate insulin with backdated carbs") func testBackdatedCarbsCalculation() async throws {
+        // STEP 1: Setup test scenario
+        let currentDate = Date()
+        let backdatedCarbsDate = currentDate.addingTimeInterval(-60 * 60) // 1 hour ago
+
+        let currentBG: Decimal = 140
+        let target: Decimal = 100
+        let isf: Decimal = 40
+        let carbRatio: Decimal = 10
+        let iob: Decimal = 0.5
+        let cob: Int16 = 10 // Existing COB before adding backdated carbs
+        let carbs: Decimal = 30 // 30g of carbs, backdated 1 hour
+
+        // Get the COB value for the backdated carbs
+        // Use the actual APS Manager to calculate simulated COB for more realistic test
+        let determination = await apsManager.simulateDetermineBasal(
+            simulatedCarbsAmount: carbs,
+            simulatedBolusAmount: 0,
+            simulatedCarbsDate: backdatedCarbsDate
+        )
+
+        // Fallback to existing COB if determination is nil
+        let simulatedCOB = determination?.cob ?? Decimal(cob)
+
+        // For comparison - same scenario but with current time carbs
+        let currentTimeInput = CalculationInput(
+            carbs: carbs, // the newly entered carbs (30g)
+            currentBG: currentBG,
+            deltaBG: 0,
+            target: target,
+            isf: isf,
+            carbRatio: carbRatio,
+            iob: iob,
+            cob: cob, // the existing cob (10g)
+            useFattyMealCorrectionFactor: false,
+            fattyMealFactor: 0.8,
+            useSuperBolus: false,
+            sweetMealFactor: 1,
+            basal: 1.0,
+            fraction: 1.0,
+            maxBolus: 10,
+            maxIOB: 15,
+            maxCOB: 120,
+            minPredBG: 80,
+            lastLoopDate: currentDate
+        )
+
+        // Backdated scenario uses the same input but simulates date in the past
+        let backdatedInput = CalculationInput(
+            carbs: 0, // as the carbs are backdated we need to set the (newly entered) carbs to 0
+            currentBG: currentBG,
+            deltaBG: 0,
+            target: target,
+            isf: isf,
+            carbRatio: carbRatio,
+            iob: iob,
+            cob: Int16(truncating: NSDecimalNumber(decimal: simulatedCOB)), // current COB we got from the simulated Determination
+            useFattyMealCorrectionFactor: false,
+            fattyMealFactor: 0.8,
+            useSuperBolus: false,
+            sweetMealFactor: 1,
+            basal: 1.0,
+            fraction: 1.0,
+            maxBolus: 10,
+            maxIOB: 15,
+            maxCOB: 120,
+            minPredBG: 80,
+            lastLoopDate: currentDate
+        )
+
+        // STEP 2: Calculate insulin for both scenarios
+        let currentTimeResult = await calculator.calculateInsulin(input: currentTimeInput)
+        let backdatedResult = await calculator.calculateInsulin(input: backdatedInput)
+
+        // STEP 3: Verify results
+
+        // In the current time scenario, we expect COB to be old COB + current carbs
+        let expectedCurrentTimeCOB = Decimal(cob) + carbs
+        #expect(
+            currentTimeResult.wholeCob == expectedCurrentTimeCOB,
+            "Current time scenario should have \(expectedCurrentTimeCOB)g COB (\(cob)g existing + \(carbs)g new)"
+        )
+
+        // For backdated scenario, COB should be less than the current time scenario
+        // because some carbs have already been absorbed
+        #expect(
+            backdatedResult.wholeCob < currentTimeResult.wholeCob,
+            """
+            Backdated scenario should have less COB than current time scenario
+            Backdated: \(backdatedResult.wholeCob)g
+            Current time: \(currentTimeResult.wholeCob)g
+            Difference: \(currentTimeResult.wholeCob - backdatedResult.wholeCob)g
+            """
+        )
+
+        // The wholeCobInsulin should reflect the difference in COB
+        #expect(
+            backdatedResult.wholeCobInsulin < currentTimeResult.wholeCobInsulin,
+            """
+            Backdated scenario should require less insulin for carbs due to partial absorption
+            Backdated insulin: \(backdatedResult.wholeCobInsulin)U
+            Current time insulin: \(currentTimeResult.wholeCobInsulin)U
+            Difference: \(currentTimeResult.wholeCobInsulin - backdatedResult.wholeCobInsulin)U
+            """
+        )
+
+        // The backdated scenario should recommend less insulin than the current time scenario
+        #expect(
+            backdatedResult.insulinCalculated < currentTimeResult.insulinCalculated,
+            """
+            Backdated carbs should result in lower insulin recommendation
+            Current time: \(currentTimeResult.insulinCalculated)U
+            Backdated: \(backdatedResult.insulinCalculated)U
+            Difference: \(currentTimeResult.insulinCalculated - backdatedResult.insulinCalculated)U
+            """
+        )
+    }
 }
 
 // Copied over from BolusCalculationManager as they are not included in the protocol definition (and I don´t want them to be included)