TemporaryScheduleOverrideHistoryTests.swift 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. //
  2. // TemporaryScheduleOverrideHistoryTests.swift
  3. // LoopKitTests
  4. //
  5. // Created by Michael Pangburn on 3/25/19.
  6. // Copyright © 2019 LoopKit Authors. All rights reserved.
  7. //
  8. import XCTest
  9. @testable import LoopKit
  10. final class TemporaryScheduleOverrideHistoryTests: XCTestCase {
  11. // Midnight of an arbitrary date
  12. let referenceDate = Calendar.current.startOfDay(for: Date(timeIntervalSinceReferenceDate: .hours(100_000)))
  13. let basalRateSchedule = BasalRateSchedule(
  14. dailyItems: [
  15. RepeatingScheduleValue(startTime: .hours(0), value: 1.2),
  16. RepeatingScheduleValue(startTime: .hours(6), value: 1.4),
  17. RepeatingScheduleValue(startTime: .hours(20), value: 1.0)
  18. ],
  19. timeZone: Calendar.current.timeZone
  20. )!
  21. let history = TemporaryScheduleOverrideHistory()
  22. private func recordOverride(
  23. beginningAt offset: TimeInterval,
  24. duration: TemporaryScheduleOverride.Duration,
  25. insulinNeedsScaleFactor scaleFactor: Double,
  26. recordedAt enableDateOffset: TimeInterval? = nil
  27. ) {
  28. let settings = TemporaryScheduleOverrideSettings(unit: .milligramsPerDeciliter, targetRange: nil, insulinNeedsScaleFactor: scaleFactor)
  29. let override = TemporaryScheduleOverride(context: .custom, settings: settings, startDate: referenceDate + offset, duration: duration, enactTrigger: .local, syncIdentifier: UUID())
  30. let enableDate: Date
  31. if let enableDateOffset = enableDateOffset {
  32. enableDate = referenceDate + enableDateOffset
  33. } else {
  34. enableDate = override.startDate
  35. }
  36. history.recordOverride(override, at: enableDate)
  37. }
  38. private func recordOverrideDisable(at offset: TimeInterval) {
  39. history.recordOverride(nil, at: referenceDate + offset)
  40. }
  41. private func historyResolves(to expected: BasalRateSchedule, referenceDateOffset: TimeInterval = 0) -> Bool {
  42. let referenceDate = self.referenceDate + referenceDateOffset
  43. let actual = history.resolvingRecentBasalSchedule(basalRateSchedule, relativeTo: referenceDate)
  44. return actual.equals(expected, accuracy: 1e-6)
  45. }
  46. override func setUp() {
  47. history.wipeHistory()
  48. }
  49. func testEmptyHistory() {
  50. XCTAssert(historyResolves(to: basalRateSchedule))
  51. }
  52. func testSingleOverrideNaturalEnd() {
  53. recordOverride(beginningAt: .hours(2), duration: .finite(.hours(3)), insulinNeedsScaleFactor: 1.5)
  54. let expected = BasalRateSchedule(dailyItems: [
  55. RepeatingScheduleValue(startTime: .hours(0), value: 1.2),
  56. RepeatingScheduleValue(startTime: .hours(2), value: 1.8),
  57. RepeatingScheduleValue(startTime: .hours(5), value: 1.2),
  58. RepeatingScheduleValue(startTime: .hours(6), value: 1.4),
  59. RepeatingScheduleValue(startTime: .hours(20), value: 1.0)
  60. ])!
  61. XCTAssert(historyResolves(to: expected, referenceDateOffset: .hours(3)))
  62. }
  63. func testSingleOverrideEarlyEnd() {
  64. recordOverride(beginningAt: .hours(2), duration: .finite(.hours(3)), insulinNeedsScaleFactor: 1.5)
  65. recordOverrideDisable(at: .hours(3))
  66. let expected = BasalRateSchedule(dailyItems: [
  67. RepeatingScheduleValue(startTime: .hours(0), value: 1.2),
  68. RepeatingScheduleValue(startTime: .hours(2), value: 1.8),
  69. RepeatingScheduleValue(startTime: .hours(3), value: 1.2),
  70. RepeatingScheduleValue(startTime: .hours(6), value: 1.4),
  71. RepeatingScheduleValue(startTime: .hours(20), value: 1.0)
  72. ])!
  73. XCTAssert(historyResolves(to: expected, referenceDateOffset: .hours(3)))
  74. }
  75. func testSingleIndefiniteOverrideEarlyEnd() {
  76. recordOverride(beginningAt: .hours(2), duration: .indefinite, insulinNeedsScaleFactor: 1.5)
  77. recordOverrideDisable(at: .hours(3))
  78. let expected = BasalRateSchedule(dailyItems: [
  79. RepeatingScheduleValue(startTime: .hours(0), value: 1.2),
  80. RepeatingScheduleValue(startTime: .hours(2), value: 1.8),
  81. RepeatingScheduleValue(startTime: .hours(3), value: 1.2),
  82. RepeatingScheduleValue(startTime: .hours(6), value: 1.4),
  83. RepeatingScheduleValue(startTime: .hours(20), value: 1.0)
  84. ])!
  85. XCTAssert(historyResolves(to: expected, referenceDateOffset: .hours(3)))
  86. }
  87. func testTwoOverrides() {
  88. recordOverride(beginningAt: .hours(2), duration: .finite(.hours(3)), insulinNeedsScaleFactor: 1.5)
  89. recordOverride(beginningAt: .hours(6), duration: .finite(.hours(4)), insulinNeedsScaleFactor: 2.0)
  90. let expected = BasalRateSchedule(dailyItems: [
  91. RepeatingScheduleValue(startTime: .hours(0), value: 1.2),
  92. RepeatingScheduleValue(startTime: .hours(2), value: 1.8),
  93. RepeatingScheduleValue(startTime: .hours(5), value: 1.2),
  94. RepeatingScheduleValue(startTime: .hours(6), value: 2.8),
  95. RepeatingScheduleValue(startTime: .hours(10), value: 1.4),
  96. RepeatingScheduleValue(startTime: .hours(20), value: 1.0)
  97. ])!
  98. XCTAssert(historyResolves(to: expected, referenceDateOffset: .hours(10)))
  99. }
  100. func testThreeOverrides() {
  101. recordOverride(beginningAt: .hours(5), duration: .finite(.hours(3)), insulinNeedsScaleFactor: 1.5)
  102. recordOverrideDisable(at: .hours(6))
  103. recordOverride(beginningAt: .hours(10), duration: .finite(.hours(1)), insulinNeedsScaleFactor: 2.0)
  104. recordOverride(beginningAt: .hours(12), duration: .finite(.hours(2)), insulinNeedsScaleFactor: 1.5)
  105. recordOverrideDisable(at: .hours(13))
  106. let expected = BasalRateSchedule(dailyItems: [
  107. RepeatingScheduleValue(startTime: .hours(0), value: 1.2),
  108. RepeatingScheduleValue(startTime: .hours(5), value: 1.8),
  109. RepeatingScheduleValue(startTime: .hours(6), value: 1.4),
  110. RepeatingScheduleValue(startTime: .hours(10), value: 2.8),
  111. RepeatingScheduleValue(startTime: .hours(11), value: 1.4),
  112. RepeatingScheduleValue(startTime: .hours(12), value: 2.1),
  113. RepeatingScheduleValue(startTime: .hours(13), value: 1.4),
  114. RepeatingScheduleValue(startTime: .hours(20), value: 1.0)
  115. ])!
  116. XCTAssert(historyResolves(to: expected, referenceDateOffset: .hours(13)))
  117. }
  118. func testOldOverrideRemoval() {
  119. recordOverride(beginningAt: .hours(-1000), duration: .finite(.hours(1)), insulinNeedsScaleFactor: 2.0)
  120. recordOverride(beginningAt: .hours(2), duration: .finite(.hours(3)), insulinNeedsScaleFactor: 1.5)
  121. let expected = BasalRateSchedule(dailyItems: [
  122. RepeatingScheduleValue(startTime: .hours(0), value: 1.2),
  123. RepeatingScheduleValue(startTime: .hours(2), value: 1.8),
  124. RepeatingScheduleValue(startTime: .hours(5), value: 1.2),
  125. RepeatingScheduleValue(startTime: .hours(6), value: 1.4),
  126. RepeatingScheduleValue(startTime: .hours(20), value: 1.0)
  127. ])!
  128. XCTAssert(historyResolves(to: expected))
  129. }
  130. func testActiveIndefiniteOverride() {
  131. recordOverride(beginningAt: .hours(2), duration: .finite(.hours(3)), insulinNeedsScaleFactor: 1.5)
  132. recordOverride(beginningAt: .hours(6), duration: .indefinite, insulinNeedsScaleFactor: 2.0)
  133. let expected = BasalRateSchedule(dailyItems: [
  134. RepeatingScheduleValue(startTime: .hours(0), value: 1.2),
  135. RepeatingScheduleValue(startTime: .hours(2), value: 1.8),
  136. RepeatingScheduleValue(startTime: .hours(5), value: 1.2),
  137. RepeatingScheduleValue(startTime: .hours(6), value: 2.8),
  138. RepeatingScheduleValue(startTime: .hours(10), value: 1.4),
  139. RepeatingScheduleValue(startTime: .hours(20), value: 1.0)
  140. ])!
  141. XCTAssert(historyResolves(to: expected))
  142. }
  143. func testEditActiveOverride() {
  144. recordOverride(beginningAt: .hours(1), duration: .finite(.hours(1)), insulinNeedsScaleFactor: 1.5, recordedAt: .hours(0))
  145. recordOverride(beginningAt: .hours(1), duration: .finite(.hours(1)), insulinNeedsScaleFactor: 2.0, recordedAt: .hours(0.5))
  146. let expected = BasalRateSchedule(dailyItems: [
  147. RepeatingScheduleValue(startTime: .hours(0), value: 1.2),
  148. RepeatingScheduleValue(startTime: .hours(1), value: 2.4),
  149. RepeatingScheduleValue(startTime: .hours(2), value: 1.2),
  150. RepeatingScheduleValue(startTime: .hours(6), value: 1.4),
  151. RepeatingScheduleValue(startTime: .hours(20), value: 1.0)
  152. ])!
  153. XCTAssert(historyResolves(to: expected))
  154. }
  155. func testRemoveFutureOverride() {
  156. recordOverride(beginningAt: .hours(2), duration: .finite(.hours(3)), insulinNeedsScaleFactor: 1.5, recordedAt: .hours(1))
  157. recordOverride(beginningAt: .hours(1), duration: .finite(.hours(1)), insulinNeedsScaleFactor: 2.0)
  158. let expected = BasalRateSchedule(dailyItems: [
  159. RepeatingScheduleValue(startTime: .hours(0), value: 1.2),
  160. RepeatingScheduleValue(startTime: .hours(1), value: 2.4),
  161. RepeatingScheduleValue(startTime: .hours(2), value: 1.2),
  162. RepeatingScheduleValue(startTime: .hours(6), value: 1.4),
  163. RepeatingScheduleValue(startTime: .hours(20), value: 1.0)
  164. ])!
  165. XCTAssert(historyResolves(to: expected))
  166. }
  167. func testCancelFutureOverride() {
  168. recordOverride(beginningAt: .hours(2), duration: .finite(.hours(3)), insulinNeedsScaleFactor: 1.5, recordedAt: .hours(1))
  169. recordOverrideDisable(at: .hours(1.5))
  170. let expected = basalRateSchedule
  171. XCTAssert(historyResolves(to: expected))
  172. }
  173. func testMultiDayOverride() {
  174. recordOverride(beginningAt: .hours(2), duration: .finite(.hours(68)), insulinNeedsScaleFactor: 1.5)
  175. let expected = BasalRateSchedule(dailyItems: [
  176. RepeatingScheduleValue(startTime: .hours(0), value: 1.8),
  177. RepeatingScheduleValue(startTime: .hours(6), value: 2.1),
  178. RepeatingScheduleValue(startTime: .hours(12), value: 1.4),
  179. RepeatingScheduleValue(startTime: .hours(16), value: 2.1),
  180. RepeatingScheduleValue(startTime: .hours(20), value: 1.5)
  181. ])!
  182. XCTAssert(historyResolves(to: expected, referenceDateOffset: .hours(26)))
  183. }
  184. func testClampedPastOverride() {
  185. recordOverride(beginningAt: .hours(-4), duration: .finite(.hours(8)), insulinNeedsScaleFactor: 1.5)
  186. let expected = BasalRateSchedule(dailyItems: [
  187. RepeatingScheduleValue(startTime: .hours(0), value: 1.8),
  188. RepeatingScheduleValue(startTime: .hours(4), value: 1.2),
  189. RepeatingScheduleValue(startTime: .hours(6), value: 1.4),
  190. RepeatingScheduleValue(startTime: .hours(20), value: 1.5),
  191. ])!
  192. XCTAssert(historyResolves(to: expected, referenceDateOffset: .hours(6)))
  193. }
  194. func testCancelSequence() {
  195. recordOverride(beginningAt: .hours(2), duration: .finite(.hours(8)), insulinNeedsScaleFactor: 1.5)
  196. recordOverrideDisable(at: .hours(4))
  197. recordOverride(beginningAt: .hours(7), duration: .finite(.hours(1)), insulinNeedsScaleFactor: 1.5)
  198. let expected = BasalRateSchedule(dailyItems: [
  199. RepeatingScheduleValue(startTime: .hours(0), value: 1.2),
  200. RepeatingScheduleValue(startTime: .hours(2), value: 1.8),
  201. RepeatingScheduleValue(startTime: .hours(4), value: 1.2),
  202. RepeatingScheduleValue(startTime: .hours(6), value: 1.4),
  203. RepeatingScheduleValue(startTime: .hours(7), value: 2.1),
  204. RepeatingScheduleValue(startTime: .hours(8), value: 1.4),
  205. RepeatingScheduleValue(startTime: .hours(20), value: 1.0)
  206. ])!
  207. XCTAssert(historyResolves(to: expected, referenceDateOffset: .hours(6)))
  208. }
  209. func testQuery() {
  210. var (overrides, deletedOverrides, newAnchor) = history.queryByAnchor(nil)
  211. XCTAssertEqual(0, overrides.count)
  212. XCTAssertEqual(0, deletedOverrides.count)
  213. recordOverride(beginningAt: .hours(2), duration: .finite(.hours(8)), insulinNeedsScaleFactor: 1.5)
  214. recordOverrideDisable(at: .hours(4))
  215. recordOverride(beginningAt: .hours(7), duration: .finite(.hours(1)), insulinNeedsScaleFactor: 1.5)
  216. (overrides, deletedOverrides, newAnchor) = history.queryByAnchor(newAnchor)
  217. XCTAssertEqual(0, deletedOverrides.count)
  218. XCTAssertEqual(2, overrides.count)
  219. XCTAssertEqual(TimeInterval(hours: 2), overrides[0].duration.timeInterval, accuracy: 1)
  220. }
  221. func testQueryOfDeletedOverrides() {
  222. var (overrides, deletedOverrides, newAnchor) = history.queryByAnchor(nil)
  223. XCTAssertEqual(0, overrides.count)
  224. XCTAssertEqual(0, deletedOverrides.count)
  225. recordOverride(beginningAt: .hours(2), duration: .finite(.hours(8)), insulinNeedsScaleFactor: 1.5)
  226. recordOverrideDisable(at: .hours(1))
  227. (overrides, deletedOverrides, newAnchor) = history.queryByAnchor(newAnchor)
  228. XCTAssertEqual(0, overrides.count)
  229. XCTAssertEqual(1, deletedOverrides.count)
  230. }
  231. }