Skip to content

Commit 3708575

Browse files
committed
Migrated more class hook tests
1 parent ae5f8e8 commit 3708575

5 files changed

Lines changed: 156 additions & 168 deletions

File tree

Tests/InterposeKitTests/ClassHookTests.swift

Lines changed: 89 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,15 @@ import XCTest
33

44
fileprivate class ExampleClass: NSObject {
55
@objc dynamic func doSomething() {}
6-
@objc dynamic var intValue = 1
6+
@objc dynamic var intValue: Int { 1 }
7+
@objc dynamic var arrayValue: [String] { ["ExampleClass"] }
78
}
89

9-
fileprivate class ExampleSubclass: ExampleClass {}
10+
fileprivate class ExampleSubclass: ExampleClass {
11+
override var arrayValue: [String] {
12+
super.arrayValue + ["ExampleSubclass"]
13+
}
14+
}
1015

1116
final class ClassHookTests: InterposeKitTestCase {
1217

@@ -117,6 +122,42 @@ final class ClassHookTests: InterposeKitTestCase {
117122
XCTAssertEqual(hook.state, .pending)
118123
}
119124

125+
func testLifecycle_subclassOverride() throws {
126+
let object = ExampleSubclass()
127+
XCTAssertEqual(object.arrayValue, ["ExampleClass", "ExampleSubclass"])
128+
129+
let superclassHook = try Interpose.applyHook(
130+
on: ExampleClass.self,
131+
for: #selector(getter: ExampleClass.arrayValue),
132+
methodSignature: (@convention(c) (NSObject, Selector) -> [String]).self,
133+
hookSignature: (@convention(block) (NSObject) -> [String]).self
134+
) { hook in
135+
return { `self` in
136+
return hook.original(self, hook.selector) + ["ExampleClass.hook"]
137+
}
138+
}
139+
XCTAssertEqual(object.arrayValue, ["ExampleClass", "ExampleClass.hook", "ExampleSubclass"])
140+
141+
let subclassHook = try Interpose.applyHook(
142+
on: ExampleSubclass.self,
143+
for: #selector(getter: ExampleClass.arrayValue),
144+
methodSignature: (@convention(c) (NSObject, Selector) -> [String]).self,
145+
hookSignature: (@convention(block) (NSObject) -> [String]).self
146+
) { hook in
147+
return { `self` in
148+
return hook.original(self, hook.selector) + ["ExampleSubclass.hook"]
149+
}
150+
}
151+
152+
XCTAssertEqual(object.arrayValue, ["ExampleClass", "ExampleClass.hook", "ExampleSubclass", "ExampleSubclass.hook"])
153+
154+
try superclassHook.revert()
155+
XCTAssertEqual(object.arrayValue, ["ExampleClass", "ExampleSubclass", "ExampleSubclass.hook"])
156+
157+
try subclassHook.revert()
158+
XCTAssertEqual(object.arrayValue, ["ExampleClass", "ExampleSubclass"])
159+
}
160+
120161
func testValidationFailure_methodNotFound() throws {
121162
XCTAssertThrowsError(
122163
try Interpose.prepareHook(
@@ -195,4 +236,50 @@ final class ClassHookTests: InterposeKitTestCase {
195236
)
196237
}
197238

239+
func testCleanup_implementationPreserved() throws {
240+
var deallocated = false
241+
242+
try autoreleasepool {
243+
let tracker = LifetimeTracker { deallocated = true }
244+
245+
try Interpose.applyHook(
246+
on: ExampleClass.self,
247+
for: #selector(ExampleClass.doSomething),
248+
methodSignature: (@convention(c) (NSObject, Selector) -> Void).self,
249+
hookSignature: (@convention(block) (NSObject) -> Void).self
250+
) { hook in
251+
return { `self` in
252+
tracker.keep()
253+
return hook.original(self, hook.selector)
254+
}
255+
}
256+
}
257+
258+
XCTAssertFalse(deallocated)
259+
}
260+
261+
func testCleanup_implementationDeallocated() throws {
262+
var deallocated = false
263+
264+
try autoreleasepool {
265+
let tracker = LifetimeTracker { deallocated = true }
266+
267+
let hook = try Interpose.applyHook(
268+
on: ExampleClass.self,
269+
for: #selector(ExampleClass.doSomething),
270+
methodSignature: (@convention(c) (NSObject, Selector) -> Void).self,
271+
hookSignature: (@convention(block) (NSObject) -> Void).self
272+
) { hook in
273+
return { `self` in
274+
tracker.keep()
275+
return hook.original(self, hook.selector)
276+
}
277+
}
278+
279+
try hook.revert()
280+
}
281+
282+
XCTAssertTrue(deallocated)
283+
}
284+
198285
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
final class LifetimeTracker {
2+
3+
init(onDeinit: @escaping () -> Void) {
4+
self.onDeinit = onDeinit
5+
}
6+
7+
private let onDeinit: () -> Void
8+
9+
func keep() {}
10+
11+
deinit {
12+
self.onDeinit()
13+
}
14+
15+
}

Tests/InterposeKitTests/ToBePolished/InterposeKitTests.swift

Lines changed: 0 additions & 166 deletions
This file was deleted.

Tests/InterposeKitTests/ToBePolished/ObjectInterposeTests.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,4 +247,33 @@ final class ObjectInterposeTests: InterposeKitTestCase {
247247
// try hook.revert()
248248
// XCTAssertEqual(testObj.invert3DTransform(transform), transform.inverted)
249249
// }
250+
251+
func testRevertedCleanup_object() throws {
252+
var deallocated = false
253+
254+
try autoreleasepool {
255+
let tracker = LifetimeTracker {
256+
deallocated = true
257+
}
258+
259+
let object = TestClass()
260+
let hook = try Interpose.applyHook(
261+
on: object,
262+
for: #selector(TestClass.doNothing),
263+
methodSignature: (@convention(c) (NSObject, Selector) -> Void).self,
264+
hookSignature: (@convention(block) (NSObject) -> Void).self
265+
) { hook in
266+
return { `self` in
267+
tracker.keep()
268+
return hook.original(self, hook.selector)
269+
}
270+
}
271+
272+
try hook.revert()
273+
}
274+
275+
// Verify that the block was deallocated
276+
XCTAssertTrue(deallocated)
277+
}
278+
250279
}

Tests/InterposeKitTests/UtilitiesTests.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,27 @@ final class UtilitiesTests: XCTestCase {
5555
XCTAssertFalse(object_isKVOActive(object))
5656
}
5757

58+
func test_impRemoveBlock() {
59+
var deallocated = false
60+
61+
let imp: IMP = autoreleasepool {
62+
let tracker = LifetimeTracker { deallocated = true }
63+
64+
let block: @convention(block) (NSObject) -> Void = { _ in
65+
tracker.keep()
66+
}
67+
68+
return imp_implementationWithBlock(block)
69+
}
70+
71+
// `imp` retains `block` which retains `tracker`.
72+
XCTAssertFalse(deallocated)
73+
74+
// Detach `block` from `imp`, while keeping `imp`.
75+
imp_removeBlock(imp)
76+
77+
// `block` and `tracker` should be deallocated now.
78+
XCTAssertTrue(deallocated)
79+
}
80+
5881
}

0 commit comments

Comments
 (0)