| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351 |
- //
- // DosingDecisionStoreTests.swift
- // LoopKitTests
- //
- // Created by Darin Krauss on 1/6/20.
- // Copyright © 2020 LoopKit Authors. All rights reserved.
- //
- import XCTest
- @testable import LoopKit
- class DosingDecisionStorePersistenceTests: PersistenceControllerTestCase, DosingDecisionStoreDelegate {
- var dosingDecisionStore: DosingDecisionStore!
- override func setUp() {
- super.setUp()
- dosingDecisionStoreHasUpdatedDosingDecisionDataHandler = nil
- dosingDecisionStore = DosingDecisionStore(store: cacheStore, expireAfter: .hours(1))
- dosingDecisionStore.delegate = self
- }
- override func tearDown() {
- dosingDecisionStore.delegate = nil
- dosingDecisionStore = nil
- dosingDecisionStoreHasUpdatedDosingDecisionDataHandler = nil
- super.tearDown()
- }
- // MARK: - DosingDecisionStoreDelegate
- var dosingDecisionStoreHasUpdatedDosingDecisionDataHandler: ((_ : DosingDecisionStore) -> Void)?
- func dosingDecisionStoreHasUpdatedDosingDecisionData(_ dosingDecisionStore: DosingDecisionStore) {
- dosingDecisionStoreHasUpdatedDosingDecisionDataHandler?(dosingDecisionStore)
- }
- // MARK: -
- func testStoreDosingDecisionData() {
- let storeDosingDecisionDataHandler = expectation(description: "Store dosing decision data handler")
- let storeDosingDecisionDataCompletion = expectation(description: "Store dosing decision data completion")
- var handlerInvocation = 0
- dosingDecisionStoreHasUpdatedDosingDecisionDataHandler = { dosingDecisionStore in
- handlerInvocation += 1
- switch handlerInvocation {
- case 1:
- storeDosingDecisionDataHandler.fulfill()
- default:
- XCTFail("Unexpected handler invocation")
- }
- }
- dosingDecisionStore.storeDosingDecisionData(StoredDosingDecisionData()) {
- storeDosingDecisionDataCompletion.fulfill()
- }
- wait(for: [storeDosingDecisionDataHandler, storeDosingDecisionDataCompletion], timeout: 2, enforceOrder: true)
- }
- func testStoreDosingDecisionDataMultiple() {
- let storeDosingDecisionDataHandler1 = expectation(description: "Store dosing decision data handler 1")
- let storeDosingDecisionDataHandler2 = expectation(description: "Store dosing decision data handler 2")
- let storeDosingDecisionDataCompletion1 = expectation(description: "Store dosing decision data completion 1")
- let storeDosingDecisionDataCompletion2 = expectation(description: "Store dosing decision data completion 2")
- var handlerInvocation = 0
- dosingDecisionStoreHasUpdatedDosingDecisionDataHandler = { dosingDecisionStore in
- handlerInvocation += 1
- switch handlerInvocation {
- case 1:
- storeDosingDecisionDataHandler1.fulfill()
- case 2:
- storeDosingDecisionDataHandler2.fulfill()
- default:
- XCTFail("Unexpected handler invocation")
- }
- }
- dosingDecisionStore.storeDosingDecisionData(StoredDosingDecisionData()) {
- storeDosingDecisionDataCompletion1.fulfill()
- }
- dosingDecisionStore.storeDosingDecisionData(StoredDosingDecisionData()) {
- storeDosingDecisionDataCompletion2.fulfill()
- }
- wait(for: [storeDosingDecisionDataHandler1, storeDosingDecisionDataCompletion1, storeDosingDecisionDataHandler2, storeDosingDecisionDataCompletion2], timeout: 2, enforceOrder: true)
- }
- }
- class DosingDecisionStoreQueryAnchorTests: XCTestCase {
- var rawValue: DosingDecisionStore.QueryAnchor.RawValue = [
- "modificationCounter": Int64(123)
- ]
- func testInitializerDefault() {
- let queryAnchor = DosingDecisionStore.QueryAnchor()
- XCTAssertEqual(queryAnchor.modificationCounter, 0)
- }
- func testInitializerRawValue() {
- let queryAnchor = DosingDecisionStore.QueryAnchor(rawValue: rawValue)
- XCTAssertNotNil(queryAnchor)
- XCTAssertEqual(queryAnchor?.modificationCounter, 123)
- }
- func testInitializerRawValueMissingModificationCounter() {
- rawValue["modificationCounter"] = nil
- XCTAssertNil(DosingDecisionStore.QueryAnchor(rawValue: rawValue))
- }
- func testInitializerRawValueInvalidModificationCounter() {
- rawValue["modificationCounter"] = "123"
- XCTAssertNil(DosingDecisionStore.QueryAnchor(rawValue: rawValue))
- }
- func testRawValueWithDefault() {
- let rawValue = DosingDecisionStore.QueryAnchor().rawValue
- XCTAssertEqual(rawValue.count, 1)
- XCTAssertEqual(rawValue["modificationCounter"] as? Int64, Int64(0))
- }
- func testRawValueWithNonDefault() {
- var queryAnchor = DosingDecisionStore.QueryAnchor()
- queryAnchor.modificationCounter = 123
- let rawValue = queryAnchor.rawValue
- XCTAssertEqual(rawValue.count, 1)
- XCTAssertEqual(rawValue["modificationCounter"] as? Int64, Int64(123))
- }
- }
- class DosingDecisionStoreQueryTests: PersistenceControllerTestCase {
- var dosingDecisionStore: DosingDecisionStore!
- var completion: XCTestExpectation!
- var queryAnchor: DosingDecisionStore.QueryAnchor!
- var limit: Int!
- override func setUp() {
- super.setUp()
- dosingDecisionStore = DosingDecisionStore(store: cacheStore, expireAfter: .hours(1))
- completion = expectation(description: "Completion")
- queryAnchor = DosingDecisionStore.QueryAnchor()
- limit = Int.max
- }
- override func tearDown() {
- limit = nil
- queryAnchor = nil
- completion = nil
- dosingDecisionStore = nil
- super.tearDown()
- }
- // MARK: -
- func testEmptyWithDefaultQueryAnchor() {
- dosingDecisionStore.executeDosingDecisionDataQuery(fromQueryAnchor: queryAnchor, limit: limit) { result in
- switch result {
- case .failure(let error):
- XCTFail("Unexpected failure: \(error)")
- case .success(let anchor, let data):
- XCTAssertEqual(anchor.modificationCounter, 0)
- XCTAssertEqual(data.count, 0)
- }
- self.completion.fulfill()
- }
- wait(for: [completion], timeout: 2, enforceOrder: true)
- }
- func testEmptyWithMissingQueryAnchor() {
- queryAnchor = nil
- dosingDecisionStore.executeDosingDecisionDataQuery(fromQueryAnchor: queryAnchor, limit: limit) { result in
- switch result {
- case .failure(let error):
- XCTFail("Unexpected failure: \(error)")
- case .success(let anchor, let data):
- XCTAssertEqual(anchor.modificationCounter, 0)
- XCTAssertEqual(data.count, 0)
- }
- self.completion.fulfill()
- }
- wait(for: [completion], timeout: 2, enforceOrder: true)
- }
- func testEmptyWithNonDefaultQueryAnchor() {
- queryAnchor.modificationCounter = 1
- dosingDecisionStore.executeDosingDecisionDataQuery(fromQueryAnchor: queryAnchor, limit: limit) { result in
- switch result {
- case .failure(let error):
- XCTFail("Unexpected failure: \(error)")
- case .success(let anchor, let data):
- XCTAssertEqual(anchor.modificationCounter, 1)
- XCTAssertEqual(data.count, 0)
- }
- self.completion.fulfill()
- }
- wait(for: [completion], timeout: 2, enforceOrder: true)
- }
- func testDataWithUnusedQueryAnchor() {
- let dataStrings = [generateDataString(), generateDataString(), generateDataString()]
- addData(withDataStrings: dataStrings)
- dosingDecisionStore.executeDosingDecisionDataQuery(fromQueryAnchor: queryAnchor, limit: limit) { result in
- switch result {
- case .failure(let error):
- XCTFail("Unexpected failure: \(error)")
- case .success(let anchor, let data):
- XCTAssertEqual(anchor.modificationCounter, 3)
- XCTAssertEqual(data.count, 3)
- for (index, dataString) in dataStrings.enumerated() {
- XCTAssertEqual(data[index].dataString, dataString)
- }
- }
- self.completion.fulfill()
- }
- wait(for: [completion], timeout: 2, enforceOrder: true)
- }
- func testDataWithStaleQueryAnchor() {
- let dataStrings = [generateDataString(), generateDataString(), generateDataString()]
- addData(withDataStrings: dataStrings)
- queryAnchor.modificationCounter = 2
- dosingDecisionStore.executeDosingDecisionDataQuery(fromQueryAnchor: queryAnchor, limit: limit) { result in
- switch result {
- case .failure(let error):
- XCTFail("Unexpected failure: \(error)")
- case .success(let anchor, let data):
- XCTAssertEqual(anchor.modificationCounter, 3)
- XCTAssertEqual(data.count, 1)
- XCTAssertEqual(data[0].dataString, dataStrings[2])
- }
- self.completion.fulfill()
- }
- wait(for: [completion], timeout: 2, enforceOrder: true)
- }
- func testDataWithCurrentQueryAnchor() {
- let dataStrings = [generateDataString(), generateDataString(), generateDataString()]
- addData(withDataStrings: dataStrings)
- queryAnchor.modificationCounter = 3
- dosingDecisionStore.executeDosingDecisionDataQuery(fromQueryAnchor: queryAnchor, limit: limit) { result in
- switch result {
- case .failure(let error):
- XCTFail("Unexpected failure: \(error)")
- case .success(let anchor, let data):
- XCTAssertEqual(anchor.modificationCounter, 3)
- XCTAssertEqual(data.count, 0)
- }
- self.completion.fulfill()
- }
- wait(for: [completion], timeout: 2, enforceOrder: true)
- }
- func testDataWithLimitZero() {
- let dataStrings = [generateDataString(), generateDataString(), generateDataString()]
- addData(withDataStrings: dataStrings)
- limit = 0
- dosingDecisionStore.executeDosingDecisionDataQuery(fromQueryAnchor: queryAnchor, limit: limit) { result in
- switch result {
- case .failure(let error):
- XCTFail("Unexpected failure: \(error)")
- case .success(let anchor, let data):
- XCTAssertEqual(anchor.modificationCounter, 0)
- XCTAssertEqual(data.count, 0)
- }
- self.completion.fulfill()
- }
- wait(for: [completion], timeout: 2, enforceOrder: true)
- }
- func testDataWithLimitCoveredByData() {
- let dataStrings = [generateDataString(), generateDataString(), generateDataString()]
- addData(withDataStrings: dataStrings)
- limit = 2
- dosingDecisionStore.executeDosingDecisionDataQuery(fromQueryAnchor: queryAnchor, limit: limit) { result in
- switch result {
- case .failure(let error):
- XCTFail("Unexpected failure: \(error)")
- case .success(let anchor, let data):
- XCTAssertEqual(anchor.modificationCounter, 2)
- XCTAssertEqual(data.count, 2)
- XCTAssertEqual(data[0].dataString, dataStrings[0])
- XCTAssertEqual(data[1].dataString, dataStrings[1])
- }
- self.completion.fulfill()
- }
- wait(for: [completion], timeout: 2, enforceOrder: true)
- }
- private func addData(withDataStrings dataStrings: [String]) {
- let semaphore = DispatchSemaphore(value: 0)
- for dataString in dataStrings {
- self.dosingDecisionStore.storeDosingDecisionData(StoredDosingDecisionData(dataString: dataString)) { semaphore.signal() }
- }
- for _ in dataStrings { semaphore.wait() }
- }
- private func generateDataString() -> String {
- return UUID().uuidString
- }
- }
- extension StoredDosingDecisionData {
- init(date: Date = Date(), dataString: String = UUID().uuidString) {
- self.init(date: date, data: dataString.data(using: .utf8)!)
- }
- var dataString: String {
- return String(data: data, encoding: .utf8)!
- }
- }
|