Javier Cicchelli 9bcdaa697b [Setup] Basic project structure (#1)
This PR contains all the work related to setting up this project as required to implement the [Assignment](https://repo.rock-n-code.com/rock-n-code/deep-linking-assignment/wiki/Assignment) on top, as intended.

To summarise this work:
- [x] created a new **Xcode** project;
- [x] cloned the `Wikipedia` app and inserted it into the **Xcode** project;
- [x] created the `Locations` app and also, its `Libraries` package;
- [x] created the `Shared` package to share dependencies between the apps;
- [x] added a `Makefile` file and implemented some **environment** and **help** commands.

Co-authored-by: Javier Cicchelli <javier@rock-n-code.com>
Reviewed-on: rock-n-code/deep-linking-assignment#1
2023-04-08 18:37:13 +00:00

183 lines
8.0 KiB
Swift

import Foundation
import UIKit
import XCTest
class CollectionAsyncMapTests: XCTestCase {
override func setUp() {
super.setUp()
}
override func tearDown() {
super.tearDown()
}
func testAsyncMapResultsAreMappedToCorrectItemsEvenIfReceivedOutOfOrder() {
let expectation = XCTestExpectation(description: "maps results received out of order correctly")
let inputItems = ["THIS", "THAT", "OTHER"]
let expectedOutputItems = ["THIS result", "THAT result", "OTHER result"]
let asyncItemTransformer = { (item: String, completion: @escaping (String) -> Void) in
// fake out a process which takes 'item' and asynchronously gets a result for it
DispatchQueue.global(qos: .default).asyncAfter(deadline: .now() + .milliseconds(Int.random(in: 0 ... 500))) { // use random delay to ensure results map to correct items even if results are received out of (collection) order
let resultForItem = "\(item) result"
completion(resultForItem)
}
}
let completionHandler: ([String]) -> Void = { results in
XCTAssertEqual(results, expectedOutputItems)
expectation.fulfill()
}
inputItems.asyncMap(asyncItemTransformer, completion: completionHandler)
wait(for:[expectation], timeout: 5, enforceOrder: true)
}
func testAsyncCompactMapResultsAreMappedToCorrectItemsEvenIfReceivedOutOfOrder() {
let expectation = XCTestExpectation(description: "maps results received out of order correctly (even when compacting)")
let inputItems = ["THIS", "MAP_TO_NIL", "THAT", "MAP_TO_NIL", "OTHER"]
let expectedOutputItems = ["THIS result", "THAT result", "OTHER result"]
let asyncItemTransformer = { (item: String, completion: @escaping (String?) -> Void) in
// fake out a process which takes 'item' and asynchronously gets a result for it
DispatchQueue.global(qos: .default).asyncAfter(deadline: .now() + .milliseconds(Int.random(in: 0 ... 500))) { // use random delay to ensure results map to correct items even if results are received out of (collection) order
guard item != "MAP_TO_NIL" else { // fake out getting nil for some items
completion(nil)
return
}
let resultForItem = "\(item) result"
completion(resultForItem)
}
}
let completionHandler: ([String]) -> Void = { results in
XCTAssertEqual(results, expectedOutputItems)
expectation.fulfill()
}
inputItems.asyncCompactMap(asyncItemTransformer, completion: completionHandler)
wait(for:[expectation], timeout: 5, enforceOrder: true)
}
func testAsyncForEachPerformsBlockForEachItem() {
let expectation = XCTestExpectation(description: "block performed for each object")
let inputItems = ["THIS", "THAT", "OTHER"]
// use Sets because `asyncForEach` does no mapping, so order isn't important
let expectedResults = Set(arrayLiteral: "THIS", "OTHER", "THAT")
var results:Set<String> = []
let semaphore = DispatchSemaphore(value: 1)
let asyncItemBlock = { (item: String, completion: @escaping () -> Void) in
// fake out a process which takes 'item' and asynchronously performs a block with it
DispatchQueue.global(qos: .default).asyncAfter(deadline: .now() + .milliseconds(Int.random(in: 0 ... 500))) { // use random delay to more closely simulate read async usage
semaphore.wait()
results.insert(item)
semaphore.signal()
completion()
}
}
let completionHandler: () -> Void = {
XCTAssertEqual(results, expectedResults)
expectation.fulfill()
}
inputItems.asyncForEach(asyncItemBlock, completion: completionHandler)
wait(for:[expectation], timeout: 5, enforceOrder: true)
}
func testAsyncMapToDictionary() {
let expectation = XCTestExpectation(description: "wait for notification")
let input = ["A", "B", "C", "D", "E", "F", "G", "H"]
let expectedOutput = ["A":"a", "B":"b", "C":"c", "D":"d", "E":"e", "F":"f", "G":"g", "H":"h"]
input.asyncMapToDictionary(block: { (input, completion) in
DispatchQueue.global(qos: .default).asyncAfter(deadline: .now() + .milliseconds(Int.random(in: 0 ... 500))) {
completion(input, input.lowercased())
}
}, queue: DispatchQueue.main) { (output) in
XCTAssert(Thread.isMainThread)
XCTAssertEqual(output, expectedOutput, "Result of asyncMapToDictionary not the same as the expected")
expectation.fulfill()
}
wait(for:[expectation], timeout: 5, enforceOrder: true)
}
func testAsyncMapToDictionaryWithLargeInput() {
let expectation = XCTestExpectation(description: "wait for notification")
let count = 10000
var input = [Int]()
input.reserveCapacity(count)
var expectedOutput = [Int:Int]()
expectedOutput.reserveCapacity(count)
let transform = { (i: Int) in return i * i }
for i in 1...count {
input.append(i)
expectedOutput[i] = transform(i)
}
input.asyncMapToDictionary(block: { (input, completion) in
DispatchQueue.global(qos: .default).asyncAfter(deadline: .now() + .milliseconds(Int.random(in: 0 ... 500))) {
completion(input, transform(input))
}
}, queue: DispatchQueue.main) { (output) in
XCTAssert(Thread.isMainThread)
XCTAssertEqual(output, expectedOutput, "Result of asyncMapToDictionary not the same as the expected")
expectation.fulfill()
}
wait(for:[expectation], timeout: 5, enforceOrder: true)
}
func testLargeAsyncMap() {
let randomDelayBlock: (String, @escaping (String) -> Void) -> Void = { (string, completion) in
let randomMillisecondDelay = DispatchTimeInterval.milliseconds(Int.random(in: 1...10))
let time = DispatchTime.now() + randomMillisecondDelay
DispatchQueue.global(qos: .default).asyncAfter(deadline: time) {
completion(string)
}
}
let count = 1000
var identifiers: [String] = []
identifiers.reserveCapacity(count)
for _ in 1...count {
identifiers.append(UUID().uuidString)
}
let expectation = XCTestExpectation(description: "wait for completion")
identifiers.asyncMap(randomDelayBlock) { (processedIdentifiers) in
XCTAssert(processedIdentifiers == identifiers)
expectation.fulfill()
}
wait(for:[expectation], timeout: 5, enforceOrder: true)
}
func testLargeAsyncCompactMap() {
let randomDelayBlock: (String, @escaping (String?) -> Void) -> Void = { (string, completion) in
let randomMillisecondDelay = DispatchTimeInterval.milliseconds(Int.random(in: 1...10))
let time = DispatchTime.now() + randomMillisecondDelay
DispatchQueue.global(qos: .default).asyncAfter(deadline: time) {
completion(string.hasPrefix("A") ? nil : string)
}
}
let count = 1000
var identifiers: [String] = []
identifiers.reserveCapacity(count)
for _ in 1...count {
identifiers.append(UUID().uuidString)
}
let expectation = XCTestExpectation(description: "wait for completion")
identifiers.asyncCompactMap(randomDelayBlock) { (processedIdentifiers) in
XCTAssert(processedIdentifiers.filter { $0.hasPrefix("A") }.isEmpty)
expectation.fulfill()
}
wait(for:[expectation], timeout: 5, enforceOrder: true)
}
}