| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964 |
- //
- // GlucoseStoreTests.swift
- // LoopKitHostedTests
- //
- // Created by Darin Krauss on 10/12/20.
- // Copyright © 2020 LoopKit Authors. All rights reserved.
- //
- import XCTest
- import HealthKit
- @testable import LoopKit
- class GlucoseStoreTestsBase: PersistenceControllerTestCase, GlucoseStoreDelegate {
- private static let device = HKDevice(name: "NAME", manufacturer: "MANUFACTURER", model: "MODEL", hardwareVersion: "HARDWAREVERSION", firmwareVersion: "FIRMWAREVERSION", softwareVersion: "SOFTWAREVERSION", localIdentifier: "LOCALIDENTIFIER", udiDeviceIdentifier: "UDIDEVICEIDENTIFIER")
- internal let sample1 = NewGlucoseSample(date: Date(timeIntervalSinceNow: -.minutes(6)),
- quantity: HKQuantity(unit: .milligramsPerDeciliter, doubleValue: 123.4),
- condition: nil,
- trend: nil,
- trendRate: nil,
- isDisplayOnly: true,
- wasUserEntered: false,
- syncIdentifier: "1925558F-E98F-442F-BBA6-F6F75FB4FD91",
- syncVersion: 2,
- device: device)
- internal let sample2 = NewGlucoseSample(date: Date(timeIntervalSinceNow: -.minutes(2)),
- quantity: HKQuantity(unit: .millimolesPerLiter, doubleValue: 7.4),
- condition: nil,
- trend: .flat,
- trendRate: HKQuantity(unit: .millimolesPerLiterPerMinute, doubleValue: 0.0),
- isDisplayOnly: false,
- wasUserEntered: true,
- syncIdentifier: "535F103C-3DFE-48F2-B15A-47313191E7B7",
- syncVersion: 3,
- device: device)
- internal let sample3 = NewGlucoseSample(date: Date(timeIntervalSinceNow: -.minutes(4)),
- quantity: HKQuantity(unit: .milligramsPerDeciliter, doubleValue: 400.0),
- condition: .aboveRange,
- trend: .upUpUp,
- trendRate: HKQuantity(unit: .milligramsPerDeciliterPerMinute, doubleValue: 4.2),
- isDisplayOnly: false,
- wasUserEntered: false,
- syncIdentifier: "E1624D2B-A971-41B8-B8A0-3A8212AC3D71",
- syncVersion: 4,
- device: device)
- var healthStore: HKHealthStoreMock!
- var glucoseStore: GlucoseStore!
- var delegateCompletion: XCTestExpectation?
- var authorizationStatus: HKAuthorizationStatus = .notDetermined
- override func setUp() {
- super.setUp()
- let semaphore = DispatchSemaphore(value: 0)
- cacheStore.onReady { error in
- XCTAssertNil(error)
- semaphore.signal()
- }
- semaphore.wait()
- healthStore = HKHealthStoreMock()
- healthStore.authorizationStatus = authorizationStatus
- glucoseStore = GlucoseStore(healthStore: healthStore,
- cacheStore: cacheStore,
- cacheLength: .hours(1),
- observationInterval: .minutes(30),
- provenanceIdentifier: HKSource.default().bundleIdentifier)
- glucoseStore.delegate = self
- }
- override func tearDown() {
- let semaphore = DispatchSemaphore(value: 0)
- glucoseStore.purgeAllGlucoseSamples(healthKitPredicate: HKQuery.predicateForObjects(from: HKSource.default())) { error in
- XCTAssertNil(error)
- semaphore.signal()
- }
- semaphore.wait()
- delegateCompletion = nil
- glucoseStore = nil
- healthStore = nil
- super.tearDown()
- }
- // MARK: - GlucoseStoreDelegate
- func glucoseStoreHasUpdatedGlucoseData(_ glucoseStore: GlucoseStore) {
- delegateCompletion?.fulfill()
- }
- }
- class GlucoseStoreTestsAuthorizationRequired: GlucoseStoreTestsBase {
- func testObserverQueryStartup() {
- XCTAssert(glucoseStore.authorizationRequired);
- XCTAssertNil(glucoseStore.observerQuery);
- let observerQueryCreated = expectation(description: "observer query created")
- glucoseStore.createObserverQuery = { (sampleType, predicate, updateHandler) -> HKObserverQuery in
- let observerQuery = HKObserverQueryMock(sampleType: sampleType, predicate: predicate, updateHandler: updateHandler)
- observerQueryCreated.fulfill()
- return observerQuery
- }
- let authorizationCompletion = expectation(description: "authorization completion")
- glucoseStore.authorize { (result) in
- authorizationCompletion.fulfill()
- }
- waitForExpectations(timeout: 10)
- XCTAssertNotNil(glucoseStore.observerQuery);
- }
- }
- class GlucoseStoreStoreTestsAuthorized: GlucoseStoreTestsBase {
- override func setUp() {
- authorizationStatus = .sharingAuthorized
- super.setUp()
- }
- func testObserverQueryStartup() {
- // Check that an observer query is registered when authorization is already determined.
- XCTAssertFalse(glucoseStore.authorizationRequired);
- let observerQueryCreated = expectation(description: "observer query created")
- glucoseStore.createObserverQuery = { (sampleType, predicate, updateHandler) -> HKObserverQuery in
- let observerQuery = HKObserverQueryMock(sampleType: sampleType, predicate: predicate, updateHandler: updateHandler)
- observerQueryCreated.fulfill()
- return observerQuery
- }
- waitForExpectations(timeout: 2)
- }
- }
- class GlucoseStoreTests: GlucoseStoreTestsBase {
- override func setUp() {
- authorizationStatus = .sharingAuthorized
- super.setUp()
- }
-
- // MARK: - HealthKitSampleStore
- func testHealthKitQueryAnchorPersistence() {
- var observerQuery: HKObserverQueryMock? = nil
- var anchoredObjectQuery: HKAnchoredObjectQueryMock? = nil
- // Check that an observer query was registered even without authorize() being called.
- XCTAssertFalse(glucoseStore.authorizationRequired);
- let observerQueryCreated = expectation(description: "observer query created")
- glucoseStore.createObserverQuery = { (sampleType, predicate, updateHandler) -> HKObserverQuery in
- observerQuery = HKObserverQueryMock(sampleType: sampleType, predicate: predicate, updateHandler: updateHandler)
- observerQueryCreated.fulfill()
- return observerQuery!
- }
- let authorizationCompletion = expectation(description: "authorization completion")
- glucoseStore.authorize { (result) in
- authorizationCompletion.fulfill()
- }
- waitForExpectations(timeout: 10)
- XCTAssertNotNil(observerQuery)
- let anchoredObjectQueryCreationExpectation = expectation(description: "anchored object query creation")
- glucoseStore.createAnchoredObjectQuery = { (sampleType, predicate, anchor, limit, resultsHandler) -> HKAnchoredObjectQuery in
- anchoredObjectQuery = HKAnchoredObjectQueryMock(type: sampleType, predicate: predicate, anchor: anchor, limit: limit, resultsHandler: resultsHandler)
- anchoredObjectQueryCreationExpectation.fulfill()
- return anchoredObjectQuery!
- }
- let observerQueryCompletionExpectation = expectation(description: "observer query completion")
- let observerQueryCompletionHandler = {
- observerQueryCompletionExpectation.fulfill()
- }
- // This simulates a signal marking the arrival of new HK Data.
- observerQuery!.updateHandler(observerQuery!, observerQueryCompletionHandler, nil)
- wait(for: [anchoredObjectQueryCreationExpectation], timeout: 10)
- // Trigger results handler for anchored object query
- let returnedAnchor = HKQueryAnchor(fromValue: 5)
- anchoredObjectQuery!.resultsHandler(anchoredObjectQuery!, [], [], returnedAnchor, nil)
- // Wait for observerQueryCompletionExpectation
- waitForExpectations(timeout: 10)
- XCTAssertNotNil(glucoseStore.queryAnchor)
- cacheStore.managedObjectContext.performAndWait {}
- // Create a new glucose store, and ensure it uses the last query anchor
- let newGlucoseStore = GlucoseStore(healthStore: healthStore,
- cacheStore: cacheStore,
- provenanceIdentifier: HKSource.default().bundleIdentifier)
- let newAuthorizationCompletion = expectation(description: "authorization completion")
- observerQuery = nil
- let newObserverQueryCreated = expectation(description: "new observer query created")
- newGlucoseStore.createObserverQuery = { (sampleType, predicate, updateHandler) -> HKObserverQuery in
- observerQuery = HKObserverQueryMock(sampleType: sampleType, predicate: predicate, updateHandler: updateHandler)
- newObserverQueryCreated.fulfill()
- return observerQuery!
- }
- newGlucoseStore.authorize { (result) in
- newAuthorizationCompletion.fulfill()
- }
- waitForExpectations(timeout: 10)
- anchoredObjectQuery = nil
- let newAnchoredObjectQueryCreationExpectation = expectation(description: "new anchored object query creation")
- newGlucoseStore.createAnchoredObjectQuery = { (sampleType, predicate, anchor, limit, resultsHandler) -> HKAnchoredObjectQuery in
- anchoredObjectQuery = HKAnchoredObjectQueryMock(type: sampleType, predicate: predicate, anchor: anchor, limit: limit, resultsHandler: resultsHandler)
- newAnchoredObjectQueryCreationExpectation.fulfill()
- return anchoredObjectQuery!
- }
- // This simulates a signal marking the arrival of new HK Data.
- observerQuery!.updateHandler(observerQuery!, {}, nil)
- waitForExpectations(timeout: 10)
- // Assert new glucose store is querying with the last anchor that our HealthKit mock returned
- XCTAssertEqual(returnedAnchor, anchoredObjectQuery?.anchor)
- anchoredObjectQuery!.resultsHandler(anchoredObjectQuery!, [], [], returnedAnchor, nil)
- }
- // MARK: - Fetching
- func testGetGlucoseSamples() {
- let addGlucoseSamplesCompletion = expectation(description: "addGlucoseSamples")
- glucoseStore.addGlucoseSamples([sample1, sample2, sample3]) { result in
- switch result {
- case .failure(let error):
- XCTFail("Unexpected failure: \(error)")
- case .success(let samples):
- XCTAssertEqual(samples.count, 3)
- }
- addGlucoseSamplesCompletion.fulfill()
- }
- waitForExpectations(timeout: 10)
- let getGlucoseSamples1Completion = expectation(description: "getGlucoseSamples1")
- glucoseStore.getGlucoseSamples() { result in
- switch result {
- case .failure(let error):
- XCTFail("Unexpected failure: \(error)")
- case .success(let samples):
- XCTAssertEqual(samples.count, 3)
- XCTAssertNotNil(samples[0].uuid)
- XCTAssertNil(samples[0].healthKitEligibleDate)
- assertEqualSamples(samples[0], self.sample1)
- XCTAssertNotNil(samples[1].uuid)
- XCTAssertNil(samples[1].healthKitEligibleDate)
- assertEqualSamples(samples[1], self.sample3)
- XCTAssertNotNil(samples[2].uuid)
- XCTAssertNil(samples[2].healthKitEligibleDate)
- assertEqualSamples(samples[2], self.sample2)
- }
- getGlucoseSamples1Completion.fulfill()
- }
- waitForExpectations(timeout: 10)
- let getGlucoseSamples2Completion = expectation(description: "getGlucoseSamples2")
- glucoseStore.getGlucoseSamples(start: Date(timeIntervalSinceNow: -.minutes(5)), end: Date(timeIntervalSinceNow: -.minutes(3))) { result in
- switch result {
- case .failure(let error):
- XCTFail("Unexpected failure: \(error)")
- case .success(let samples):
- XCTAssertEqual(samples.count, 1)
- XCTAssertNotNil(samples[0].uuid)
- XCTAssertNil(samples[0].healthKitEligibleDate)
- assertEqualSamples(samples[0], self.sample3)
- }
- getGlucoseSamples2Completion.fulfill()
- }
- waitForExpectations(timeout: 10)
- let purgeCachedGlucoseObjectsCompletion = expectation(description: "purgeCachedGlucoseObjects")
- glucoseStore.purgeCachedGlucoseObjects() { error in
- XCTAssertNil(error)
- purgeCachedGlucoseObjectsCompletion.fulfill()
- }
- waitForExpectations(timeout: 10)
- let getGlucoseSamples3Completion = expectation(description: "getGlucoseSamples3")
- glucoseStore.getGlucoseSamples() { result in
- switch result {
- case .failure(let error):
- XCTFail("Unexpected failure: \(error)")
- case .success(let samples):
- XCTAssertEqual(samples.count, 0)
- }
- getGlucoseSamples3Completion.fulfill()
- }
- waitForExpectations(timeout: 10)
- }
-
- enum Error: Swift.Error { case arbitrary }
- func testGetGlucoseSamplesDelayedHealthKitStorage() {
- glucoseStore.healthKitStorageDelay = .minutes(5)
- var hkobjects = [HKObject]()
- healthStore.setSaveHandler { o, _, _ in hkobjects = o }
- let addGlucoseSamplesCompletion = expectation(description: "addGlucoseSamples")
- glucoseStore.addGlucoseSamples([sample1, sample2, sample3]) { result in
- switch result {
- case .failure(let error):
- XCTFail("Unexpected failure: \(error)")
- case .success(let samples):
- XCTAssertEqual(samples.count, 3)
- }
- addGlucoseSamplesCompletion.fulfill()
- }
- waitForExpectations(timeout: 10)
- let getGlucoseSamples1Completion = expectation(description: "getGlucoseSamples1")
- glucoseStore.getGlucoseSamples() { result in
- switch result {
- case .failure(let error):
- XCTFail("Unexpected failure: \(error)")
- case .success(let samples):
- XCTAssertEqual(samples.count, 3)
- // HealthKit storage is deferred, so the second 2 UUIDs are nil
- XCTAssertNotNil(samples[0].uuid)
- XCTAssertNil(samples[0].healthKitEligibleDate)
- assertEqualSamples(samples[0], self.sample1)
- XCTAssertNil(samples[1].uuid)
- XCTAssertNotNil(samples[1].healthKitEligibleDate)
- assertEqualSamples(samples[1], self.sample3)
- XCTAssertNil(samples[2].uuid)
- XCTAssertNotNil(samples[2].healthKitEligibleDate)
- assertEqualSamples(samples[2], self.sample2)
- }
- getGlucoseSamples1Completion.fulfill()
- }
- waitForExpectations(timeout: 10)
- let stored = hkobjects[0] as! HKQuantitySample
- XCTAssertEqual(sample1.quantitySample.quantity, stored.quantity)
- }
-
- func testGetGlucoseSamplesErrorHealthKitStorage() {
- healthStore.saveError = Error.arbitrary
- var hkobjects = [HKObject]()
- healthStore.setSaveHandler { o, _, _ in hkobjects = o }
- let addGlucoseSamplesCompletion = expectation(description: "addGlucoseSamples")
- glucoseStore.addGlucoseSamples([sample1, sample2, sample3]) { result in
- switch result {
- case .failure(let error):
- XCTFail("Unexpected failure: \(error)")
- case .success(let samples):
- XCTAssertEqual(samples.count, 3)
- }
- addGlucoseSamplesCompletion.fulfill()
- }
- waitForExpectations(timeout: 10)
- let getGlucoseSamples1Completion = expectation(description: "getGlucoseSamples1")
- glucoseStore.getGlucoseSamples() { result in
- switch result {
- case .failure(let error):
- XCTFail("Unexpected failure: \(error)")
- case .success(let samples):
- XCTAssertEqual(samples.count, 3)
- // HealthKit storage is deferred, so the second 2 UUIDs are nil
- XCTAssertNil(samples[0].uuid)
- XCTAssertNotNil(samples[0].healthKitEligibleDate)
- assertEqualSamples(samples[0], self.sample1)
- XCTAssertNil(samples[1].uuid)
- XCTAssertNotNil(samples[1].healthKitEligibleDate)
- assertEqualSamples(samples[1], self.sample3)
- XCTAssertNil(samples[2].uuid)
- XCTAssertNotNil(samples[2].healthKitEligibleDate)
- assertEqualSamples(samples[2], self.sample2)
- }
- getGlucoseSamples1Completion.fulfill()
- }
- waitForExpectations(timeout: 10)
- XCTAssertEqual(3, hkobjects.count)
- }
- func testGetGlucoseSamplesDeniedHealthKitStorage() {
- healthStore.authorizationStatus = .sharingDenied
- var hkobjects = [HKObject]()
- healthStore.setSaveHandler { o, _, _ in hkobjects = o }
- let addGlucoseSamplesCompletion = expectation(description: "addGlucoseSamples")
- glucoseStore.addGlucoseSamples([sample1, sample2, sample3]) { result in
- switch result {
- case .failure(let error):
- XCTFail("Unexpected failure: \(error)")
- case .success(let samples):
- XCTAssertEqual(samples.count, 3)
- }
- addGlucoseSamplesCompletion.fulfill()
- }
- waitForExpectations(timeout: 10)
- let getGlucoseSamples1Completion = expectation(description: "getGlucoseSamples1")
- glucoseStore.getGlucoseSamples() { result in
- switch result {
- case .failure(let error):
- XCTFail("Unexpected failure: \(error)")
- case .success(let samples):
- XCTAssertEqual(samples.count, 3)
- // HealthKit storage is denied, so all UUIDs are nil
- XCTAssertNil(samples[0].uuid)
- XCTAssertNil(samples[0].healthKitEligibleDate)
- assertEqualSamples(samples[0], self.sample1)
- XCTAssertNil(samples[1].uuid)
- XCTAssertNil(samples[1].healthKitEligibleDate)
- assertEqualSamples(samples[1], self.sample3)
- XCTAssertNil(samples[2].uuid)
- XCTAssertNil(samples[2].healthKitEligibleDate)
- assertEqualSamples(samples[2], self.sample2)
- }
- getGlucoseSamples1Completion.fulfill()
- }
- waitForExpectations(timeout: 10)
- XCTAssertTrue(hkobjects.isEmpty)
- }
-
- func testGetGlucoseSamplesSomeDeniedHealthKitStorage() {
- glucoseStore.healthKitStorageDelay = 0
- var hkobjects = [HKObject]()
- healthStore.setSaveHandler { o, _, _ in hkobjects = o }
- let addGlucoseSamples1Completion = expectation(description: "addGlucoseSamples1")
- // Authorized
- glucoseStore.addGlucoseSamples([sample1]) { result in
- switch result {
- case .failure(let error):
- XCTFail("Unexpected failure: \(error)")
- case .success(let samples):
- XCTAssertEqual(samples.count, 1)
- }
- addGlucoseSamples1Completion.fulfill()
- }
- waitForExpectations(timeout: 10)
- XCTAssertEqual(1, hkobjects.count)
- hkobjects = []
-
- healthStore.authorizationStatus = .sharingDenied
- let addGlucoseSamples2Completion = expectation(description: "addGlucoseSamples2")
- // Denied
- glucoseStore.addGlucoseSamples([sample2]) { result in
- switch result {
- case .failure(let error):
- XCTFail("Unexpected failure: \(error)")
- case .success(let samples):
- XCTAssertEqual(samples.count, 1)
- }
- addGlucoseSamples2Completion.fulfill()
- }
- waitForExpectations(timeout: 10)
- XCTAssertEqual(0, hkobjects.count)
- hkobjects = []
- healthStore.authorizationStatus = .sharingAuthorized
- let addGlucoseSamples3Completion = expectation(description: "addGlucoseSamples3")
- // Authorized
- glucoseStore.addGlucoseSamples([sample3]) { result in
- switch result {
- case .failure(let error):
- XCTFail("Unexpected failure: \(error)")
- case .success(let samples):
- XCTAssertEqual(samples.count, 1)
- }
- addGlucoseSamples3Completion.fulfill()
- }
- waitForExpectations(timeout: 10)
- XCTAssertEqual(1, hkobjects.count)
- hkobjects = []
- let getGlucoseSamples1Completion = expectation(description: "getGlucoseSamples1")
- glucoseStore.getGlucoseSamples() { result in
- switch result {
- case .failure(let error):
- XCTFail("Unexpected failure: \(error)")
- case .success(let samples):
- XCTAssertEqual(samples.count, 3)
- XCTAssertNotNil(samples[0].uuid)
- XCTAssertNil(samples[0].healthKitEligibleDate)
- assertEqualSamples(samples[0], self.sample1)
- XCTAssertNotNil(samples[1].uuid)
- XCTAssertNil(samples[1].healthKitEligibleDate)
- assertEqualSamples(samples[1], self.sample3)
- XCTAssertNil(samples[2].uuid)
- XCTAssertNil(samples[2].healthKitEligibleDate)
- assertEqualSamples(samples[2], self.sample2)
- }
- getGlucoseSamples1Completion.fulfill()
- }
- waitForExpectations(timeout: 10)
- }
-
- func testLatestGlucose() {
- XCTAssertNil(glucoseStore.latestGlucose)
- let addGlucoseSamplesCompletion = expectation(description: "addGlucoseSamples")
- glucoseStore.addGlucoseSamples([sample1, sample2, sample3]) { result in
- switch result {
- case .failure(let error):
- XCTFail("Unexpected failure: \(error)")
- case .success(let samples):
- XCTAssertEqual(samples.count, 3)
- assertEqualSamples(samples[0], self.sample1)
- assertEqualSamples(samples[1], self.sample2)
- assertEqualSamples(samples[2], self.sample3)
- }
- addGlucoseSamplesCompletion.fulfill()
- }
- waitForExpectations(timeout: 10)
- XCTAssertNotNil(glucoseStore.latestGlucose)
- XCTAssertEqual(glucoseStore.latestGlucose?.startDate, sample2.date)
- XCTAssertEqual(glucoseStore.latestGlucose?.endDate, sample2.date)
- XCTAssertEqual(glucoseStore.latestGlucose?.quantity, sample2.quantity)
- XCTAssertEqual(glucoseStore.latestGlucose?.provenanceIdentifier, HKSource.default().bundleIdentifier)
- XCTAssertEqual(glucoseStore.latestGlucose?.isDisplayOnly, sample2.isDisplayOnly)
- XCTAssertEqual(glucoseStore.latestGlucose?.wasUserEntered, sample2.wasUserEntered)
- let purgeCachedGlucoseObjectsCompletion = expectation(description: "purgeCachedGlucoseObjects")
- glucoseStore.purgeCachedGlucoseObjects() { error in
- XCTAssertNil(error)
- purgeCachedGlucoseObjectsCompletion.fulfill()
- }
- waitForExpectations(timeout: 10)
- XCTAssertNil(glucoseStore.latestGlucose)
- }
- // MARK: - Modification
- func testAddGlucoseSamples() {
- let addGlucoseSamples1Completion = expectation(description: "addGlucoseSamples1")
- glucoseStore.addGlucoseSamples([sample1, sample2, sample3, sample1, sample2, sample3]) { result in
- switch result {
- case .failure(let error):
- XCTFail("Unexpected failure: \(error)")
- case .success(let samples):
- XCTAssertEqual(samples.count, 3)
- // Note: the HealthKit UUID is no longer updated before being returned as a result of addGlucoseSamples.
- XCTAssertNil(samples[0].uuid)
- XCTAssertNotNil(samples[0].healthKitEligibleDate)
- assertEqualSamples(samples[0], self.sample1)
- XCTAssertNil(samples[1].uuid)
- XCTAssertNotNil(samples[1].healthKitEligibleDate)
- assertEqualSamples(samples[1], self.sample2)
- XCTAssertNil(samples[2].uuid)
- XCTAssertNotNil(samples[2].healthKitEligibleDate)
- assertEqualSamples(samples[2], self.sample3)
- }
- addGlucoseSamples1Completion.fulfill()
- }
- waitForExpectations(timeout: 10)
- let getGlucoseSamples1Completion = expectation(description: "getGlucoseSamples1")
- glucoseStore.getGlucoseSamples() { result in
- switch result {
- case .failure(let error):
- XCTFail("Unexpected failure: \(error)")
- case .success(let samples):
- XCTAssertEqual(samples.count, 3)
- XCTAssertNotNil(samples[0].uuid)
- XCTAssertNil(samples[0].healthKitEligibleDate)
- assertEqualSamples(samples[0], self.sample1)
- XCTAssertNotNil(samples[1].uuid)
- XCTAssertNil(samples[1].healthKitEligibleDate)
- assertEqualSamples(samples[1], self.sample3)
- XCTAssertNotNil(samples[2].uuid)
- XCTAssertNil(samples[2].healthKitEligibleDate)
- assertEqualSamples(samples[2], self.sample2)
- }
- getGlucoseSamples1Completion.fulfill()
- }
- waitForExpectations(timeout: 10)
- let addGlucoseSamples2Completion = expectation(description: "addGlucoseSamples2")
- glucoseStore.addGlucoseSamples([sample3, sample1, sample2]) { result in
- switch result {
- case .failure(let error):
- XCTFail("Unexpected failure: \(error)")
- case .success(let samples):
- XCTAssertEqual(samples.count, 0)
- }
- addGlucoseSamples2Completion.fulfill()
- }
- waitForExpectations(timeout: 10)
- let getGlucoseSamples2Completion = expectation(description: "getGlucoseSamples2Completion")
- glucoseStore.getGlucoseSamples() { result in
- switch result {
- case .failure(let error):
- XCTFail("Unexpected failure: \(error)")
- case .success(let samples):
- XCTAssertEqual(samples.count, 3)
- XCTAssertNotNil(samples[0].uuid)
- XCTAssertNil(samples[0].healthKitEligibleDate)
- assertEqualSamples(samples[0], self.sample1)
- XCTAssertNotNil(samples[1].uuid)
- XCTAssertNil(samples[1].healthKitEligibleDate)
- assertEqualSamples(samples[1], self.sample3)
- XCTAssertNotNil(samples[2].uuid)
- XCTAssertNil(samples[2].healthKitEligibleDate)
- assertEqualSamples(samples[2], self.sample2)
- }
- getGlucoseSamples2Completion.fulfill()
- }
- waitForExpectations(timeout: 10)
- }
- func testAddGlucoseSamplesEmpty() {
- let addGlucoseSamplesCompletion = expectation(description: "addGlucoseSamples")
- glucoseStore.addGlucoseSamples([]) { result in
- switch result {
- case .failure(let error):
- XCTFail("Unexpected failure: \(error)")
- case .success(let samples):
- XCTAssertEqual(samples.count, 0)
- }
- addGlucoseSamplesCompletion.fulfill()
- }
- waitForExpectations(timeout: 10)
- }
- func testAddGlucoseSamplesNotification() {
- delegateCompletion = expectation(description: "delegate")
- let glucoseSamplesDidChangeCompletion = expectation(description: "glucoseSamplesDidChange")
- let observer = NotificationCenter.default.addObserver(forName: GlucoseStore.glucoseSamplesDidChange, object: glucoseStore, queue: nil) { notification in
- glucoseSamplesDidChangeCompletion.fulfill()
- }
- let addGlucoseSamplesCompletion = expectation(description: "addGlucoseSamples")
- glucoseStore.addGlucoseSamples([sample1, sample2, sample3]) { result in
- switch result {
- case .failure(let error):
- XCTFail("Unexpected failure: \(error)")
- case .success(let samples):
- XCTAssertEqual(samples.count, 3)
- }
- addGlucoseSamplesCompletion.fulfill()
- }
- wait(for: [glucoseSamplesDidChangeCompletion, delegateCompletion!, addGlucoseSamplesCompletion], timeout: 10, enforceOrder: true)
- NotificationCenter.default.removeObserver(observer)
- delegateCompletion = nil
- }
- // MARK: - Watch Synchronization
- func testSyncGlucoseSamples() {
- var syncGlucoseSamples: [StoredGlucoseSample] = []
- let addGlucoseSamplesCompletion = expectation(description: "addGlucoseSamples")
- glucoseStore.addGlucoseSamples([sample1, sample2, sample3]) { result in
- switch result {
- case .failure(let error):
- XCTFail("Unexpected failure: \(error)")
- case .success(let samples):
- XCTAssertEqual(samples.count, 3)
- }
- addGlucoseSamplesCompletion.fulfill()
- }
- waitForExpectations(timeout: 10)
- let getSyncGlucoseSamples1Completion = expectation(description: "getSyncGlucoseSamples1")
- glucoseStore.getSyncGlucoseSamples() { result in
- switch result {
- case .failure(let error):
- XCTFail("Unexpected failure: \(error)")
- case .success(let objects):
- XCTAssertEqual(objects.count, 3)
- XCTAssertNotNil(objects[0].uuid)
- assertEqualSamples(objects[0], self.sample1)
- XCTAssertNotNil(objects[1].uuid)
- assertEqualSamples(objects[1], self.sample3)
- XCTAssertNotNil(objects[2].uuid)
- assertEqualSamples(objects[2], self.sample2)
- syncGlucoseSamples = objects
- }
- getSyncGlucoseSamples1Completion.fulfill()
- }
- waitForExpectations(timeout: 10)
- let getSyncGlucoseSamples2Completion = expectation(description: "getSyncGlucoseSamples2")
- glucoseStore.getSyncGlucoseSamples(start: Date(timeIntervalSinceNow: -.minutes(5)), end: Date(timeIntervalSinceNow: -.minutes(3))) { result in
- switch result {
- case .failure(let error):
- XCTFail("Unexpected failure: \(error)")
- case .success(let objects):
- XCTAssertEqual(objects.count, 1)
- XCTAssertNotNil(objects[0].uuid)
- assertEqualSamples(objects[0], self.sample3)
- }
- getSyncGlucoseSamples2Completion.fulfill()
- }
- waitForExpectations(timeout: 10)
- let purgeCachedGlucoseObjectsCompletion = expectation(description: "purgeCachedGlucoseObjects")
- glucoseStore.purgeCachedGlucoseObjects() { error in
- XCTAssertNil(error)
- purgeCachedGlucoseObjectsCompletion.fulfill()
- }
- waitForExpectations(timeout: 10)
- let getSyncGlucoseSamples3Completion = expectation(description: "getSyncGlucoseSamples3")
- glucoseStore.getSyncGlucoseSamples() { result in
- switch result {
- case .failure(let error):
- XCTFail("Unexpected failure: \(error)")
- case .success(let samples):
- XCTAssertEqual(samples.count, 0)
- }
- getSyncGlucoseSamples3Completion.fulfill()
- }
- waitForExpectations(timeout: 10)
- let setSyncGlucoseSamplesCompletion = expectation(description: "setSyncGlucoseSamples")
- glucoseStore.setSyncGlucoseSamples(syncGlucoseSamples) { error in
- XCTAssertNil(error)
- setSyncGlucoseSamplesCompletion.fulfill()
- }
- waitForExpectations(timeout: 10)
- let getSyncGlucoseSamples4Completion = expectation(description: "getSyncGlucoseSamples4")
- glucoseStore.getSyncGlucoseSamples() { result in
- switch result {
- case .failure(let error):
- XCTFail("Unexpected failure: \(error)")
- case .success(let objects):
- XCTAssertEqual(objects.count, 3)
- XCTAssertNotNil(objects[0].uuid)
- assertEqualSamples(objects[0], self.sample1)
- XCTAssertNotNil(objects[1].uuid)
- assertEqualSamples(objects[1], self.sample3)
- XCTAssertNotNil(objects[2].uuid)
- assertEqualSamples(objects[2], self.sample2)
- syncGlucoseSamples = objects
- }
- getSyncGlucoseSamples4Completion.fulfill()
- }
- waitForExpectations(timeout: 10)
- }
- // MARK: - Cache Management
- func testEarliestCacheDate() {
- XCTAssertEqual(glucoseStore.earliestCacheDate.timeIntervalSinceNow, -.hours(1), accuracy: 1)
- }
- func testPurgeAllGlucoseSamples() {
- let addGlucoseSamplesCompletion = expectation(description: "addGlucoseSamples")
- glucoseStore.addGlucoseSamples([sample1, sample2, sample3]) { result in
- switch result {
- case .failure(let error):
- XCTFail("Unexpected failure: \(error)")
- case .success(let samples):
- XCTAssertEqual(samples.count, 3)
- }
- addGlucoseSamplesCompletion.fulfill()
- }
- waitForExpectations(timeout: 10)
- let getGlucoseSamples1Completion = expectation(description: "getGlucoseSamples1")
- glucoseStore.getGlucoseSamples() { result in
- switch result {
- case .failure(let error):
- XCTFail("Unexpected failure: \(error)")
- case .success(let samples):
- XCTAssertEqual(samples.count, 3)
- }
- getGlucoseSamples1Completion.fulfill()
- }
- waitForExpectations(timeout: 10)
- let purgeAllGlucoseSamplesCompletion = expectation(description: "purgeAllGlucoseSamples")
- glucoseStore.purgeAllGlucoseSamples(healthKitPredicate: HKQuery.predicateForObjects(from: HKSource.default())) { error in
- XCTAssertNil(error)
- purgeAllGlucoseSamplesCompletion.fulfill()
- }
- waitForExpectations(timeout: 10)
- let getGlucoseSamples2Completion = expectation(description: "getGlucoseSamples2")
- glucoseStore.getGlucoseSamples() { result in
- switch result {
- case .failure(let error):
- XCTFail("Unexpected failure: \(error)")
- case .success(let samples):
- XCTAssertEqual(samples.count, 0)
- }
- getGlucoseSamples2Completion.fulfill()
- }
- waitForExpectations(timeout: 10)
- }
- func testPurgeExpiredGlucoseObjects() {
- let expiredSample = NewGlucoseSample(date: Date(timeIntervalSinceNow: -.hours(2)),
- quantity: HKQuantity(unit: .milligramsPerDeciliter, doubleValue: 198.7),
- condition: nil,
- trend: nil,
- trendRate: nil,
- isDisplayOnly: false,
- wasUserEntered: false,
- syncIdentifier: "6AB8C7F3-A2CE-442F-98C4-3D0514626B5F",
- syncVersion: 3)
- let addGlucoseSamplesCompletion = expectation(description: "addGlucoseSamples")
- glucoseStore.addGlucoseSamples([sample1, sample2, sample3, expiredSample]) { result in
- switch result {
- case .failure(let error):
- XCTFail("Unexpected failure: \(error)")
- case .success(let samples):
- XCTAssertEqual(samples.count, 4)
- }
- addGlucoseSamplesCompletion.fulfill()
- }
- waitForExpectations(timeout: 10)
- let getGlucoseSamplesCompletion = expectation(description: "getGlucoseSamples")
- glucoseStore.getGlucoseSamples() { result in
- switch result {
- case .failure(let error):
- XCTFail("Unexpected failure: \(error)")
- case .success(let samples):
- XCTAssertEqual(samples.count, 3)
- }
- getGlucoseSamplesCompletion.fulfill()
- }
- waitForExpectations(timeout: 10)
- }
- func testPurgeCachedGlucoseObjects() {
- let addGlucoseSamplesCompletion = expectation(description: "addGlucoseSamples")
- glucoseStore.addGlucoseSamples([sample1, sample2, sample3]) { result in
- switch result {
- case .failure(let error):
- XCTFail("Unexpected failure: \(error)")
- case .success(let samples):
- XCTAssertEqual(samples.count, 3)
- }
- addGlucoseSamplesCompletion.fulfill()
- }
- waitForExpectations(timeout: 10)
- let getGlucoseSamples1Completion = expectation(description: "getGlucoseSamples1")
- glucoseStore.getGlucoseSamples() { result in
- switch result {
- case .failure(let error):
- XCTFail("Unexpected failure: \(error)")
- case .success(let samples):
- XCTAssertEqual(samples.count, 3)
- }
- getGlucoseSamples1Completion.fulfill()
- }
- waitForExpectations(timeout: 10)
- let purgeCachedGlucoseObjects1Completion = expectation(description: "purgeCachedGlucoseObjects1")
- glucoseStore.purgeCachedGlucoseObjects(before: Date(timeIntervalSinceNow: -.minutes(5))) { error in
- XCTAssertNil(error)
- purgeCachedGlucoseObjects1Completion.fulfill()
- }
- waitForExpectations(timeout: 10)
- let getGlucoseSamples2Completion = expectation(description: "getGlucoseSamples2")
- glucoseStore.getGlucoseSamples() { result in
- switch result {
- case .failure(let error):
- XCTFail("Unexpected failure: \(error)")
- case .success(let samples):
- XCTAssertEqual(samples.count, 2)
- }
- getGlucoseSamples2Completion.fulfill()
- }
- waitForExpectations(timeout: 10)
- let purgeCachedGlucoseObjects2Completion = expectation(description: "purgeCachedGlucoseObjects2")
- glucoseStore.purgeCachedGlucoseObjects() { error in
- XCTAssertNil(error)
- purgeCachedGlucoseObjects2Completion.fulfill()
- }
- waitForExpectations(timeout: 10)
- let getGlucoseSamples3Completion = expectation(description: "getGlucoseSamples3")
- glucoseStore.getGlucoseSamples() { result in
- switch result {
- case .failure(let error):
- XCTFail("Unexpected failure: \(error)")
- case .success(let samples):
- XCTAssertEqual(samples.count, 0)
- }
- getGlucoseSamples3Completion.fulfill()
- }
- waitForExpectations(timeout: 10)
- }
- func testPurgeCachedGlucoseObjectsNotification() {
- let addGlucoseSamplesCompletion = expectation(description: "addGlucoseSamples")
- glucoseStore.addGlucoseSamples([sample1, sample2, sample3]) { result in
- switch result {
- case .failure(let error):
- XCTFail("Unexpected failure: \(error)")
- case .success(let samples):
- XCTAssertEqual(samples.count, 3)
- }
- addGlucoseSamplesCompletion.fulfill()
- }
- waitForExpectations(timeout: 10)
- delegateCompletion = expectation(description: "delegate")
- let glucoseSamplesDidChangeCompletion = expectation(description: "glucoseSamplesDidChange")
- let observer = NotificationCenter.default.addObserver(forName: GlucoseStore.glucoseSamplesDidChange, object: glucoseStore, queue: nil) { notification in
- glucoseSamplesDidChangeCompletion.fulfill()
- }
- let purgeCachedGlucoseObjectsCompletion = expectation(description: "purgeCachedGlucoseObjects")
- glucoseStore.purgeCachedGlucoseObjects() { error in
- XCTAssertNil(error)
- purgeCachedGlucoseObjectsCompletion.fulfill()
- }
- wait(for: [glucoseSamplesDidChangeCompletion, delegateCompletion!, purgeCachedGlucoseObjectsCompletion], timeout: 10, enforceOrder: true)
- NotificationCenter.default.removeObserver(observer)
- delegateCompletion = nil
- }
- }
- fileprivate func assertEqualSamples(_ storedGlucoseSample: StoredGlucoseSample,
- _ newGlucoseSample: NewGlucoseSample,
- provenanceIdentifier: String = HKSource.default().bundleIdentifier,
- file: StaticString = #file,
- line: UInt = #line) {
- XCTAssertEqual(storedGlucoseSample.provenanceIdentifier, provenanceIdentifier, file: file, line: line)
- XCTAssertEqual(storedGlucoseSample.syncIdentifier, newGlucoseSample.syncIdentifier, file: file, line: line)
- XCTAssertEqual(storedGlucoseSample.syncVersion, newGlucoseSample.syncVersion, file: file, line: line)
- XCTAssertEqual(storedGlucoseSample.startDate, newGlucoseSample.date, file: file, line: line)
- XCTAssertEqual(storedGlucoseSample.quantity, newGlucoseSample.quantity, file: file, line: line)
- XCTAssertEqual(storedGlucoseSample.isDisplayOnly, newGlucoseSample.isDisplayOnly, file: file, line: line)
- XCTAssertEqual(storedGlucoseSample.wasUserEntered, newGlucoseSample.wasUserEntered, file: file, line: line)
- XCTAssertEqual(storedGlucoseSample.device, newGlucoseSample.device, file: file, line: line)
- XCTAssertEqual(storedGlucoseSample.condition, newGlucoseSample.condition, file: file, line: line)
- XCTAssertEqual(storedGlucoseSample.trend, newGlucoseSample.trend, file: file, line: line)
- XCTAssertEqual(storedGlucoseSample.trendRate, newGlucoseSample.trendRate, file: file, line: line)
- }
|