GlucoseStoreTests.swift 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773
  1. //
  2. // GlucoseStoreTests.swift
  3. // LoopKitHostedTests
  4. //
  5. // Created by Darin Krauss on 10/12/20.
  6. // Copyright © 2020 LoopKit Authors. All rights reserved.
  7. //
  8. import XCTest
  9. import HealthKit
  10. @testable import LoopKit
  11. class GlucoseStoreTests: PersistenceControllerTestCase, GlucoseStoreDelegate {
  12. private let sample1 = NewGlucoseSample(date: Date(timeIntervalSinceNow: -.minutes(6)),
  13. quantity: HKQuantity(unit: .milligramsPerDeciliter, doubleValue: 123.4),
  14. isDisplayOnly: true,
  15. wasUserEntered: false,
  16. syncIdentifier: "1925558F-E98F-442F-BBA6-F6F75FB4FD91",
  17. syncVersion: 2)
  18. private let sample2 = NewGlucoseSample(date: Date(timeIntervalSinceNow: -.minutes(2)),
  19. quantity: HKQuantity(unit: .milligramsPerDeciliter, doubleValue: 134.5),
  20. isDisplayOnly: false,
  21. wasUserEntered: true,
  22. syncIdentifier: "535F103C-3DFE-48F2-B15A-47313191E7B7",
  23. syncVersion: 3)
  24. private let sample3 = NewGlucoseSample(date: Date(timeIntervalSinceNow: -.minutes(4)),
  25. quantity: HKQuantity(unit: .millimolesPerLiter, doubleValue: 7.65),
  26. isDisplayOnly: false,
  27. wasUserEntered: false,
  28. syncIdentifier: "E1624D2B-A971-41B8-B8A0-3A8212AC3D71",
  29. syncVersion: 4)
  30. var healthStore: HKHealthStoreMock!
  31. var glucoseStore: GlucoseStore!
  32. var delegateCompletion: XCTestExpectation?
  33. override func setUp() {
  34. super.setUp()
  35. let semaphore = DispatchSemaphore(value: 0)
  36. cacheStore.onReady { error in
  37. XCTAssertNil(error)
  38. semaphore.signal()
  39. }
  40. semaphore.wait()
  41. healthStore = HKHealthStoreMock()
  42. glucoseStore = GlucoseStore(healthStore: healthStore,
  43. cacheStore: cacheStore,
  44. cacheLength: .hours(1),
  45. observationInterval: .minutes(30),
  46. provenanceIdentifier: HKSource.default().bundleIdentifier)
  47. glucoseStore.delegate = self
  48. }
  49. override func tearDown() {
  50. let semaphore = DispatchSemaphore(value: 0)
  51. glucoseStore.purgeAllGlucoseSamples(healthKitPredicate: HKQuery.predicateForObjects(from: HKSource.default())) { error in
  52. XCTAssertNil(error)
  53. semaphore.signal()
  54. }
  55. semaphore.wait()
  56. delegateCompletion = nil
  57. glucoseStore = nil
  58. healthStore = nil
  59. super.tearDown()
  60. }
  61. // MARK: - GlucoseStoreDelegate
  62. func glucoseStoreHasUpdatedGlucoseData(_ glucoseStore: GlucoseStore) {
  63. delegateCompletion?.fulfill()
  64. }
  65. // MARK: - HealthKitSampleStore
  66. func testHealthKitQueryAnchorPersistence() {
  67. var observerQuery: HKObserverQueryMock? = nil
  68. var anchoredObjectQuery: HKAnchoredObjectQueryMock? = nil
  69. glucoseStore.createObserverQuery = { (sampleType, predicate, updateHandler) -> HKObserverQuery in
  70. observerQuery = HKObserverQueryMock(sampleType: sampleType, predicate: predicate, updateHandler: updateHandler)
  71. return observerQuery!
  72. }
  73. let authorizationCompletion = expectation(description: "authorization completion")
  74. glucoseStore.authorize { (result) in
  75. authorizationCompletion.fulfill()
  76. }
  77. waitForExpectations(timeout: 10)
  78. XCTAssertNotNil(observerQuery)
  79. let anchoredObjectQueryCreationExpectation = expectation(description: "anchored object query creation")
  80. glucoseStore.createAnchoredObjectQuery = { (sampleType, predicate, anchor, limit, resultsHandler) -> HKAnchoredObjectQuery in
  81. anchoredObjectQuery = HKAnchoredObjectQueryMock(type: sampleType, predicate: predicate, anchor: anchor, limit: limit, resultsHandler: resultsHandler)
  82. anchoredObjectQueryCreationExpectation.fulfill()
  83. return anchoredObjectQuery!
  84. }
  85. let observerQueryCompletionExpectation = expectation(description: "observer query completion")
  86. let observerQueryCompletionHandler = {
  87. observerQueryCompletionExpectation.fulfill()
  88. }
  89. // This simulates a signal marking the arrival of new HK Data.
  90. observerQuery!.updateHandler(observerQuery!, observerQueryCompletionHandler, nil)
  91. wait(for: [anchoredObjectQueryCreationExpectation], timeout: 10)
  92. // Trigger results handler for anchored object query
  93. let returnedAnchor = HKQueryAnchor(fromValue: 5)
  94. anchoredObjectQuery!.resultsHandler(anchoredObjectQuery!, [], [], returnedAnchor, nil)
  95. // Wait for observerQueryCompletionExpectation
  96. waitForExpectations(timeout: 10)
  97. XCTAssertNotNil(glucoseStore.queryAnchor)
  98. cacheStore.managedObjectContext.performAndWait {}
  99. // Create a new glucose store, and ensure it uses the last query anchor
  100. let newGlucoseStore = GlucoseStore(healthStore: healthStore,
  101. cacheStore: cacheStore,
  102. provenanceIdentifier: HKSource.default().bundleIdentifier)
  103. let newAuthorizationCompletion = expectation(description: "authorization completion")
  104. observerQuery = nil
  105. newGlucoseStore.createObserverQuery = { (sampleType, predicate, updateHandler) -> HKObserverQuery in
  106. observerQuery = HKObserverQueryMock(sampleType: sampleType, predicate: predicate, updateHandler: updateHandler)
  107. return observerQuery!
  108. }
  109. newGlucoseStore.authorize { (result) in
  110. newAuthorizationCompletion.fulfill()
  111. }
  112. waitForExpectations(timeout: 10)
  113. anchoredObjectQuery = nil
  114. let newAnchoredObjectQueryCreationExpectation = expectation(description: "new anchored object query creation")
  115. newGlucoseStore.createAnchoredObjectQuery = { (sampleType, predicate, anchor, limit, resultsHandler) -> HKAnchoredObjectQuery in
  116. anchoredObjectQuery = HKAnchoredObjectQueryMock(type: sampleType, predicate: predicate, anchor: anchor, limit: limit, resultsHandler: resultsHandler)
  117. newAnchoredObjectQueryCreationExpectation.fulfill()
  118. return anchoredObjectQuery!
  119. }
  120. // This simulates a signal marking the arrival of new HK Data.
  121. observerQuery!.updateHandler(observerQuery!, {}, nil)
  122. waitForExpectations(timeout: 10)
  123. // Assert new glucose store is querying with the last anchor that our HealthKit mock returned
  124. XCTAssertEqual(returnedAnchor, anchoredObjectQuery?.anchor)
  125. anchoredObjectQuery!.resultsHandler(anchoredObjectQuery!, [], [], returnedAnchor, nil)
  126. }
  127. // MARK: - Fetching
  128. func testGetGlucoseSamples() {
  129. let addGlucoseSamplesCompletion = expectation(description: "addGlucoseSamples")
  130. glucoseStore.addGlucoseSamples([sample1, sample2, sample3]) { result in
  131. switch result {
  132. case .failure(let error):
  133. XCTFail("Unexpected failure: \(error)")
  134. case .success(let samples):
  135. XCTAssertEqual(samples.count, 3)
  136. }
  137. addGlucoseSamplesCompletion.fulfill()
  138. }
  139. waitForExpectations(timeout: 10)
  140. let getGlucoseSamples1Completion = expectation(description: "getGlucoseSamples1")
  141. glucoseStore.getGlucoseSamples() { result in
  142. switch result {
  143. case .failure(let error):
  144. XCTFail("Unexpected failure: \(error)")
  145. case .success(let samples):
  146. XCTAssertEqual(samples.count, 3)
  147. XCTAssertNotNil(samples[0].uuid)
  148. XCTAssertEqual(samples[0].provenanceIdentifier, HKSource.default().bundleIdentifier)
  149. XCTAssertEqual(samples[0].syncIdentifier, self.sample1.syncIdentifier)
  150. XCTAssertEqual(samples[0].syncVersion, self.sample1.syncVersion)
  151. XCTAssertEqual(samples[0].startDate, self.sample1.date)
  152. XCTAssertEqual(samples[0].quantity, self.sample1.quantity)
  153. XCTAssertEqual(samples[0].isDisplayOnly, self.sample1.isDisplayOnly)
  154. XCTAssertEqual(samples[0].wasUserEntered, self.sample1.wasUserEntered)
  155. XCTAssertNotNil(samples[1].uuid)
  156. XCTAssertEqual(samples[1].provenanceIdentifier, HKSource.default().bundleIdentifier)
  157. XCTAssertEqual(samples[1].syncIdentifier, self.sample3.syncIdentifier)
  158. XCTAssertEqual(samples[1].syncVersion, self.sample3.syncVersion)
  159. XCTAssertEqual(samples[1].startDate, self.sample3.date)
  160. XCTAssertEqual(samples[1].quantity, self.sample3.quantity)
  161. XCTAssertEqual(samples[1].isDisplayOnly, self.sample3.isDisplayOnly)
  162. XCTAssertEqual(samples[1].wasUserEntered, self.sample3.wasUserEntered)
  163. XCTAssertNotNil(samples[2].uuid)
  164. XCTAssertEqual(samples[2].provenanceIdentifier, HKSource.default().bundleIdentifier)
  165. XCTAssertEqual(samples[2].syncIdentifier, self.sample2.syncIdentifier)
  166. XCTAssertEqual(samples[2].syncVersion, self.sample2.syncVersion)
  167. XCTAssertEqual(samples[2].startDate, self.sample2.date)
  168. XCTAssertEqual(samples[2].quantity, self.sample2.quantity)
  169. XCTAssertEqual(samples[2].isDisplayOnly, self.sample2.isDisplayOnly)
  170. XCTAssertEqual(samples[2].wasUserEntered, self.sample2.wasUserEntered)
  171. }
  172. getGlucoseSamples1Completion.fulfill()
  173. }
  174. waitForExpectations(timeout: 10)
  175. let getGlucoseSamples2Completion = expectation(description: "getGlucoseSamples2")
  176. glucoseStore.getGlucoseSamples(start: Date(timeIntervalSinceNow: -.minutes(5)), end: Date(timeIntervalSinceNow: -.minutes(3))) { result in
  177. switch result {
  178. case .failure(let error):
  179. XCTFail("Unexpected failure: \(error)")
  180. case .success(let samples):
  181. XCTAssertEqual(samples.count, 1)
  182. XCTAssertNotNil(samples[0].uuid)
  183. XCTAssertEqual(samples[0].provenanceIdentifier, HKSource.default().bundleIdentifier)
  184. XCTAssertEqual(samples[0].syncIdentifier, self.sample3.syncIdentifier)
  185. XCTAssertEqual(samples[0].syncVersion, self.sample3.syncVersion)
  186. XCTAssertEqual(samples[0].startDate, self.sample3.date)
  187. XCTAssertEqual(samples[0].quantity, self.sample3.quantity)
  188. XCTAssertEqual(samples[0].isDisplayOnly, self.sample3.isDisplayOnly)
  189. XCTAssertEqual(samples[0].wasUserEntered, self.sample3.wasUserEntered)
  190. }
  191. getGlucoseSamples2Completion.fulfill()
  192. }
  193. waitForExpectations(timeout: 10)
  194. let purgeCachedGlucoseObjectsCompletion = expectation(description: "purgeCachedGlucoseObjects")
  195. glucoseStore.purgeCachedGlucoseObjects() { error in
  196. XCTAssertNil(error)
  197. purgeCachedGlucoseObjectsCompletion.fulfill()
  198. }
  199. waitForExpectations(timeout: 10)
  200. let getGlucoseSamples3Completion = expectation(description: "getGlucoseSamples3")
  201. glucoseStore.getGlucoseSamples() { result in
  202. switch result {
  203. case .failure(let error):
  204. XCTFail("Unexpected failure: \(error)")
  205. case .success(let samples):
  206. XCTAssertEqual(samples.count, 0)
  207. }
  208. getGlucoseSamples3Completion.fulfill()
  209. }
  210. waitForExpectations(timeout: 10)
  211. }
  212. func testLatestGlucose() {
  213. XCTAssertNil(glucoseStore.latestGlucose)
  214. let addGlucoseSamplesCompletion = expectation(description: "addGlucoseSamples")
  215. glucoseStore.addGlucoseSamples([sample1, sample2, sample3]) { result in
  216. switch result {
  217. case .failure(let error):
  218. XCTFail("Unexpected failure: \(error)")
  219. case .success(let samples):
  220. XCTAssertEqual(samples.count, 3)
  221. }
  222. addGlucoseSamplesCompletion.fulfill()
  223. }
  224. waitForExpectations(timeout: 10)
  225. XCTAssertNotNil(glucoseStore.latestGlucose)
  226. XCTAssertEqual(glucoseStore.latestGlucose?.startDate, sample2.date)
  227. XCTAssertEqual(glucoseStore.latestGlucose?.endDate, sample2.date)
  228. XCTAssertEqual(glucoseStore.latestGlucose?.quantity, sample2.quantity)
  229. XCTAssertEqual(glucoseStore.latestGlucose?.provenanceIdentifier, HKSource.default().bundleIdentifier)
  230. XCTAssertEqual(glucoseStore.latestGlucose?.isDisplayOnly, sample2.isDisplayOnly)
  231. XCTAssertEqual(glucoseStore.latestGlucose?.wasUserEntered, sample2.wasUserEntered)
  232. let purgeCachedGlucoseObjectsCompletion = expectation(description: "purgeCachedGlucoseObjects")
  233. glucoseStore.purgeCachedGlucoseObjects() { error in
  234. XCTAssertNil(error)
  235. purgeCachedGlucoseObjectsCompletion.fulfill()
  236. }
  237. waitForExpectations(timeout: 10)
  238. XCTAssertNil(glucoseStore.latestGlucose)
  239. }
  240. // MARK: - Modification
  241. func testAddGlucoseSamples() {
  242. let addGlucoseSamples1Completion = expectation(description: "addGlucoseSamples1")
  243. glucoseStore.addGlucoseSamples([sample1, sample2, sample3, sample1, sample2, sample3]) { result in
  244. switch result {
  245. case .failure(let error):
  246. XCTFail("Unexpected failure: \(error)")
  247. case .success(let samples):
  248. XCTAssertEqual(samples.count, 3)
  249. XCTAssertNotNil(samples[0].uuid)
  250. XCTAssertEqual(samples[0].provenanceIdentifier, HKSource.default().bundleIdentifier)
  251. XCTAssertEqual(samples[0].syncIdentifier, self.sample1.syncIdentifier)
  252. XCTAssertEqual(samples[0].syncVersion, self.sample1.syncVersion)
  253. XCTAssertEqual(samples[0].startDate, self.sample1.date)
  254. XCTAssertEqual(samples[0].quantity, self.sample1.quantity)
  255. XCTAssertEqual(samples[0].isDisplayOnly, self.sample1.isDisplayOnly)
  256. XCTAssertEqual(samples[0].wasUserEntered, self.sample1.wasUserEntered)
  257. XCTAssertNotNil(samples[1].uuid)
  258. XCTAssertEqual(samples[1].provenanceIdentifier, HKSource.default().bundleIdentifier)
  259. XCTAssertEqual(samples[1].syncIdentifier, self.sample2.syncIdentifier)
  260. XCTAssertEqual(samples[1].syncVersion, self.sample2.syncVersion)
  261. XCTAssertEqual(samples[1].startDate, self.sample2.date)
  262. XCTAssertEqual(samples[1].quantity, self.sample2.quantity)
  263. XCTAssertEqual(samples[1].isDisplayOnly, self.sample2.isDisplayOnly)
  264. XCTAssertEqual(samples[1].wasUserEntered, self.sample2.wasUserEntered)
  265. XCTAssertNotNil(samples[2].uuid)
  266. XCTAssertEqual(samples[2].provenanceIdentifier, HKSource.default().bundleIdentifier)
  267. XCTAssertEqual(samples[2].syncIdentifier, self.sample3.syncIdentifier)
  268. XCTAssertEqual(samples[2].syncVersion, self.sample3.syncVersion)
  269. XCTAssertEqual(samples[2].startDate, self.sample3.date)
  270. XCTAssertEqual(samples[2].quantity, self.sample3.quantity)
  271. XCTAssertEqual(samples[2].isDisplayOnly, self.sample3.isDisplayOnly)
  272. XCTAssertEqual(samples[2].wasUserEntered, self.sample3.wasUserEntered)
  273. }
  274. addGlucoseSamples1Completion.fulfill()
  275. }
  276. waitForExpectations(timeout: 10)
  277. let getGlucoseSamples1Completion = expectation(description: "getGlucoseSamples1")
  278. glucoseStore.getGlucoseSamples() { result in
  279. switch result {
  280. case .failure(let error):
  281. XCTFail("Unexpected failure: \(error)")
  282. case .success(let samples):
  283. XCTAssertEqual(samples.count, 3)
  284. XCTAssertNotNil(samples[0].uuid)
  285. XCTAssertEqual(samples[0].provenanceIdentifier, HKSource.default().bundleIdentifier)
  286. XCTAssertEqual(samples[0].syncIdentifier, self.sample1.syncIdentifier)
  287. XCTAssertEqual(samples[0].syncVersion, self.sample1.syncVersion)
  288. XCTAssertEqual(samples[0].startDate, self.sample1.date)
  289. XCTAssertEqual(samples[0].quantity, self.sample1.quantity)
  290. XCTAssertEqual(samples[0].isDisplayOnly, self.sample1.isDisplayOnly)
  291. XCTAssertEqual(samples[0].wasUserEntered, self.sample1.wasUserEntered)
  292. XCTAssertNotNil(samples[1].uuid)
  293. XCTAssertEqual(samples[1].provenanceIdentifier, HKSource.default().bundleIdentifier)
  294. XCTAssertEqual(samples[1].syncIdentifier, self.sample3.syncIdentifier)
  295. XCTAssertEqual(samples[1].syncVersion, self.sample3.syncVersion)
  296. XCTAssertEqual(samples[1].startDate, self.sample3.date)
  297. XCTAssertEqual(samples[1].quantity, self.sample3.quantity)
  298. XCTAssertEqual(samples[1].isDisplayOnly, self.sample3.isDisplayOnly)
  299. XCTAssertEqual(samples[1].wasUserEntered, self.sample3.wasUserEntered)
  300. XCTAssertNotNil(samples[2].uuid)
  301. XCTAssertEqual(samples[2].provenanceIdentifier, HKSource.default().bundleIdentifier)
  302. XCTAssertEqual(samples[2].syncIdentifier, self.sample2.syncIdentifier)
  303. XCTAssertEqual(samples[2].syncVersion, self.sample2.syncVersion)
  304. XCTAssertEqual(samples[2].startDate, self.sample2.date)
  305. XCTAssertEqual(samples[2].quantity, self.sample2.quantity)
  306. XCTAssertEqual(samples[2].isDisplayOnly, self.sample2.isDisplayOnly)
  307. XCTAssertEqual(samples[2].wasUserEntered, self.sample2.wasUserEntered)
  308. }
  309. getGlucoseSamples1Completion.fulfill()
  310. }
  311. waitForExpectations(timeout: 10)
  312. let addGlucoseSamples2Completion = expectation(description: "addGlucoseSamples2")
  313. glucoseStore.addGlucoseSamples([sample3, sample1, sample2]) { result in
  314. switch result {
  315. case .failure(let error):
  316. XCTFail("Unexpected failure: \(error)")
  317. case .success(let samples):
  318. XCTAssertEqual(samples.count, 0)
  319. }
  320. addGlucoseSamples2Completion.fulfill()
  321. }
  322. waitForExpectations(timeout: 10)
  323. let getGlucoseSamples2Completion = expectation(description: "getGlucoseSamples2Completion")
  324. glucoseStore.getGlucoseSamples() { result in
  325. switch result {
  326. case .failure(let error):
  327. XCTFail("Unexpected failure: \(error)")
  328. case .success(let samples):
  329. XCTAssertEqual(samples.count, 3)
  330. XCTAssertNotNil(samples[0].uuid)
  331. XCTAssertEqual(samples[0].provenanceIdentifier, HKSource.default().bundleIdentifier)
  332. XCTAssertEqual(samples[0].syncIdentifier, self.sample1.syncIdentifier)
  333. XCTAssertEqual(samples[0].syncVersion, self.sample1.syncVersion)
  334. XCTAssertEqual(samples[0].startDate, self.sample1.date)
  335. XCTAssertEqual(samples[0].quantity, self.sample1.quantity)
  336. XCTAssertEqual(samples[0].isDisplayOnly, self.sample1.isDisplayOnly)
  337. XCTAssertEqual(samples[0].wasUserEntered, self.sample1.wasUserEntered)
  338. XCTAssertNotNil(samples[1].uuid)
  339. XCTAssertEqual(samples[1].provenanceIdentifier, HKSource.default().bundleIdentifier)
  340. XCTAssertEqual(samples[1].syncIdentifier, self.sample3.syncIdentifier)
  341. XCTAssertEqual(samples[1].syncVersion, self.sample3.syncVersion)
  342. XCTAssertEqual(samples[1].startDate, self.sample3.date)
  343. XCTAssertEqual(samples[1].quantity, self.sample3.quantity)
  344. XCTAssertEqual(samples[1].isDisplayOnly, self.sample3.isDisplayOnly)
  345. XCTAssertEqual(samples[1].wasUserEntered, self.sample3.wasUserEntered)
  346. XCTAssertNotNil(samples[2].uuid)
  347. XCTAssertEqual(samples[2].provenanceIdentifier, HKSource.default().bundleIdentifier)
  348. XCTAssertEqual(samples[2].syncIdentifier, self.sample2.syncIdentifier)
  349. XCTAssertEqual(samples[2].syncVersion, self.sample2.syncVersion)
  350. XCTAssertEqual(samples[2].startDate, self.sample2.date)
  351. XCTAssertEqual(samples[2].quantity, self.sample2.quantity)
  352. XCTAssertEqual(samples[2].isDisplayOnly, self.sample2.isDisplayOnly)
  353. XCTAssertEqual(samples[2].wasUserEntered, self.sample2.wasUserEntered)
  354. }
  355. getGlucoseSamples2Completion.fulfill()
  356. }
  357. waitForExpectations(timeout: 10)
  358. }
  359. func testAddGlucoseSamplesEmpty() {
  360. let addGlucoseSamplesCompletion = expectation(description: "addGlucoseSamples")
  361. glucoseStore.addGlucoseSamples([]) { result in
  362. switch result {
  363. case .failure(let error):
  364. XCTFail("Unexpected failure: \(error)")
  365. case .success(let samples):
  366. XCTAssertEqual(samples.count, 0)
  367. }
  368. addGlucoseSamplesCompletion.fulfill()
  369. }
  370. waitForExpectations(timeout: 10)
  371. }
  372. func testAddGlucoseSamplesNotification() {
  373. delegateCompletion = expectation(description: "delegate")
  374. let glucoseSamplesDidChangeCompletion = expectation(description: "glucoseSamplesDidChange")
  375. let observer = NotificationCenter.default.addObserver(forName: GlucoseStore.glucoseSamplesDidChange, object: glucoseStore, queue: nil) { notification in
  376. let updateSource = notification.userInfo?[HealthKitSampleStore.notificationUpdateSourceKey] as? Int
  377. XCTAssertEqual(updateSource, UpdateSource.changedInApp.rawValue)
  378. glucoseSamplesDidChangeCompletion.fulfill()
  379. }
  380. let addGlucoseSamplesCompletion = expectation(description: "addGlucoseSamples")
  381. glucoseStore.addGlucoseSamples([sample1, sample2, sample3]) { result in
  382. switch result {
  383. case .failure(let error):
  384. XCTFail("Unexpected failure: \(error)")
  385. case .success(let samples):
  386. XCTAssertEqual(samples.count, 3)
  387. }
  388. addGlucoseSamplesCompletion.fulfill()
  389. }
  390. wait(for: [glucoseSamplesDidChangeCompletion, delegateCompletion!, addGlucoseSamplesCompletion], timeout: 10, enforceOrder: true)
  391. NotificationCenter.default.removeObserver(observer)
  392. delegateCompletion = nil
  393. }
  394. // MARK: - Watch Synchronization
  395. func testSyncGlucoseSamples() {
  396. var syncGlucoseSamples: [StoredGlucoseSample] = []
  397. let addGlucoseSamplesCompletion = expectation(description: "addGlucoseSamples")
  398. glucoseStore.addGlucoseSamples([sample1, sample2, sample3]) { result in
  399. switch result {
  400. case .failure(let error):
  401. XCTFail("Unexpected failure: \(error)")
  402. case .success(let samples):
  403. XCTAssertEqual(samples.count, 3)
  404. }
  405. addGlucoseSamplesCompletion.fulfill()
  406. }
  407. waitForExpectations(timeout: 10)
  408. let getSyncGlucoseSamples1Completion = expectation(description: "getSyncGlucoseSamples1")
  409. glucoseStore.getSyncGlucoseSamples() { result in
  410. switch result {
  411. case .failure(let error):
  412. XCTFail("Unexpected failure: \(error)")
  413. case .success(let objects):
  414. XCTAssertEqual(objects.count, 3)
  415. XCTAssertNotNil(objects[0].uuid)
  416. XCTAssertEqual(objects[0].provenanceIdentifier, HKSource.default().bundleIdentifier)
  417. XCTAssertEqual(objects[0].syncIdentifier, self.sample1.syncIdentifier)
  418. XCTAssertEqual(objects[0].syncVersion, self.sample1.syncVersion)
  419. XCTAssertEqual(objects[0].quantity, self.sample1.quantity)
  420. XCTAssertEqual(objects[0].startDate, self.sample1.date)
  421. XCTAssertEqual(objects[0].isDisplayOnly, self.sample1.isDisplayOnly)
  422. XCTAssertEqual(objects[0].wasUserEntered, self.sample1.wasUserEntered)
  423. XCTAssertNotNil(objects[1].uuid)
  424. XCTAssertEqual(objects[1].provenanceIdentifier, HKSource.default().bundleIdentifier)
  425. XCTAssertEqual(objects[1].syncIdentifier, self.sample3.syncIdentifier)
  426. XCTAssertEqual(objects[1].syncVersion, self.sample3.syncVersion)
  427. XCTAssertEqual(objects[1].quantity, self.sample3.quantity)
  428. XCTAssertEqual(objects[1].startDate, self.sample3.date)
  429. XCTAssertEqual(objects[1].isDisplayOnly, self.sample3.isDisplayOnly)
  430. XCTAssertEqual(objects[1].wasUserEntered, self.sample3.wasUserEntered)
  431. XCTAssertNotNil(objects[2].uuid)
  432. XCTAssertEqual(objects[2].provenanceIdentifier, HKSource.default().bundleIdentifier)
  433. XCTAssertEqual(objects[2].syncIdentifier, self.sample2.syncIdentifier)
  434. XCTAssertEqual(objects[2].syncVersion, self.sample2.syncVersion)
  435. XCTAssertEqual(objects[2].quantity, self.sample2.quantity)
  436. XCTAssertEqual(objects[2].startDate, self.sample2.date)
  437. XCTAssertEqual(objects[2].isDisplayOnly, self.sample2.isDisplayOnly)
  438. XCTAssertEqual(objects[2].wasUserEntered, self.sample2.wasUserEntered)
  439. syncGlucoseSamples = objects
  440. }
  441. getSyncGlucoseSamples1Completion.fulfill()
  442. }
  443. waitForExpectations(timeout: 10)
  444. let getSyncGlucoseSamples2Completion = expectation(description: "getSyncGlucoseSamples2")
  445. glucoseStore.getSyncGlucoseSamples(start: Date(timeIntervalSinceNow: -.minutes(5)), end: Date(timeIntervalSinceNow: -.minutes(3))) { result in
  446. switch result {
  447. case .failure(let error):
  448. XCTFail("Unexpected failure: \(error)")
  449. case .success(let objects):
  450. XCTAssertEqual(objects.count, 1)
  451. XCTAssertNotNil(objects[0].uuid)
  452. XCTAssertEqual(objects[0].provenanceIdentifier, HKSource.default().bundleIdentifier)
  453. XCTAssertEqual(objects[0].syncIdentifier, self.sample3.syncIdentifier)
  454. XCTAssertEqual(objects[0].syncVersion, self.sample3.syncVersion)
  455. XCTAssertEqual(objects[0].quantity, self.sample3.quantity)
  456. XCTAssertEqual(objects[0].startDate, self.sample3.date)
  457. XCTAssertEqual(objects[0].isDisplayOnly, self.sample3.isDisplayOnly)
  458. XCTAssertEqual(objects[0].wasUserEntered, self.sample3.wasUserEntered)
  459. }
  460. getSyncGlucoseSamples2Completion.fulfill()
  461. }
  462. waitForExpectations(timeout: 10)
  463. let purgeCachedGlucoseObjectsCompletion = expectation(description: "purgeCachedGlucoseObjects")
  464. glucoseStore.purgeCachedGlucoseObjects() { error in
  465. XCTAssertNil(error)
  466. purgeCachedGlucoseObjectsCompletion.fulfill()
  467. }
  468. waitForExpectations(timeout: 10)
  469. let getSyncGlucoseSamples3Completion = expectation(description: "getSyncGlucoseSamples3")
  470. glucoseStore.getSyncGlucoseSamples() { result in
  471. switch result {
  472. case .failure(let error):
  473. XCTFail("Unexpected failure: \(error)")
  474. case .success(let samples):
  475. XCTAssertEqual(samples.count, 0)
  476. }
  477. getSyncGlucoseSamples3Completion.fulfill()
  478. }
  479. waitForExpectations(timeout: 10)
  480. let setSyncGlucoseSamplesCompletion = expectation(description: "setSyncGlucoseSamples")
  481. glucoseStore.setSyncGlucoseSamples(syncGlucoseSamples) { error in
  482. XCTAssertNil(error)
  483. setSyncGlucoseSamplesCompletion.fulfill()
  484. }
  485. waitForExpectations(timeout: 10)
  486. let getSyncGlucoseSamples4Completion = expectation(description: "getSyncGlucoseSamples4")
  487. glucoseStore.getSyncGlucoseSamples() { result in
  488. switch result {
  489. case .failure(let error):
  490. XCTFail("Unexpected failure: \(error)")
  491. case .success(let objects):
  492. XCTAssertEqual(objects.count, 3)
  493. XCTAssertNotNil(objects[0].uuid)
  494. XCTAssertEqual(objects[0].provenanceIdentifier, HKSource.default().bundleIdentifier)
  495. XCTAssertEqual(objects[0].syncIdentifier, self.sample1.syncIdentifier)
  496. XCTAssertEqual(objects[0].syncVersion, self.sample1.syncVersion)
  497. XCTAssertEqual(objects[0].quantity, self.sample1.quantity)
  498. XCTAssertEqual(objects[0].startDate, self.sample1.date)
  499. XCTAssertEqual(objects[0].isDisplayOnly, self.sample1.isDisplayOnly)
  500. XCTAssertEqual(objects[0].wasUserEntered, self.sample1.wasUserEntered)
  501. XCTAssertNotNil(objects[1].uuid)
  502. XCTAssertEqual(objects[1].provenanceIdentifier, HKSource.default().bundleIdentifier)
  503. XCTAssertEqual(objects[1].syncIdentifier, self.sample3.syncIdentifier)
  504. XCTAssertEqual(objects[1].syncVersion, self.sample3.syncVersion)
  505. XCTAssertEqual(objects[1].quantity, self.sample3.quantity)
  506. XCTAssertEqual(objects[1].startDate, self.sample3.date)
  507. XCTAssertEqual(objects[1].isDisplayOnly, self.sample3.isDisplayOnly)
  508. XCTAssertEqual(objects[1].wasUserEntered, self.sample3.wasUserEntered)
  509. XCTAssertNotNil(objects[2].uuid)
  510. XCTAssertEqual(objects[2].provenanceIdentifier, HKSource.default().bundleIdentifier)
  511. XCTAssertEqual(objects[2].syncIdentifier, self.sample2.syncIdentifier)
  512. XCTAssertEqual(objects[2].syncVersion, self.sample2.syncVersion)
  513. XCTAssertEqual(objects[2].quantity, self.sample2.quantity)
  514. XCTAssertEqual(objects[2].startDate, self.sample2.date)
  515. XCTAssertEqual(objects[2].isDisplayOnly, self.sample2.isDisplayOnly)
  516. XCTAssertEqual(objects[2].wasUserEntered, self.sample2.wasUserEntered)
  517. syncGlucoseSamples = objects
  518. }
  519. getSyncGlucoseSamples4Completion.fulfill()
  520. }
  521. waitForExpectations(timeout: 10)
  522. }
  523. // MARK: - Cache Management
  524. func testEarliestCacheDate() {
  525. XCTAssertEqual(glucoseStore.earliestCacheDate.timeIntervalSinceNow, -.hours(1), accuracy: 1)
  526. }
  527. func testPurgeAllGlucoseSamples() {
  528. let addGlucoseSamplesCompletion = expectation(description: "addGlucoseSamples")
  529. glucoseStore.addGlucoseSamples([sample1, sample2, sample3]) { result in
  530. switch result {
  531. case .failure(let error):
  532. XCTFail("Unexpected failure: \(error)")
  533. case .success(let samples):
  534. XCTAssertEqual(samples.count, 3)
  535. }
  536. addGlucoseSamplesCompletion.fulfill()
  537. }
  538. waitForExpectations(timeout: 10)
  539. let getGlucoseSamples1Completion = expectation(description: "getGlucoseSamples1")
  540. glucoseStore.getGlucoseSamples() { result in
  541. switch result {
  542. case .failure(let error):
  543. XCTFail("Unexpected failure: \(error)")
  544. case .success(let samples):
  545. XCTAssertEqual(samples.count, 3)
  546. }
  547. getGlucoseSamples1Completion.fulfill()
  548. }
  549. waitForExpectations(timeout: 10)
  550. let purgeAllGlucoseSamplesCompletion = expectation(description: "purgeAllGlucoseSamples")
  551. glucoseStore.purgeAllGlucoseSamples(healthKitPredicate: HKQuery.predicateForObjects(from: HKSource.default())) { error in
  552. XCTAssertNil(error)
  553. purgeAllGlucoseSamplesCompletion.fulfill()
  554. }
  555. waitForExpectations(timeout: 10)
  556. let getGlucoseSamples2Completion = expectation(description: "getGlucoseSamples2")
  557. glucoseStore.getGlucoseSamples() { result in
  558. switch result {
  559. case .failure(let error):
  560. XCTFail("Unexpected failure: \(error)")
  561. case .success(let samples):
  562. XCTAssertEqual(samples.count, 0)
  563. }
  564. getGlucoseSamples2Completion.fulfill()
  565. }
  566. waitForExpectations(timeout: 10)
  567. }
  568. func testPurgeExpiredGlucoseObjects() {
  569. let expiredSample = NewGlucoseSample(date: Date(timeIntervalSinceNow: -.hours(2)),
  570. quantity: HKQuantity(unit: .milligramsPerDeciliter, doubleValue: 198.7),
  571. isDisplayOnly: false,
  572. wasUserEntered: false,
  573. syncIdentifier: "6AB8C7F3-A2CE-442F-98C4-3D0514626B5F",
  574. syncVersion: 3)
  575. let addGlucoseSamplesCompletion = expectation(description: "addGlucoseSamples")
  576. glucoseStore.addGlucoseSamples([sample1, sample2, sample3, expiredSample]) { result in
  577. switch result {
  578. case .failure(let error):
  579. XCTFail("Unexpected failure: \(error)")
  580. case .success(let samples):
  581. XCTAssertEqual(samples.count, 4)
  582. }
  583. addGlucoseSamplesCompletion.fulfill()
  584. }
  585. waitForExpectations(timeout: 10)
  586. let getGlucoseSamplesCompletion = expectation(description: "getGlucoseSamples")
  587. glucoseStore.getGlucoseSamples() { result in
  588. switch result {
  589. case .failure(let error):
  590. XCTFail("Unexpected failure: \(error)")
  591. case .success(let samples):
  592. XCTAssertEqual(samples.count, 3)
  593. }
  594. getGlucoseSamplesCompletion.fulfill()
  595. }
  596. waitForExpectations(timeout: 10)
  597. }
  598. func testPurgeCachedGlucoseObjects() {
  599. let addGlucoseSamplesCompletion = expectation(description: "addGlucoseSamples")
  600. glucoseStore.addGlucoseSamples([sample1, sample2, sample3]) { result in
  601. switch result {
  602. case .failure(let error):
  603. XCTFail("Unexpected failure: \(error)")
  604. case .success(let samples):
  605. XCTAssertEqual(samples.count, 3)
  606. }
  607. addGlucoseSamplesCompletion.fulfill()
  608. }
  609. waitForExpectations(timeout: 10)
  610. let getGlucoseSamples1Completion = expectation(description: "getGlucoseSamples1")
  611. glucoseStore.getGlucoseSamples() { result in
  612. switch result {
  613. case .failure(let error):
  614. XCTFail("Unexpected failure: \(error)")
  615. case .success(let samples):
  616. XCTAssertEqual(samples.count, 3)
  617. }
  618. getGlucoseSamples1Completion.fulfill()
  619. }
  620. waitForExpectations(timeout: 10)
  621. let purgeCachedGlucoseObjects1Completion = expectation(description: "purgeCachedGlucoseObjects1")
  622. glucoseStore.purgeCachedGlucoseObjects(before: Date(timeIntervalSinceNow: -.minutes(5))) { error in
  623. XCTAssertNil(error)
  624. purgeCachedGlucoseObjects1Completion.fulfill()
  625. }
  626. waitForExpectations(timeout: 10)
  627. let getGlucoseSamples2Completion = expectation(description: "getGlucoseSamples2")
  628. glucoseStore.getGlucoseSamples() { result in
  629. switch result {
  630. case .failure(let error):
  631. XCTFail("Unexpected failure: \(error)")
  632. case .success(let samples):
  633. XCTAssertEqual(samples.count, 2)
  634. }
  635. getGlucoseSamples2Completion.fulfill()
  636. }
  637. waitForExpectations(timeout: 10)
  638. let purgeCachedGlucoseObjects2Completion = expectation(description: "purgeCachedGlucoseObjects2")
  639. glucoseStore.purgeCachedGlucoseObjects() { error in
  640. XCTAssertNil(error)
  641. purgeCachedGlucoseObjects2Completion.fulfill()
  642. }
  643. waitForExpectations(timeout: 10)
  644. let getGlucoseSamples3Completion = expectation(description: "getGlucoseSamples3")
  645. glucoseStore.getGlucoseSamples() { result in
  646. switch result {
  647. case .failure(let error):
  648. XCTFail("Unexpected failure: \(error)")
  649. case .success(let samples):
  650. XCTAssertEqual(samples.count, 0)
  651. }
  652. getGlucoseSamples3Completion.fulfill()
  653. }
  654. waitForExpectations(timeout: 10)
  655. }
  656. func testPurgeCachedGlucoseObjectsNotification() {
  657. let addGlucoseSamplesCompletion = expectation(description: "addGlucoseSamples")
  658. glucoseStore.addGlucoseSamples([sample1, sample2, sample3]) { result in
  659. switch result {
  660. case .failure(let error):
  661. XCTFail("Unexpected failure: \(error)")
  662. case .success(let samples):
  663. XCTAssertEqual(samples.count, 3)
  664. }
  665. addGlucoseSamplesCompletion.fulfill()
  666. }
  667. waitForExpectations(timeout: 10)
  668. delegateCompletion = expectation(description: "delegate")
  669. let glucoseSamplesDidChangeCompletion = expectation(description: "glucoseSamplesDidChange")
  670. let observer = NotificationCenter.default.addObserver(forName: GlucoseStore.glucoseSamplesDidChange, object: glucoseStore, queue: nil) { notification in
  671. let updateSource = notification.userInfo?[HealthKitSampleStore.notificationUpdateSourceKey] as? Int
  672. XCTAssertEqual(updateSource, UpdateSource.changedInApp.rawValue)
  673. glucoseSamplesDidChangeCompletion.fulfill()
  674. }
  675. let purgeCachedGlucoseObjectsCompletion = expectation(description: "purgeCachedGlucoseObjects")
  676. glucoseStore.purgeCachedGlucoseObjects() { error in
  677. XCTAssertNil(error)
  678. purgeCachedGlucoseObjectsCompletion.fulfill()
  679. }
  680. wait(for: [glucoseSamplesDidChangeCompletion, delegateCompletion!, purgeCachedGlucoseObjectsCompletion], timeout: 10, enforceOrder: true)
  681. NotificationCenter.default.removeObserver(observer)
  682. delegateCompletion = nil
  683. }
  684. }