CarbStoreHKQueryTests.swift 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. //
  2. // CarbStoreHKQueryTests.swift
  3. // LoopKitHostedTests
  4. //
  5. // Created by Darin Krauss on 10/9/20.
  6. // Copyright © 2020 LoopKit Authors. All rights reserved.
  7. //
  8. import XCTest
  9. import HealthKit
  10. @testable import LoopKit
  11. class CarbStoreHKQueryTestsBase: PersistenceControllerTestCase {
  12. var mockHealthStore: HKHealthStoreMock!
  13. var carbStore: CarbStore!
  14. var authorizationStatus: HKAuthorizationStatus = .notDetermined
  15. var hkSampleStore: HealthKitSampleStore!
  16. override func setUp() {
  17. super.setUp()
  18. mockHealthStore = HKHealthStoreMock()
  19. mockHealthStore.authorizationStatus = authorizationStatus
  20. hkSampleStore = HealthKitSampleStore(healthStore: mockHealthStore, type: HealthKitSampleStore.carbType)
  21. hkSampleStore.observerQueryType = MockHKObserverQuery.self
  22. hkSampleStore.anchoredObjectQueryType = MockHKAnchoredObjectQuery.self
  23. carbStore = CarbStore(healthKitSampleStore: hkSampleStore,
  24. cacheStore: cacheStore,
  25. cacheLength: .hours(24),
  26. defaultAbsorptionTimes: (fast: .minutes(30), medium: .hours(3), slow: .hours(5)),
  27. provenanceIdentifier: Bundle.main.bundleIdentifier!)
  28. let semaphore = DispatchSemaphore(value: 0)
  29. cacheStore.onReady { (error) in
  30. semaphore.signal()
  31. }
  32. semaphore.wait()
  33. }
  34. override func tearDown() {
  35. carbStore = nil
  36. mockHealthStore = nil
  37. super.tearDown()
  38. }
  39. }
  40. class CarbStoreHKQueryTestsAuthorized: CarbStoreHKQueryTestsBase {
  41. override func setUp() {
  42. authorizationStatus = .sharingAuthorized
  43. super.setUp()
  44. }
  45. func testObserverQueryStartup() {
  46. // Check that an observer query is registered when authorization is already determined.
  47. XCTAssertFalse(hkSampleStore.authorizationRequired);
  48. mockHealthStore.observerQueryStartedExpectation = expectation(description: "observer query started")
  49. waitForExpectations(timeout: 2)
  50. XCTAssertNotNil(mockHealthStore.observerQuery)
  51. }
  52. }
  53. class CarbStoreHKQueryTests: CarbStoreHKQueryTestsBase {
  54. func testHKQueryAnchorPersistence() {
  55. XCTAssert(hkSampleStore.authorizationRequired);
  56. XCTAssertNil(hkSampleStore.observerQuery);
  57. mockHealthStore.observerQueryStartedExpectation = expectation(description: "observer query started")
  58. mockHealthStore.authorizationStatus = .sharingAuthorized
  59. hkSampleStore.authorizationIsDetermined()
  60. waitForExpectations(timeout: 3)
  61. XCTAssertNotNil(mockHealthStore.observerQuery)
  62. mockHealthStore.anchorQueryStartedExpectation = expectation(description: "anchored object query started")
  63. let observerQueryCompletionExpectation = expectation(description: "observer query completion")
  64. let observerQueryCompletionHandler = {
  65. observerQueryCompletionExpectation.fulfill()
  66. }
  67. let mockObserverQuery = mockHealthStore.observerQuery as! MockHKObserverQuery
  68. // This simulates a signal marking the arrival of new HK Data.
  69. mockObserverQuery.updateHandler?(mockObserverQuery, observerQueryCompletionHandler, nil)
  70. wait(for: [mockHealthStore.anchorQueryStartedExpectation!])
  71. let currentAnchor = HKQueryAnchor(fromValue: 5)
  72. let mockAnchoredObjectQuery = mockHealthStore.anchoredObjectQuery as! MockHKAnchoredObjectQuery
  73. mockAnchoredObjectQuery.resultsHandler?(mockAnchoredObjectQuery, [], [], currentAnchor, nil)
  74. // Wait for observerQueryCompletionExpectation
  75. waitForExpectations(timeout: 3)
  76. XCTAssertNotNil(hkSampleStore.queryAnchor)
  77. cacheStore.managedObjectContext.performAndWait {}
  78. // Create a new carb store, and ensure it uses the last query anchor
  79. let newSampleStore = HealthKitSampleStore(healthStore: mockHealthStore, type: HealthKitSampleStore.carbType)
  80. newSampleStore.observerQueryType = MockHKObserverQuery.self
  81. newSampleStore.anchoredObjectQueryType = MockHKAnchoredObjectQuery.self
  82. let _ = CarbStore(healthKitSampleStore: newSampleStore,
  83. cacheStore: cacheStore,
  84. cacheLength: .hours(24),
  85. defaultAbsorptionTimes: (fast: .minutes(30), medium: .hours(3), slow: .hours(5)),
  86. provenanceIdentifier: Bundle.main.bundleIdentifier!)
  87. mockHealthStore.observerQueryStartedExpectation = expectation(description: "new observer query started")
  88. mockHealthStore.authorizationStatus = .sharingAuthorized
  89. newSampleStore.authorizationIsDetermined()
  90. // Wait for observerQueryCompletionExpectation
  91. waitForExpectations(timeout: 3)
  92. mockHealthStore.anchorQueryStartedExpectation = expectation(description: "new anchored object query started")
  93. let mockObserverQuery2 = mockHealthStore.observerQuery as! MockHKObserverQuery
  94. // This simulates a signal marking the arrival of new HK Data.
  95. mockObserverQuery2.updateHandler?(mockObserverQuery2, {}, nil)
  96. // Wait for anchorQueryStartedExpectation
  97. waitForExpectations(timeout: 3)
  98. // Assert new carb store is querying with the last anchor that our HealthKit mock returned
  99. let mockAnchoredObjectQuery2 = mockHealthStore.anchoredObjectQuery as! MockHKAnchoredObjectQuery
  100. XCTAssertEqual(currentAnchor, mockAnchoredObjectQuery2.anchor)
  101. mockAnchoredObjectQuery2.resultsHandler?(mockAnchoredObjectQuery2, [], [], currentAnchor, nil)
  102. }
  103. }