155 lines
4.1 KiB
Swift
155 lines
4.1 KiB
Swift
//
|
|
// Dip
|
|
//
|
|
// Copyright (c) 2015 Olivier Halligon <olivier@halligon.net>
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE.
|
|
//
|
|
|
|
#if canImport(ObjectiveC)
|
|
import XCTest
|
|
@testable import Dip
|
|
|
|
private protocol Server: AnyObject {
|
|
var client: Client! { get set }
|
|
}
|
|
|
|
private protocol Client: AnyObject {
|
|
var server: Server { get }
|
|
}
|
|
|
|
private class ClientImp: Client, Equatable {
|
|
var server: Server
|
|
init(server: Server) {
|
|
self.server = server
|
|
}
|
|
}
|
|
|
|
private func ==<T: ClientImp>(lhs: T, rhs: T) -> Bool {
|
|
return lhs === rhs
|
|
}
|
|
|
|
private class ServerImp: Server, Hashable {
|
|
weak var client: Client!
|
|
init() {}
|
|
|
|
func hash(into hasher: inout Hasher) {
|
|
hasher.combine(ObjectIdentifier(self))
|
|
}
|
|
}
|
|
|
|
private func ==<T: ServerImp>(lhs: T, rhs: T) -> Bool {
|
|
return lhs === rhs
|
|
}
|
|
|
|
private var resolvedServers = Set<ServerImp>()
|
|
private var resolvedClients = Array<ClientImp>()
|
|
|
|
private var container: DependencyContainer!
|
|
|
|
let queue = OperationQueue()
|
|
let lock = RecursiveLock()
|
|
|
|
private let resolveClientSync: () -> Client? = {
|
|
var client: Client?
|
|
DispatchQueue.global(qos: .default).sync() {
|
|
client = try! container.resolve() as Client
|
|
}
|
|
return client
|
|
}
|
|
|
|
let resolveServerAsync = {
|
|
let server = try! container.resolve() as Server
|
|
lock.lock()
|
|
resolvedServers.insert(server as! ServerImp)
|
|
lock.unlock()
|
|
}
|
|
|
|
let resolveClientAsync = {
|
|
let client = try! container.resolve() as Client
|
|
lock.lock()
|
|
resolvedClients.append(client as! ClientImp)
|
|
lock.unlock()
|
|
}
|
|
|
|
class ThreadSafetyTests: XCTestCase {
|
|
|
|
override func setUp() {
|
|
Dip.logLevel = .Verbose
|
|
container = DependencyContainer()
|
|
}
|
|
|
|
override func tearDown() {
|
|
resolvedServers.removeAll()
|
|
resolvedClients.removeAll()
|
|
}
|
|
|
|
func testSingletonThreadSafety() {
|
|
container.register(.singleton) { ServerImp() as Server }
|
|
|
|
for _ in 0..<100 {
|
|
queue.addOperation(resolveServerAsync)
|
|
}
|
|
|
|
queue.waitUntilAllOperationsAreFinished()
|
|
|
|
XCTAssertEqual(resolvedServers.count, 1, "Should create only one instance")
|
|
}
|
|
|
|
|
|
func testFactoryThreadSafety() {
|
|
container.register { ServerImp() as Server }
|
|
|
|
for _ in 0..<100 {
|
|
queue.addOperation(resolveServerAsync)
|
|
}
|
|
|
|
queue.waitUntilAllOperationsAreFinished()
|
|
|
|
XCTAssertEqual(resolvedServers.count, 100, "All instances should be different")
|
|
}
|
|
|
|
|
|
func testCircularReferenceThreadSafety() {
|
|
container.register {
|
|
ClientImp(server: try container.resolve()) as Client
|
|
}
|
|
|
|
container.register { ServerImp() as Server }
|
|
.resolvingProperties { container, server in
|
|
server.client = resolveClientSync()
|
|
}
|
|
|
|
for _ in 0..<100 {
|
|
queue.addOperation(resolveClientAsync)
|
|
}
|
|
|
|
queue.waitUntilAllOperationsAreFinished()
|
|
|
|
XCTAssertEqual(resolvedClients.count, 100, "Instances should be not reused in different object graphs")
|
|
for client in resolvedClients {
|
|
let service = client.server as! ServerImp
|
|
let serviceClient = service.client as! ClientImp
|
|
XCTAssertEqual(serviceClient, client, "Instances should be reused when resolving single object graph")
|
|
}
|
|
}
|
|
|
|
}
|
|
#endif
|