Skip to content

Commit

Permalink
Merge pull request #31 from rock88/master
Browse files Browse the repository at this point in the history
Handle unbehaviour singleton duplication in incorrect dependencies graph
  • Loading branch information
alexwillrock authored Feb 7, 2022
2 parents 0c07da2 + cdfb91e commit 9811707
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 3 deletions.
4 changes: 4 additions & 0 deletions EasyDi.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
objects = {

/* Begin PBXBuildFile section */
283175422612173700C09104 /* Test_SingletonDuplication.swift in Sources */ = {isa = PBXBuildFile; fileRef = 283175412612173700C09104 /* Test_SingletonDuplication.swift */; };
5C88753F236FD22500019260 /* Test_CrossAssemblyInjections_WeakSingletonCycle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C88753E236FD22500019260 /* Test_CrossAssemblyInjections_WeakSingletonCycle.swift */; };
5C887540236FD22500019260 /* Test_CrossAssemblyInjections_WeakSingletonCycle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C88753E236FD22500019260 /* Test_CrossAssemblyInjections_WeakSingletonCycle.swift */; };
8BEE13521F9A27C800A02331 /* EasyDi.h in Headers */ = {isa = PBXBuildFile; fileRef = 8BEE13501F9A27C800A02331 /* EasyDi.h */; settings = {ATTRIBUTES = (Public, ); }; };
Expand Down Expand Up @@ -54,6 +55,7 @@
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
283175412612173700C09104 /* Test_SingletonDuplication.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Test_SingletonDuplication.swift; sourceTree = "<group>"; };
5C88753E236FD22500019260 /* Test_CrossAssemblyInjections_WeakSingletonCycle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Test_CrossAssemblyInjections_WeakSingletonCycle.swift; sourceTree = "<group>"; };
8BEE13501F9A27C800A02331 /* EasyDi.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EasyDi.h; sourceTree = "<group>"; };
8BEE13511F9A27C800A02331 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
Expand Down Expand Up @@ -144,6 +146,7 @@
C3614B4A1F1C8B5F00B1F4A1 /* Test_Scope.swift */,
C3614B4B1F1C8B5F00B1F4A1 /* Test_StructsInjection.swift */,
A5ABB84221A5522400C96320 /* Test_Threadsafety.swift */,
283175412612173700C09104 /* Test_SingletonDuplication.swift */,
);
path = Tests;
sourceTree = "<group>";
Expand Down Expand Up @@ -367,6 +370,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
283175422612173700C09104 /* Test_SingletonDuplication.swift in Sources */,
C3614B571F1C8B6800B1F4A1 /* Test_Patches.swift in Sources */,
C3614B551F1C8B6800B1F4A1 /* Test_CrossAssemblyInjections.swift in Sources */,
D2B16C972123116500CF69E8 /* Test_ImplicitlyUnwrappedOptional.swift in Sources */,
Expand Down
19 changes: 16 additions & 3 deletions Sources/EasyDi.swift
Original file line number Diff line number Diff line change
Expand Up @@ -417,8 +417,21 @@ open class Assembly: AssemblyInternal {
}

// And save singletons
if context.singletons[key] == nil, scope == .lazySingleton {
context.singletons[key] = result
if scope == .lazySingleton {
if context.singletons[key] == nil {
context.singletons[key] = result
} else {
let current = context.singletons[key] as! ObjectType

if type(of: current) is AnyClass {
if unsafeBitCast(current, to: Int.self) != unsafeBitCast(result, to: Int.self) {
let reason = "Singleton already exist, inspect your dependencies graph"
NSException(name: .internalInconsistencyException, reason: reason, userInfo: nil).raise()
}
} else {
// Skip value types
}
}
}

if context.weakSingletons[key] == nil, scope == .weakSingleton {
Expand Down Expand Up @@ -459,7 +472,7 @@ public final class Definition<ObjectType: InjectableObject>: DefinitionInternal
func injectObject(object: InjectableObject) -> InjectableObject {

guard let injectableObject = object as? ObjectType else {
fatalError()
fatalError("Failed to build result object. Expected \(ObjectType.self) received: \(object)")
}

guard let actualInjectClosure = self.injectClosure else {
Expand Down
71 changes: 71 additions & 0 deletions Tests/Test_SingletonDuplication.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//
// Test_SingletoneDuplication.swift
// EasyDi-iOS-Tests
//
// Created by Andrey Konoplyankin on 29.03.2021.
// Copyright © 2021 AndreyZarembo. All rights reserved.
//

import XCTest
@testable import EasyDi

fileprivate class SomeObjectA {
let objectB: SomeObjectB

init(objectB: SomeObjectB) {
self.objectB = objectB
}
}

fileprivate class SomeObjectB {
var objectA: SomeObjectA?
}

fileprivate class TestAssembly: Assembly {
var objectA: SomeObjectA {
return define(scope: .lazySingleton, init: SomeObjectA(objectB: self.objectB))
}

var objectB: SomeObjectB {
return define(scope: .lazySingleton, init: SomeObjectB()) {
$0.objectA = self.objectA
return $0
}
}
}

final class Test_SingletonDuplication: XCTestCase {
func testSingletonDuplication() {
NSException.test_swizzleRaise()

let context = DIContext()
let assembly = TestAssembly.instance(from: context)

let _ = assembly.objectA
let _ = assembly.objectB

XCTAssertEqual(NSException.last?.reason, "Singleton already exist, inspect your dependencies graph")
NSException.last = nil
}
}

extension NSException {
static var last: NSException?
static var alreadySwizzled = false

static func test_swizzleRaise() {
guard !alreadySwizzled else { return }

let origin = class_getInstanceMethod(NSException.self, NSSelectorFromString("raise"))
let new = class_getInstanceMethod(NSException.self, NSSelectorFromString("test_raise"))

if let origin = origin, let new = new {
method_exchangeImplementations(origin, new)
alreadySwizzled = true
}
}

@objc func test_raise() {
NSException.last = self
}
}

0 comments on commit 9811707

Please sign in to comment.