@@ -3,10 +3,15 @@ import XCTest
33
44fileprivate 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
1116final 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}
0 commit comments