Files
iOS/Tests/Shared/DiskCache.test.swift
Zac West 4992c97b49 "Open page" widget (#1863)
## Summary
Adds to Shortcuts and Widgets the ability to open a particular page/panel in Lovelace.

## Screenshots
| State | 1 | 2 |
| -- | -- | -- |
| Empty | ![Simulator Screen Shot - iPad Pro (12 9-inch) (5th generation) - 2021-09-27 at 22 26 11](https://user-images.githubusercontent.com/74188/135033260-a54ac04e-e2dd-4da7-a497-b04d5297db8b.png) | ![Simulator Screen Shot - iPad Pro (12 9-inch) (5th generation) - 2021-09-27 at 22 26 57](https://user-images.githubusercontent.com/74188/135033274-d7dee9da-48f6-4219-8e47-c128746e26dc.png) |
| Contents | ![Simulator Screen Shot - iPad Pro (12 9-inch) (5th generation) - 2021-09-27 at 22 53 26](https://user-images.githubusercontent.com/74188/135033294-52953ad1-c73c-49ea-bc7b-919ce5c35068.png) | ![Simulator Screen Shot - iPad Pro (12 9-inch) (5th generation) - 2021-09-27 at 22 53 33](https://user-images.githubusercontent.com/74188/135033305-2d6c2e93-9ef0-485c-a533-e6ee34e17bfa.png) |
| Shortcuts | ![Simulator Screen Shot - iPad Pro (12 9-inch) (5th generation) - 2021-09-27 at 23 17 58](https://user-images.githubusercontent.com/74188/135033520-d5a848ca-c4f5-41aa-bba8-a27b9c80489a.png) | ![Simulator Screen Shot - iPad Pro (12 9-inch) (5th generation) - 2021-09-27 at 23 18 06](https://user-images.githubusercontent.com/74188/135033531-a2102fdc-c834-4994-bc4d-c97d7af93342.png) |

## Any other notes
A good starting point for widgets which need cached information. This one is simple: 1 request that is a small JSON payload that needs to be saved. This can start to get more complex later.

- Adds an on-disk cache backed by Codable, which we use to cache panels in the widget (for data) and in the app (to see if we need to reload the widget).
- Fixes some issues and crashes with opening the action of a widget multiple times in a row.
- Adds support for grabbing frontend localizations from Lokalise and uses them for panel localization.
2021-09-27 23:24:06 -07:00

185 lines
5.9 KiB
Swift

import PromiseKit
@testable import Shared
import XCTest
struct TestObject1: Codable, Equatable {
var uuid1 = UUID()
}
struct TestObject2: Codable, Equatable {
var uuid2 = UUID()
}
struct DoubleObject1: Codable, Equatable {
var value: Double
}
class DiskCacheTests: XCTestCase {
var cache: DiskCacheImpl!
override func setUp() {
super.setUp()
cache = DiskCacheImpl(containerName: "UnitTests_\(UUID().uuidString)")
}
func testReadNothing() {
XCTAssertThrowsError(try hang(cache.value(for: "missing_value")) as TestObject1)
XCTAssertThrowsError(try hang(cache.value(for: "missing_value2")) as TestObject2)
XCTAssertThrowsError(try hang(cache.value(for: "missing_value2")) as TestObject1)
}
func testReadAfterWrite() {
let obj1 = TestObject1()
let obj2 = TestObject1()
let obj3 = TestObject1()
let cache: DiskCache = cache
let key = "key"
let write1 = cache.set(obj1, for: key)
let read1 = write1.then { cache.value(for: key) as Promise<TestObject1> }
let write2 = read1.then { _ in cache.set(obj2, for: key) }
let read2 = write2.then { cache.value(for: key) as Promise<TestObject1> }
let write3 = read2.then { _ in cache.set(obj3, for: key) }
let read3 = write3.then { cache.value(for: key) as Promise<TestObject1> }
XCTAssertNoThrow(try hang(write1))
XCTAssertEqual(try hang(read1), obj1)
XCTAssertNoThrow(try hang(write2))
XCTAssertEqual(try hang(read2), obj2)
XCTAssertNoThrow(try hang(write3))
XCTAssertEqual(try hang(read3), obj3)
}
func testInterleaveObjectType() {
let obj1 = TestObject1()
let obj2 = TestObject2()
let obj3 = TestObject1()
let cache: DiskCache = cache
let key = "key"
let write1 = cache.set(obj1, for: key)
let read1 = write1.then { cache.value(for: key) as Promise<TestObject1> }
let write2 = read1.then { _ in cache.set(obj2, for: key) }
let read2a = write2.then { cache.value(for: key) as Promise<TestObject1> }
let read2b = when(resolved: read2a).then { _ in cache.value(for: key) as Promise<TestObject2> }
let write3 = read2b.then { _ in cache.set(obj3, for: key) }
let read3 = write3.then { cache.value(for: key) as Promise<TestObject1> }
XCTAssertNoThrow(try hang(write1))
XCTAssertEqual(try hang(read1), obj1)
XCTAssertNoThrow(try hang(write2))
XCTAssertThrowsError(try hang(read2a))
XCTAssertEqual(try hang(read2b), obj2)
XCTAssertNoThrow(try hang(write3))
XCTAssertEqual(try hang(read3), obj3)
}
func testMultipleKeys() {
let obj1 = TestObject1()
let obj2 = TestObject2()
let obj3 = TestObject1()
let obj4 = TestObject2()
let cache: DiskCache = cache
let write1 = cache.set(obj1, for: "obj1")
let write2 = cache.set(obj2, for: "obj2")
let write3 = cache.set(obj3, for: "obj3")
let write4 = cache.set(obj4, for: "obj4")
let read1 = write1.then { cache.value(for: "obj1") as Promise<TestObject1> }
let read2 = write2.then { cache.value(for: "obj2") as Promise<TestObject2> }
let read3 = write3.then { cache.value(for: "obj3") as Promise<TestObject1> }
let read4 = write4.then { cache.value(for: "obj4") as Promise<TestObject2> }
XCTAssertEqual(try hang(read1), obj1)
XCTAssertEqual(try hang(read2), obj2)
XCTAssertEqual(try hang(read3), obj3)
XCTAssertEqual(try hang(read4), obj4)
}
func testJsonEncoderFail() {
let obj1 = DoubleObject1(value: 3)
let obj2 = DoubleObject1(value: .infinity)
let cache: DiskCache = cache
let write1 = cache.set(obj1, for: "obj1/")
let read1 = write1.then { cache.value(for: "obj1/") as Promise<DoubleObject1> }
let write2 = cache.set(obj2, for: "obj2")
let read2 = when(resolved: write2).then { _ in cache.value(for: "obj2") as Promise<DoubleObject1> }
XCTAssertEqual(try hang(read1), obj1)
XCTAssertThrowsError(try hang(write2))
XCTAssertThrowsError(try hang(read2))
}
func testInvalidFileNameKey() {
let obj1 = TestObject1()
let cache: DiskCache = cache
let write1 = cache.set(obj1, for: "a//")
let read1 = write1.then { cache.value(for: "a//") as Promise<TestObject1> }
XCTAssertEqual(try hang(read1), obj1)
}
func testWriteFailure() {
let obj1 = TestObject1()
cache.container = URL(fileURLWithPath: "/dev/null")
let write1 = cache.set(obj1, for: "key")
XCTAssertThrowsError(try hang(write1))
}
func testCoordinatorError() {
let obj1 = TestObject1()
let cache: DiskCacheImpl = cache
// uses a fake coordinator because presenters do not work in the simulator
cache.coordinator = FailingNSFileCoordinator()
let write1 = cache.set(obj1, for: "key")
let read1 = when(resolved: write1).then { _ in cache.value(for: "key") as Promise<TestObject1> }
XCTAssertThrowsError(try hang(write1))
XCTAssertThrowsError(try hang(read1))
}
}
class FailingNSFileCoordinator: NSFileCoordinator {
enum TestError: Error {
case any
}
init() {
super.init(filePresenter: nil)
}
override func coordinate(
readingItemAt url: URL,
options: NSFileCoordinator.ReadingOptions = [],
error outError: NSErrorPointer,
byAccessor reader: (URL) -> Void
) {
outError?.pointee = TestError.any as NSError
}
override func coordinate(
writingItemAt url: URL,
options: NSFileCoordinator.WritingOptions = [],
error outError: NSErrorPointer,
byAccessor writer: (URL) -> Void
) {
outError?.pointee = TestError.any as NSError
}
}