TherapySettingsTests.swift 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. //
  2. // TherapySettingsTests.swift
  3. // LoopKitTests
  4. //
  5. // Created by Anna Quinlan on 7/27/20.
  6. // Copyright © 2020 LoopKit Authors. All rights reserved.
  7. //
  8. import XCTest
  9. import LoopKit
  10. class TherapySettingsTests: XCTestCase {
  11. private let dateFormatter = ISO8601DateFormatter()
  12. let date = Date(timeIntervalSince1970: 100000)
  13. private let encoder: JSONEncoder = {
  14. let encoder = JSONEncoder()
  15. encoder.outputFormatting = [.prettyPrinted, .sortedKeys, .withoutEscapingSlashes]
  16. encoder.dateEncodingStrategy = .iso8601
  17. return encoder
  18. }()
  19. private let decoder: JSONDecoder = {
  20. let decoder = JSONDecoder()
  21. decoder.dateDecodingStrategy = .iso8601
  22. return decoder
  23. }()
  24. private func getTherapySettings() -> TherapySettings {
  25. let timeZone = TimeZone(identifier: "America/Los_Angeles")!
  26. let glucoseTargetRangeSchedule = GlucoseRangeSchedule(
  27. rangeSchedule: DailyQuantitySchedule(unit: .milligramsPerDeciliter,
  28. dailyItems: [RepeatingScheduleValue(startTime: .hours(0), value: DoubleRange(minValue: 100.0, maxValue: 110.0)),
  29. RepeatingScheduleValue(startTime: .hours(8), value: DoubleRange(minValue: 95.0, maxValue: 105.0)),
  30. RepeatingScheduleValue(startTime: .hours(14), value: DoubleRange(minValue: 95.0, maxValue: 105.0)),
  31. RepeatingScheduleValue(startTime: .hours(16), value: DoubleRange(minValue: 100.0, maxValue: 110.0)),
  32. RepeatingScheduleValue(startTime: .hours(18), value: DoubleRange(minValue: 90.0, maxValue: 100.0)),
  33. RepeatingScheduleValue(startTime: .hours(21), value: DoubleRange(minValue: 110.0, maxValue: 120.0))],
  34. timeZone: timeZone)!,
  35. override: GlucoseRangeSchedule.Override(value: DoubleRange(minValue: 80.0, maxValue: 90.0),
  36. start: date.addingTimeInterval(.minutes(-30)),
  37. end: date.addingTimeInterval(.minutes(30)))
  38. )
  39. let basalRateSchedule = BasalRateSchedule(
  40. dailyItems: [RepeatingScheduleValue(startTime: .hours(0), value: 1.0),
  41. RepeatingScheduleValue(startTime: .hours(8), value: 1.125),
  42. RepeatingScheduleValue(startTime: .hours(10), value: 1.25),
  43. RepeatingScheduleValue(startTime: .hours(12), value: 1.5),
  44. RepeatingScheduleValue(startTime: .hours(14), value: 1.25),
  45. RepeatingScheduleValue(startTime: .hours(16), value: 1.5),
  46. RepeatingScheduleValue(startTime: .hours(18), value: 1.25),
  47. RepeatingScheduleValue(startTime: .hours(21), value: 1.0)],
  48. timeZone: timeZone)!
  49. let insulinSensitivitySchedule = InsulinSensitivitySchedule(
  50. unit: .milligramsPerDeciliter,
  51. dailyItems: [RepeatingScheduleValue(startTime: .hours(0), value: 45.0),
  52. RepeatingScheduleValue(startTime: .hours(8), value: 40.0),
  53. RepeatingScheduleValue(startTime: .hours(10), value: 35.0),
  54. RepeatingScheduleValue(startTime: .hours(12), value: 30.0),
  55. RepeatingScheduleValue(startTime: .hours(14), value: 35.0),
  56. RepeatingScheduleValue(startTime: .hours(16), value: 40.0)],
  57. timeZone: timeZone)!
  58. let carbRatioSchedule = CarbRatioSchedule(
  59. unit: .gram(),
  60. dailyItems: [RepeatingScheduleValue(startTime: .hours(0), value: 10.0),
  61. RepeatingScheduleValue(startTime: .hours(8), value: 12.0),
  62. RepeatingScheduleValue(startTime: .hours(10), value: 9.0),
  63. RepeatingScheduleValue(startTime: .hours(12), value: 10.0),
  64. RepeatingScheduleValue(startTime: .hours(14), value: 11.0),
  65. RepeatingScheduleValue(startTime: .hours(16), value: 12.0),
  66. RepeatingScheduleValue(startTime: .hours(18), value: 8.0),
  67. RepeatingScheduleValue(startTime: .hours(21), value: 10.0)],
  68. timeZone: timeZone)!
  69. return TherapySettings(
  70. glucoseTargetRangeSchedule: glucoseTargetRangeSchedule,
  71. preMealTargetRange: DoubleRange(minValue: 80.0, maxValue: 90.0),
  72. workoutTargetRange: DoubleRange(minValue: 130.0, maxValue: 140.0),
  73. maximumBasalRatePerHour: 3,
  74. maximumBolus: 5,
  75. suspendThreshold: GlucoseThreshold(unit: .milligramsPerDeciliter, value: 80),
  76. insulinSensitivitySchedule: insulinSensitivitySchedule,
  77. carbRatioSchedule: carbRatioSchedule,
  78. basalRateSchedule: basalRateSchedule,
  79. insulinModelSettings: InsulinModelSettings(model: ExponentialInsulinModelPreset.rapidActingAdult)
  80. )
  81. }
  82. let encodedString = """
  83. {
  84. "basalRateSchedule" : {
  85. "items" : [
  86. {
  87. "startTime" : 0,
  88. "value" : 1
  89. },
  90. {
  91. "startTime" : 28800,
  92. "value" : 1.125
  93. },
  94. {
  95. "startTime" : 36000,
  96. "value" : 1.25
  97. },
  98. {
  99. "startTime" : 43200,
  100. "value" : 1.5
  101. },
  102. {
  103. "startTime" : 50400,
  104. "value" : 1.25
  105. },
  106. {
  107. "startTime" : 57600,
  108. "value" : 1.5
  109. },
  110. {
  111. "startTime" : 64800,
  112. "value" : 1.25
  113. },
  114. {
  115. "startTime" : 75600,
  116. "value" : 1
  117. }
  118. ],
  119. "referenceTimeInterval" : 0,
  120. "repeatInterval" : 86400,
  121. "timeZone" : {
  122. "identifier" : "America/Los_Angeles"
  123. }
  124. },
  125. "carbRatioSchedule" : {
  126. "unit" : "g",
  127. "valueSchedule" : {
  128. "items" : [
  129. {
  130. "startTime" : 0,
  131. "value" : 10
  132. },
  133. {
  134. "startTime" : 28800,
  135. "value" : 12
  136. },
  137. {
  138. "startTime" : 36000,
  139. "value" : 9
  140. },
  141. {
  142. "startTime" : 43200,
  143. "value" : 10
  144. },
  145. {
  146. "startTime" : 50400,
  147. "value" : 11
  148. },
  149. {
  150. "startTime" : 57600,
  151. "value" : 12
  152. },
  153. {
  154. "startTime" : 64800,
  155. "value" : 8
  156. },
  157. {
  158. "startTime" : 75600,
  159. "value" : 10
  160. }
  161. ],
  162. "referenceTimeInterval" : 0,
  163. "repeatInterval" : 86400,
  164. "timeZone" : {
  165. "identifier" : "America/Los_Angeles"
  166. }
  167. }
  168. },
  169. "glucoseTargetRangeSchedule" : {
  170. "override" : {
  171. "end" : "1970-01-02T04:16:40Z",
  172. "start" : "1970-01-02T03:16:40Z",
  173. "value" : {
  174. "maxValue" : 90,
  175. "minValue" : 80
  176. }
  177. },
  178. "rangeSchedule" : {
  179. "unit" : "mg/dL",
  180. "valueSchedule" : {
  181. "items" : [
  182. {
  183. "startTime" : 0,
  184. "value" : {
  185. "maxValue" : 110,
  186. "minValue" : 100
  187. }
  188. },
  189. {
  190. "startTime" : 28800,
  191. "value" : {
  192. "maxValue" : 105,
  193. "minValue" : 95
  194. }
  195. },
  196. {
  197. "startTime" : 50400,
  198. "value" : {
  199. "maxValue" : 105,
  200. "minValue" : 95
  201. }
  202. },
  203. {
  204. "startTime" : 57600,
  205. "value" : {
  206. "maxValue" : 110,
  207. "minValue" : 100
  208. }
  209. },
  210. {
  211. "startTime" : 64800,
  212. "value" : {
  213. "maxValue" : 100,
  214. "minValue" : 90
  215. }
  216. },
  217. {
  218. "startTime" : 75600,
  219. "value" : {
  220. "maxValue" : 120,
  221. "minValue" : 110
  222. }
  223. }
  224. ],
  225. "referenceTimeInterval" : 0,
  226. "repeatInterval" : 86400,
  227. "timeZone" : {
  228. "identifier" : "America/Los_Angeles"
  229. }
  230. }
  231. }
  232. },
  233. "insulinModelSettings" : {
  234. "exponential" : "rapidActingAdult"
  235. },
  236. "insulinSensitivitySchedule" : {
  237. "unit" : "mg/dL",
  238. "valueSchedule" : {
  239. "items" : [
  240. {
  241. "startTime" : 0,
  242. "value" : 45
  243. },
  244. {
  245. "startTime" : 28800,
  246. "value" : 40
  247. },
  248. {
  249. "startTime" : 36000,
  250. "value" : 35
  251. },
  252. {
  253. "startTime" : 43200,
  254. "value" : 30
  255. },
  256. {
  257. "startTime" : 50400,
  258. "value" : 35
  259. },
  260. {
  261. "startTime" : 57600,
  262. "value" : 40
  263. }
  264. ],
  265. "referenceTimeInterval" : 0,
  266. "repeatInterval" : 86400,
  267. "timeZone" : {
  268. "identifier" : "America/Los_Angeles"
  269. }
  270. }
  271. },
  272. "maximumBasalRatePerHour" : 3,
  273. "maximumBolus" : 5,
  274. "preMealTargetRange" : {
  275. "maxValue" : 90,
  276. "minValue" : 80
  277. },
  278. "suspendThreshold" : {
  279. "unit" : "mg/dL",
  280. "value" : 80
  281. },
  282. "workoutTargetRange" : {
  283. "maxValue" : 140,
  284. "minValue" : 130
  285. }
  286. }
  287. """
  288. func testInsulinModelEncoding() throws {
  289. let adult = InsulinModelSettings.exponentialPreset(.rapidActingAdult)
  290. let child = InsulinModelSettings.exponentialPreset(.rapidActingChild)
  291. let walsh = InsulinModelSettings.walsh(WalshInsulinModel(actionDuration: 10))
  292. XCTAssertEqual("""
  293. {
  294. "exponential" : "rapidActingAdult"
  295. }
  296. """, String(data: try encoder.encode(adult), encoding: .utf8)!)
  297. XCTAssertEqual("""
  298. {
  299. "exponential" : "rapidActingChild"
  300. }
  301. """, String(data: try encoder.encode(child), encoding: .utf8)!)
  302. XCTAssertEqual("""
  303. {
  304. "walsh" : {
  305. "actionDuration" : 10,
  306. "delay" : 600
  307. }
  308. }
  309. """, String(data: try encoder.encode(walsh), encoding: .utf8)!)
  310. }
  311. func testTherapySettingEncoding() throws {
  312. let original = getTherapySettings()
  313. let data = try encoder.encode(original)
  314. XCTAssertEqual(encodedString, String(data: data, encoding: .utf8)!)
  315. }
  316. func testTherapySettingDecoding() throws {
  317. let data = encodedString.data(using: .utf8)!
  318. let decoded = try decoder.decode(TherapySettings.self, from: data)
  319. let expected = getTherapySettings()
  320. XCTAssertEqual(expected, decoded)
  321. XCTAssertEqual(decoded.basalRateSchedule, expected.basalRateSchedule)
  322. XCTAssertEqual(decoded.glucoseUnit, expected.glucoseUnit)
  323. XCTAssertEqual(decoded.insulinSensitivitySchedule, expected.insulinSensitivitySchedule)
  324. XCTAssertEqual(decoded.preMealTargetRange, expected.preMealTargetRange)
  325. XCTAssertEqual(decoded.workoutTargetRange, expected.workoutTargetRange)
  326. XCTAssertEqual(decoded.maximumBolus, expected.maximumBolus)
  327. XCTAssertEqual(decoded.maximumBasalRatePerHour, expected.maximumBasalRatePerHour)
  328. XCTAssertEqual(decoded.suspendThreshold, expected.suspendThreshold)
  329. XCTAssertEqual(decoded.carbRatioSchedule, expected.carbRatioSchedule)
  330. XCTAssertEqual(decoded.insulinModelSettings, expected.insulinModelSettings)
  331. XCTAssertEqual(decoded.glucoseTargetRangeSchedule, expected.glucoseTargetRangeSchedule)
  332. }
  333. }