diff --git a/Sources/WhoopDIKit/DependencyModule.swift b/Sources/WhoopDIKit/DependencyModule.swift index fbebbd8..7e33958 100644 --- a/Sources/WhoopDIKit/DependencyModule.swift +++ b/Sources/WhoopDIKit/DependencyModule.swift @@ -1,6 +1,7 @@ import Foundation /// Provides dependencies to the object graph. Modules can be registered with WhoopDI via `WhoopDI.registerModules`. +@MainActor open class DependencyModule { private var dependencies: [DependencyDefinition] = [] diff --git a/Sources/WhoopDIKit/DependencyRegister.swift b/Sources/WhoopDIKit/DependencyRegister.swift index 6618d4f..e7d720c 100644 --- a/Sources/WhoopDIKit/DependencyRegister.swift +++ b/Sources/WhoopDIKit/DependencyRegister.swift @@ -1,3 +1,4 @@ +@MainActor public protocol DependencyRegister { static func removeAllDependencies() static func registerModules(modules: [DependencyModule]) diff --git a/Sources/WhoopDIKit/DependencyTree.swift b/Sources/WhoopDIKit/DependencyTree.swift index 4c07f1d..3550ed7 100644 --- a/Sources/WhoopDIKit/DependencyTree.swift +++ b/Sources/WhoopDIKit/DependencyTree.swift @@ -2,6 +2,7 @@ import Foundation /// Performs a depth first, post ordered search of the given module's dependency tree and flattens the tree into a list of modules /// in which the lowest level modules are ordered first. +@MainActor public final class DependencyTree { private let dependencyModule: [DependencyModule] diff --git a/Sources/WhoopDIKit/WhoopDI.swift b/Sources/WhoopDIKit/WhoopDI.swift index c2026d3..e8d7cb9 100644 --- a/Sources/WhoopDIKit/WhoopDI.swift +++ b/Sources/WhoopDIKit/WhoopDI.swift @@ -1,4 +1,5 @@ import Foundation +@MainActor public final class WhoopDI: DependencyRegister { private static let serviceDict = ServiceDictionary() private static var localServiceDict: ServiceDictionary? = nil diff --git a/Sources/WhoopDIKit/WhoopDIValidator.swift b/Sources/WhoopDIKit/WhoopDIValidator.swift index 3abce74..936e02c 100644 --- a/Sources/WhoopDIKit/WhoopDIValidator.swift +++ b/Sources/WhoopDIKit/WhoopDIValidator.swift @@ -1,4 +1,5 @@ /// Provides verification that the object graph is complete. This is intended to be called from within a test. +@MainActor public final class WhoopDIValidator { private let paramsDict = ServiceDictionary() diff --git a/Sources/WhoopDIKitMacros/InjectableMacro.swift b/Sources/WhoopDIKitMacros/InjectableMacro.swift index 1b2085c..850d702 100644 --- a/Sources/WhoopDIKitMacros/InjectableMacro.swift +++ b/Sources/WhoopDIKitMacros/InjectableMacro.swift @@ -32,11 +32,12 @@ struct InjectableMacro: ExtensionMacro, MemberMacro { return [ /// Adds the static inject function, such as: + /// @MainActor /// public static func inject() -> Self { /// Self.init(myValue: WhoopDI.inject(nil)) /// } """ - + @MainActor \(raw: accessLevel) static func inject() -> Self { Self.init(\(raw: injectingVariables)) } diff --git a/Tests/WhoopDIKitTests/DependencyModuleTests.swift b/Tests/WhoopDIKitTests/DependencyModuleTests.swift index 4cd75de..502ac65 100644 --- a/Tests/WhoopDIKitTests/DependencyModuleTests.swift +++ b/Tests/WhoopDIKitTests/DependencyModuleTests.swift @@ -6,9 +6,9 @@ class DependencyModuleTests: XCTestCase { private let serviceKey = ServiceKey(String.self, name: "name") private let serviceDict = ServiceDictionary() - private let module = DependencyModule() - + @MainActor func test_factory() { + let module = DependencyModule() module.factory(name: "name") { "dependency" } module.addToServiceDictionary(serviceDict: serviceDict) @@ -16,7 +16,9 @@ class DependencyModuleTests: XCTestCase { XCTAssertTrue(defintion is FactoryDefinition) } + @MainActor func test_factoryWithParams() { + let module = DependencyModule() module.factoryWithParams(name: "name") { (_: Any) in "dependency" } module.addToServiceDictionary(serviceDict: serviceDict) @@ -24,7 +26,9 @@ class DependencyModuleTests: XCTestCase { XCTAssertTrue(defintion is FactoryDefinition) } + @MainActor func test_singleton() { + let module = DependencyModule() module.singleton(name: "name") { "dependency" } module.addToServiceDictionary(serviceDict: serviceDict) @@ -32,7 +36,9 @@ class DependencyModuleTests: XCTestCase { XCTAssertTrue(defintion is SingletonDefinition) } + @MainActor func test_singletonWithParams() { + let module = DependencyModule() module.singletonWithParams(name: "name") { (_: Any) in "dependency" } module.addToServiceDictionary(serviceDict: serviceDict) @@ -40,11 +46,13 @@ class DependencyModuleTests: XCTestCase { XCTAssertTrue(defintion is SingletonDefinition) } + @MainActor func test_ServiceKey_Returns_Subclass_Type() { let testModule = TestDependencyModule(testModuleDependencies: []) XCTAssertEqual(testModule.serviceKey, ServiceKey(type(of: TestDependencyModule()))) } + @MainActor func test_SetMultipleModuleDependencies() { let moduleA = DependencyModule() let moduleB = DependencyModule() @@ -55,6 +63,7 @@ class DependencyModuleTests: XCTestCase { XCTAssertEqual(module.moduleDependencies, [moduleD, moduleC, moduleB, moduleA]) } + @MainActor func test_SetSingleModuleDependency() { let moduleA = DependencyModule() @@ -62,6 +71,7 @@ class DependencyModuleTests: XCTestCase { XCTAssertEqual(module.moduleDependencies, [moduleA]) } + @MainActor func test_SetNoModuleDependencies() { let module = TestDependencyModule() XCTAssertEqual(module.moduleDependencies, []) diff --git a/Tests/WhoopDIKitTests/DependencyTreeTests.swift b/Tests/WhoopDIKitTests/DependencyTreeTests.swift index 0375913..46e9796 100644 --- a/Tests/WhoopDIKitTests/DependencyTreeTests.swift +++ b/Tests/WhoopDIKitTests/DependencyTreeTests.swift @@ -2,11 +2,12 @@ import XCTest @testable import WhoopDIKit final class DependencyTreeTests: XCTestCase { - - override func tearDown() { - WhoopDI.removeAllDependencies() + + override func tearDown() async throws { + await WhoopDI.removeAllDependencies() } + @MainActor func testDepthFirstSearch_SingleList() { let moduleD = module("D") let moduleC = module("C", dependentModules: [moduleD]) @@ -17,6 +18,7 @@ final class DependencyTreeTests: XCTestCase { XCTAssertEqual([moduleD, moduleC, moduleB, moduleA], tree.modules) } + @MainActor func testDepthFirstSearch_SingeList_FilterDuplicates() { let moduleDuplicate = module("C") let moduleC = module("C", dependentModules: [moduleDuplicate]) @@ -27,6 +29,7 @@ final class DependencyTreeTests: XCTestCase { XCTAssertEqual([moduleC, moduleB, moduleA], tree.modules) } + @MainActor func testDepthFirstSearch_NoDependencyLoop() { var loopDependency: [DependencyModule] = [] let moduleC = KeyedModule(key: "C", keyedModuleDependencies: loopDependency) @@ -38,6 +41,7 @@ final class DependencyTreeTests: XCTestCase { XCTAssertEqual([moduleC, moduleB, moduleA], tree.modules) } + @MainActor func testDepthFirstSearch_OneTopLevelModule() { let modules = generateTree(rootKey: "A") @@ -58,6 +62,7 @@ final class DependencyTreeTests: XCTestCase { XCTAssertEqual(keys, tree.modules.map { $0.serviceKey.name }) } + @MainActor func testDepthFirstSearch_MultipleTopLevelModules() { let modules = generateTree(rootKey: "A") + generateTree(rootKey: "B") @@ -80,6 +85,7 @@ final class DependencyTreeTests: XCTestCase { XCTAssertEqual(keys, tree.modules.map { $0.serviceKey.name }) } + @MainActor func testDepthFirstSearch_DuplicateTopLevelModules() { let modules = generateTree(rootKey: "A") + generateTree(rootKey: "A") @@ -102,6 +108,7 @@ final class DependencyTreeTests: XCTestCase { } private extension DependencyTreeTests { + @MainActor func generateTree(rootKey: String = "", depth: Int = 0) -> [KeyedModule] { if depth == 3 { return [] } @@ -112,6 +119,7 @@ private extension DependencyTreeTests { } } + @MainActor func module(_ name: String, dependentModules: [DependencyModule] = []) -> KeyedModule { KeyedModule(key: name, keyedModuleDependencies: dependentModules) } diff --git a/Tests/WhoopDIKitTests/InjectableTests.swift b/Tests/WhoopDIKitTests/InjectableTests.swift index 4670850..adec764 100644 --- a/Tests/WhoopDIKitTests/InjectableTests.swift +++ b/Tests/WhoopDIKitTests/InjectableTests.swift @@ -22,6 +22,7 @@ final class InjectableTests: XCTestCase { var newerThing: String { "not again" } let bestThing: Int + @MainActor internal static func inject() -> Self { Self.init(bestThing: WhoopDI.inject("Test")) } @@ -60,6 +61,7 @@ final class InjectableTests: XCTestCase { lazy var lazyVar: Double = 100 let otherStringType: String.Type + @MainActor public static func inject() -> Self { Self.init(bestThing: WhoopDI.inject(nil), otherStringType: WhoopDI.inject(nil)) } @@ -91,6 +93,7 @@ final class InjectableTests: XCTestCase { var newerThing: String { "not again" } var bestThing: Int = 1 + @MainActor private static func inject() -> Self { Self.init(bestThing: WhoopDI.inject(nil)) } @@ -117,6 +120,7 @@ final class InjectableTests: XCTestCase { struct ClosureHolder { let closure: () -> String + @MainActor internal static func inject() -> Self { Self.init(closure: WhoopDI.inject(nil)) } diff --git a/Tests/WhoopDIKitTests/WhoopDITests.swift b/Tests/WhoopDIKitTests/WhoopDITests.swift index e33bf10..0308c5e 100644 --- a/Tests/WhoopDIKitTests/WhoopDITests.swift +++ b/Tests/WhoopDIKitTests/WhoopDITests.swift @@ -3,28 +3,32 @@ import XCTest class WhoopDITests: XCTestCase { - override func tearDown() { - WhoopDI.removeAllDependencies() + override func tearDown() async throws { + await WhoopDI.removeAllDependencies() } + @MainActor func test_inject() { WhoopDI.registerModules(modules: [GoodTestModule()]) let dependency: Dependency = WhoopDI.inject("C_Factory", "param") XCTAssertTrue(dependency is DependencyC) } + @MainActor func test_inject_generic_integer() { WhoopDI.registerModules(modules: [GoodTestModule()]) let dependency: GenericDependency = WhoopDI.inject() XCTAssertEqual(42, dependency.value) } + @MainActor func test_inject_generic_string() { WhoopDI.registerModules(modules: [GoodTestModule()]) let dependency: GenericDependency = WhoopDI.inject() XCTAssertEqual("string", dependency.value) } + @MainActor func test_inject_localDefinition() { WhoopDI.registerModules(modules: [GoodTestModule()]) let dependency: Dependency = WhoopDI.inject("C_Factory") { module in @@ -35,12 +39,14 @@ class WhoopDITests: XCTestCase { XCTAssertTrue(dependency is DependencyA) } + @MainActor func test_inject_localDefinition_noOverride() { WhoopDI.registerModules(modules: [GoodTestModule()]) let dependency: Dependency = WhoopDI.inject("C_Factory", params: "params") { _ in } XCTAssertTrue(dependency is DependencyC) } + @MainActor func test_inject_localDefinition_withParams() { WhoopDI.registerModules(modules: [GoodTestModule()]) let dependency: Dependency = WhoopDI.inject("C_Factory", params: "params") { module in @@ -49,6 +55,7 @@ class WhoopDITests: XCTestCase { XCTAssertTrue(dependency is DependencyB) } + @MainActor func test_validation_fails_barParams() { WhoopDI.registerModules(modules: [GoodTestModule()]) let validator = WhoopDIValidator() @@ -57,6 +64,7 @@ class WhoopDITests: XCTestCase { XCTAssertTrue(failed) } + @MainActor func test_validation_fails_missingDependencies() { WhoopDI.registerModules(modules: [BadTestModule()]) let validator = WhoopDIValidator() @@ -70,6 +78,7 @@ class WhoopDITests: XCTestCase { XCTAssertTrue(failed) } + @MainActor func test_validation_fails_nilFactoryDependency() { WhoopDI.registerModules(modules: [NilFactoryModule()]) let validator = WhoopDIValidator() @@ -83,6 +92,7 @@ class WhoopDITests: XCTestCase { XCTAssertTrue(failed) } + @MainActor func test_validation_fails_nilSingletonDependency() { WhoopDI.registerModules(modules: [NilSingletonModule()]) let validator = WhoopDIValidator() @@ -96,6 +106,7 @@ class WhoopDITests: XCTestCase { XCTAssertTrue(failed) } + @MainActor func test_validation_succeeds() { WhoopDI.registerModules(modules: [GoodTestModule()]) let validator = WhoopDIValidator() @@ -110,6 +121,7 @@ class WhoopDITests: XCTestCase { } } + @MainActor func test_injecting() { WhoopDI.registerModules(modules: [FakeTestModuleForInjecting()]) let testInjecting: TestInjectingThing = WhoopDI.inject()