TempBasalTests.swift 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. //
  2. // TempBasalTests.swift
  3. // OmniKitTests
  4. //
  5. // Created by Pete Schwamb on 6/5/18.
  6. // Copyright © 2018 Pete Schwamb. All rights reserved.
  7. //
  8. import Foundation
  9. import XCTest
  10. @testable import OmniKit
  11. class TempBasalTests: XCTestCase {
  12. func testRateQuantization() {
  13. // // Test previously failing case
  14. // XCTAssertEqual(0.15, OmnipodPumpManager.roundToDeliveryIncrement(units: 0.15))
  15. //
  16. // XCTAssertEqual(0.15, OmnipodPumpManager.roundToDeliveryIncrement(units: 0.15000000000000002))
  17. //
  18. // XCTAssertEqual(0.15, OmnipodPumpManager.roundToDeliveryIncrement(units: 0.145))
  19. }
  20. func testAlternatingSegmentFlag() {
  21. // Encode 0.05U/hr 30mins
  22. let cmd = SetInsulinScheduleCommand(nonce: 0x9746c65b, tempBasalRate: 0.05, duration: .hours(0.5))
  23. // 1a 0e 9746c65b 01 0079 01 3840 0000 0000
  24. XCTAssertEqual("1a0e9746c65b01007901384000000000", cmd.data.hexadecimalString)
  25. // Encode 0.05U/hr 8.5hours
  26. let cmd2 = SetInsulinScheduleCommand(nonce: 0x9746c65b, tempBasalRate: 0.05, duration: .hours(8.5))
  27. // 1a 10 9746c65b 01 0091 11 3840 0000 f800 0000
  28. XCTAssertEqual("1a109746c65b0100911138400000f8000000", cmd2.data.hexadecimalString)
  29. // Encode 0.05U/hr 16.5hours
  30. let cmd3 = SetInsulinScheduleCommand(nonce: 0x9746c65b, tempBasalRate: 0.05, duration: .hours(16.5))
  31. // 1a 12 9746c65b 01 00a9 21 3840 0000 f800 f800 0000
  32. XCTAssertEqual("1a129746c65b0100a92138400000f800f8000000", cmd3.data.hexadecimalString)
  33. }
  34. func testTempBasalThreeTenthsUnitPerHour() {
  35. let cmd = SetInsulinScheduleCommand(nonce: 0xeac79411, tempBasalRate: 0.3, duration: .hours(0.5))
  36. XCTAssertEqual("1a0eeac7941101007f01384000030003", cmd.data.hexadecimalString)
  37. }
  38. func testSetTempBasalCommand() {
  39. do {
  40. // Decode 1a 0e ea2d0a3b 01 007d 01 3840 0002 0002
  41. // 1a 0e 9746c65b 01 0079 01 3840 0000 0000 160e7c00000515752a
  42. let cmd = try SetInsulinScheduleCommand(encodedData: Data(hexadecimalString: "1a0eea2d0a3b01007d01384000020002")!)
  43. XCTAssertEqual(0xea2d0a3b, cmd.nonce)
  44. if case SetInsulinScheduleCommand.DeliverySchedule.tempBasal(let secondsRemaining, let firstSegmentPulses, let table) = cmd.deliverySchedule {
  45. XCTAssertEqual(1800, secondsRemaining)
  46. XCTAssertEqual(2, firstSegmentPulses)
  47. let entry = table.entries[0]
  48. XCTAssertEqual(1, entry.segments)
  49. XCTAssertEqual(2, entry.pulses)
  50. } else {
  51. XCTFail("Expected ScheduleEntry.tempBasal type")
  52. }
  53. } catch (let error) {
  54. XCTFail("message decoding threw error: \(error)")
  55. }
  56. // Encode
  57. let cmd = SetInsulinScheduleCommand(nonce: 0xea2d0a3b, tempBasalRate: 0.20, duration: .hours(0.5))
  58. XCTAssertEqual("1a0eea2d0a3b01007d01384000020002", cmd.data.hexadecimalString)
  59. }
  60. func testSetTempBasalWithAlternatingPulse() {
  61. do {
  62. // 0.05U/hr for 2.5 hours
  63. // Decode 1a 0e 4e2c2717 01 007f 05 3840 0000 4800
  64. let cmd = try SetInsulinScheduleCommand(encodedData: Data(hexadecimalString: "1a0e4e2c271701007f05384000004800")!)
  65. XCTAssertEqual(0x4e2c2717, cmd.nonce)
  66. if case SetInsulinScheduleCommand.DeliverySchedule.tempBasal(let secondsRemaining, let firstSegmentPulses, let table) = cmd.deliverySchedule {
  67. XCTAssertEqual(1800, secondsRemaining)
  68. XCTAssertEqual(0, firstSegmentPulses)
  69. XCTAssertEqual(1, table.entries.count)
  70. XCTAssertEqual(5, table.entries[0].segments)
  71. XCTAssertEqual(0, table.entries[0].pulses)
  72. XCTAssertEqual(true, table.entries[0].alternateSegmentPulse)
  73. } else {
  74. XCTFail("Expected ScheduleEntry.tempBasal type")
  75. }
  76. } catch (let error) {
  77. XCTFail("message decoding threw error: \(error)")
  78. }
  79. // Encode
  80. let cmd = SetInsulinScheduleCommand(nonce: 0x4e2c2717, tempBasalRate: 0.05, duration: .hours(2.5))
  81. XCTAssertEqual("1a0e4e2c271701007f05384000004800", cmd.data.hexadecimalString)
  82. }
  83. func testLargerTempBasalCommand() {
  84. do {
  85. // 2.00 U/h for 1.5h
  86. // Decode 1a 0e 87e8d03a 01 00cb 03 3840 0014 2014
  87. let cmd = try SetInsulinScheduleCommand(encodedData: Data(hexadecimalString: "1a0e87e8d03a0100cb03384000142014")!)
  88. XCTAssertEqual(0x87e8d03a, cmd.nonce)
  89. if case SetInsulinScheduleCommand.DeliverySchedule.tempBasal(let secondsRemaining, let firstSegmentPulses, let table) = cmd.deliverySchedule {
  90. XCTAssertEqual(1800, secondsRemaining)
  91. XCTAssertEqual(0x14, firstSegmentPulses)
  92. let entry = table.entries[0]
  93. XCTAssertEqual(3, entry.segments)
  94. XCTAssertEqual(20, entry.pulses)
  95. } else {
  96. XCTFail("Expected ScheduleEntry.tempBasal type")
  97. }
  98. } catch (let error) {
  99. XCTFail("message decoding threw error: \(error)")
  100. }
  101. // Encode
  102. let cmd = SetInsulinScheduleCommand(nonce: 0x87e8d03a, tempBasalRate: 2, duration: .hours(1.5))
  103. XCTAssertEqual("1a0e87e8d03a0100cb03384000142014", cmd.data.hexadecimalString)
  104. }
  105. func testCancelTempBasalCommand() {
  106. do {
  107. // Decode 1f 05 f76d34c4 62
  108. let cmd = try CancelDeliveryCommand(encodedData: Data(hexadecimalString: "1f05f76d34c462")!)
  109. XCTAssertEqual(0xf76d34c4, cmd.nonce)
  110. XCTAssertEqual(.beeeeeep, cmd.beepType)
  111. XCTAssertEqual(.tempBasal, cmd.deliveryType)
  112. } catch (let error) {
  113. XCTFail("message decoding threw error: \(error)")
  114. }
  115. // Encode
  116. let cmd = CancelDeliveryCommand(nonce: 0xf76d34c4, deliveryType: .tempBasal, beepType: .beeeeeep)
  117. XCTAssertEqual("1f05f76d34c462", cmd.data.hexadecimalString)
  118. }
  119. func testCancelTempBasalnoBeepCommand() {
  120. do {
  121. let cmd = try CancelDeliveryCommand(encodedData: Data(hexadecimalString: "1f05f76d34c402")!)
  122. XCTAssertEqual(0xf76d34c4, cmd.nonce)
  123. XCTAssertEqual(.noBeep, cmd.beepType)
  124. XCTAssertEqual(.tempBasal, cmd.deliveryType)
  125. } catch (let error) {
  126. XCTFail("message decoding threw error: \(error)")
  127. }
  128. // Encode
  129. let cmd = CancelDeliveryCommand(nonce: 0xf76d34c4, deliveryType: .tempBasal, beepType: .noBeep)
  130. XCTAssertEqual("1f05f76d34c402", cmd.data.hexadecimalString)
  131. }
  132. func testZeroTempExtraCommand() {
  133. do {
  134. // 0 U/h for 0.5 hours
  135. // 16 LL RR MM NNNN XXXXXXXX YYYY ZZZZZZZZ
  136. // Decode 16 0e 7c 00 0000 6b49d200 0000 6b49d200
  137. let cmd = try TempBasalExtraCommand(encodedData: Data(hexadecimalString: "160e7c0000006b49d20000006b49d200")!)
  138. XCTAssertEqual(false, cmd.acknowledgementBeep)
  139. XCTAssertEqual(true, cmd.completionBeep)
  140. XCTAssertEqual(.minutes(60), cmd.programReminderInterval)
  141. XCTAssertEqual(TimeInterval(seconds: 1800), cmd.delayUntilFirstTenthOfPulse)
  142. XCTAssertEqual(0, cmd.remainingPulses)
  143. XCTAssertEqual(1, cmd.rateEntries.count)
  144. let entry = cmd.rateEntries[0]
  145. XCTAssertEqual(TimeInterval(seconds: 1800), entry.delayBetweenPulses)
  146. XCTAssertEqual(TimeInterval(minutes: 30), entry.duration)
  147. XCTAssertEqual(0, entry.rate)
  148. } catch (let error) {
  149. XCTFail("message decoding threw error: \(error)")
  150. }
  151. // Encode
  152. let cmd = TempBasalExtraCommand(rate: 0, duration: .hours(0.5), acknowledgementBeep: false, completionBeep: true, programReminderInterval: .minutes(60))
  153. XCTAssertEqual("160e7c0000006b49d20000006b49d200", cmd.data.hexadecimalString)
  154. }
  155. func testZeroTempThreeHoursExtraCommand() {
  156. do {
  157. // 0 U/h for 3 hours
  158. let cmd = try TempBasalExtraCommand(encodedData: Data(hexadecimalString: "162c7c0000006b49d20000006b49d20000006b49d20000006b49d20000006b49d20000006b49d20000006b49d200")!)
  159. XCTAssertEqual(false, cmd.acknowledgementBeep)
  160. XCTAssertEqual(true, cmd.completionBeep)
  161. XCTAssertEqual(.minutes(60), cmd.programReminderInterval)
  162. XCTAssertEqual(TimeInterval(seconds: 1800), cmd.delayUntilFirstTenthOfPulse)
  163. XCTAssertEqual(0, cmd.remainingPulses)
  164. XCTAssertEqual(6, cmd.rateEntries.count)
  165. let entry = cmd.rateEntries[0]
  166. XCTAssertEqual(TimeInterval(seconds: 1800), entry.delayBetweenPulses)
  167. XCTAssertEqual(TimeInterval(minutes: 30), entry.duration)
  168. XCTAssertEqual(0, entry.rate)
  169. } catch (let error) {
  170. XCTFail("message decoding threw error: \(error)")
  171. }
  172. // Encode
  173. let cmd = TempBasalExtraCommand(rate: 0, duration: .hours(3), acknowledgementBeep: false, completionBeep: true, programReminderInterval: .minutes(60))
  174. XCTAssertEqual("162c7c0000006b49d20000006b49d20000006b49d20000006b49d20000006b49d20000006b49d20000006b49d200", cmd.data.hexadecimalString)
  175. }
  176. func testTempBasalExtremeValues() {
  177. do {
  178. // 30 U/h for 12 hours
  179. // Decode 1a 10 a958c5ad 01 04f5 18 3840 012c f12c 712c
  180. let cmd = try SetInsulinScheduleCommand(encodedData: Data(hexadecimalString: "1a10a958c5ad0104f5183840012cf12c712c")!)
  181. XCTAssertEqual(0xa958c5ad, cmd.nonce)
  182. if case SetInsulinScheduleCommand.DeliverySchedule.tempBasal(let secondsRemaining, let firstSegmentPulses, let table) = cmd.deliverySchedule {
  183. XCTAssertEqual(1800, secondsRemaining)
  184. XCTAssertEqual(300, firstSegmentPulses)
  185. XCTAssertEqual(2, table.entries.count)
  186. let entry1 = table.entries[0]
  187. XCTAssertEqual(16, entry1.segments)
  188. XCTAssertEqual(300, entry1.pulses)
  189. let entry2 = table.entries[1]
  190. XCTAssertEqual(8, entry2.segments)
  191. XCTAssertEqual(300, entry2.pulses)
  192. } else {
  193. XCTFail("Expected ScheduleEntry.tempBasal type")
  194. }
  195. } catch (let error) {
  196. XCTFail("message decoding threw error: \(error)")
  197. }
  198. // Encode
  199. let cmd = SetInsulinScheduleCommand(nonce: 0xa958c5ad, tempBasalRate: 30, duration: .hours(12))
  200. XCTAssertEqual("1a10a958c5ad0104f5183840012cf12c712c", cmd.data.hexadecimalString)
  201. }
  202. func testTempBasalExtraCommand() {
  203. do {
  204. // 30 U/h for 0.5 hours
  205. // Decode 16 0e 7c 00 0bb8 000927c0 0bb8 000927c0
  206. let cmd = try TempBasalExtraCommand(encodedData: Data(hexadecimalString: "160e7c000bb8000927c00bb8000927c0")!)
  207. XCTAssertEqual(false, cmd.acknowledgementBeep)
  208. XCTAssertEqual(true, cmd.completionBeep)
  209. XCTAssertEqual(.minutes(60), cmd.programReminderInterval)
  210. XCTAssertEqual(TimeInterval(seconds: 6), cmd.delayUntilFirstTenthOfPulse)
  211. XCTAssertEqual(300, cmd.remainingPulses)
  212. XCTAssertEqual(1, cmd.rateEntries.count)
  213. let entry = cmd.rateEntries[0]
  214. XCTAssertEqual(TimeInterval(seconds: 6), entry.delayBetweenPulses)
  215. XCTAssertEqual(TimeInterval(minutes: 30), entry.duration)
  216. XCTAssertEqual(30, entry.rate)
  217. } catch (let error) {
  218. XCTFail("message decoding threw error: \(error)")
  219. }
  220. // Encode
  221. let cmd = TempBasalExtraCommand(rate: 30, duration: .hours(0.5), acknowledgementBeep: false, completionBeep: true, programReminderInterval: .minutes(60))
  222. XCTAssertEqual("160e7c000bb8000927c00bb8000927c0", cmd.data.hexadecimalString)
  223. }
  224. func testBasalExtraCommandForOddPulseCountRate() {
  225. let cmd1 = TempBasalExtraCommand(rate: 0.05, duration: .hours(0.5), acknowledgementBeep: false, completionBeep: true, programReminderInterval: .minutes(60))
  226. XCTAssertEqual("160e7c00000515752a00000515752a00", cmd1.data.hexadecimalString)
  227. let cmd2 = TempBasalExtraCommand(rate: 2.05, duration: .hours(0.5), acknowledgementBeep: false, completionBeep: false, programReminderInterval: .minutes(60))
  228. XCTAssertEqual("160e3c0000cd0085fac700cd0085fac7", cmd2.data.hexadecimalString)
  229. let cmd3 = TempBasalExtraCommand(rate: 2.10, duration: .hours(0.5), acknowledgementBeep: false, completionBeep: false, programReminderInterval: .minutes(60))
  230. XCTAssertEqual("160e3c0000d20082ca2400d20082ca24", cmd3.data.hexadecimalString)
  231. let cmd4 = TempBasalExtraCommand(rate: 2.15, duration: .hours(0.5), acknowledgementBeep: false, completionBeep: false, programReminderInterval: .minutes(60))
  232. XCTAssertEqual("160e3c0000d7007fbf7d00d7007fbf7d", cmd4.data.hexadecimalString)
  233. }
  234. func testBasalExtraCommandPulseCount() {
  235. // 16 LL RR MM NNNN XXXXXXXX YYYY ZZZZZZZZ YYYY ZZZZZZZZ
  236. // 16 14 00 00 f5b9 000a0ad7 f5b9 000a0ad7 0aaf 000a0ad7
  237. // 16 14 00 00 f618 000a0ad7 f618 000a0ad7 0a50 000a0ad7
  238. let cmd2 = TempBasalExtraCommand(rate: 27.35, duration: .hours(12), acknowledgementBeep: false, completionBeep: false, programReminderInterval: 0)
  239. XCTAssertEqual("16140000f5b9000a0ad7f5b9000a0ad70aaf000a0ad7", cmd2.data.hexadecimalString)
  240. }
  241. func testTempBasalExtraCommandExtremeValues() {
  242. do {
  243. // 30 U/h for 12 hours
  244. // Decode 16 14 3c 00 f618 000927c0 f618 000927c0 2328 000927c0
  245. let cmd = try TempBasalExtraCommand(encodedData: Data(hexadecimalString: "16143c00f618000927c0f618000927c02328000927c0")!)
  246. XCTAssertEqual(false, cmd.acknowledgementBeep)
  247. XCTAssertEqual(false, cmd.completionBeep)
  248. XCTAssertEqual(.minutes(60), cmd.programReminderInterval)
  249. XCTAssertEqual(TimeInterval(seconds: 6), cmd.delayUntilFirstTenthOfPulse)
  250. XCTAssertEqual(6300, cmd.remainingPulses)
  251. XCTAssertEqual(2, cmd.rateEntries.count)
  252. let entry = cmd.rateEntries[0]
  253. XCTAssertEqual(TimeInterval(seconds: 6), entry.delayBetweenPulses)
  254. XCTAssertEqual(TimeInterval(hours: 10.5), entry.duration)
  255. XCTAssertEqual(30, entry.rate)
  256. } catch (let error) {
  257. XCTFail("message decoding threw error: \(error)")
  258. }
  259. // Encode
  260. let cmd = TempBasalExtraCommand(rate: 30, duration: .hours(12), acknowledgementBeep: false, completionBeep: false, programReminderInterval: .minutes(60))
  261. XCTAssertEqual("16143c00f618000927c0f618000927c02328000927c0", cmd.data.hexadecimalString)
  262. }
  263. func testTempBasalExtraCommandExtremeValues2() {
  264. do {
  265. // 29.95 U/h for 12 hours
  266. let cmd = try TempBasalExtraCommand(encodedData: Data(hexadecimalString: "16143c00f5af00092ba9f5af00092ba9231900092ba9")!)
  267. XCTAssertEqual(false, cmd.acknowledgementBeep)
  268. XCTAssertEqual(false, cmd.completionBeep)
  269. XCTAssertEqual(.minutes(60), cmd.programReminderInterval)
  270. XCTAssertEqual(TimeInterval(seconds: 6.01001), cmd.delayUntilFirstTenthOfPulse)
  271. XCTAssertEqual(6289.5, cmd.remainingPulses)
  272. XCTAssertEqual(2, cmd.rateEntries.count)
  273. let entry1 = cmd.rateEntries[0]
  274. let entry2 = cmd.rateEntries[1]
  275. XCTAssertEqual(TimeInterval(seconds: 6.01001), entry1.delayBetweenPulses, accuracy: .ulpOfOne)
  276. XCTAssertEqual(TimeInterval(hours: 12), entry1.duration + entry2.duration, accuracy: 1)
  277. XCTAssertEqual(29.95, entry1.rate, accuracy: 0.025)
  278. } catch (let error) {
  279. XCTFail("message decoding threw error: \(error)")
  280. }
  281. let cmd = TempBasalExtraCommand(rate: 29.95, duration: .hours(12), acknowledgementBeep: false, completionBeep: false, programReminderInterval: .minutes(60))
  282. XCTAssertEqual("16143c00f5af00092ba9f5af00092ba9231900092ba9", cmd.data.hexadecimalString)
  283. }
  284. }