@@ -2,7 +2,7 @@ import JavaScriptKit
22import XCTest
33
44final class StressTests : XCTestCase {
5-
5+
66 func testJSObjectMemoryExhaustion( ) async throws {
77 guard let gc = JSObject . global. gc. function else {
88 throw XCTSkip ( " Missing --expose-gc flag " )
@@ -13,22 +13,22 @@ final class StressTests: XCTestCase {
1313 let maxIterations = 25_000
1414 var objects : [ JSObject ] = [ ]
1515 var lastSuccessfulCount = 0
16-
16+
1717 do {
1818 for i in 0 ..< maxIterations {
1919 let obj = JSObject ( )
2020 // Add properties to increase memory pressure
2121 obj [ " index " ] = JSValue . number ( Double ( i) )
22- obj [ " data " ] = JSValue . string ( String ( repeating: " x " , count: 1000 ) ) // 1KB string per object
23-
22+ obj [ " data " ] = JSValue . string ( String ( repeating: " x " , count: 1000 ) ) // 1KB string per object
23+
2424 // Create nested objects to stress the reference graph
2525 let nested = JSObject ( )
26- nested [ " parent_ref " ] = obj. jsValue // Circular reference
26+ nested [ " parent_ref " ] = obj. jsValue // Circular reference
2727 obj [ " nested " ] = nested. jsValue
28-
28+
2929 objects. append ( obj)
3030 lastSuccessfulCount = i
31-
31+
3232 // Aggressive GC every 1000 objects to test cleanup under pressure
3333 if i % 1000 == 0 {
3434 gc ( )
@@ -39,33 +39,33 @@ final class StressTests: XCTestCase {
3939 // Expected to eventually fail due to memory pressure
4040 print ( " JSObject stress test stopped at \( lastSuccessfulCount) objects: \( error) " )
4141 }
42-
42+
4343 // Verify objects are still accessible after memory pressure
4444 let sampleCount = min ( 1000 , objects. count)
4545 for i in 0 ..< sampleCount {
4646 XCTAssertEqual ( objects [ i] [ " index " ] , JSValue . number ( Double ( i) ) )
4747 XCTAssertNotNil ( objects [ i] [ " nested " ] . object)
4848 }
49-
49+
5050 // Force cleanup
5151 objects. removeAll ( )
5252 for _ in 0 ..< 20 {
5353 gc ( )
5454 try await Task . sleep ( for: . milliseconds( 10 ) )
5555 }
5656 }
57-
57+
5858 func testJSClosureMemoryPressureWithoutFinalizationRegistry( ) async throws {
5959 guard let gc = JSObject . global. gc. function else {
6060 throw XCTSkip ( " Missing --expose-gc flag " )
6161 }
6262
63- // Test heavy closure allocation to stress Swift heap management
63+ // Test heavy closure allocation to stress Swift heap management
6464 // Focus on scenarios where FinalizationRegistry is not used
6565 let maxClosures = 15_000
6666 var closures : [ JSClosure ] = [ ]
6767 var successCount = 0
68-
68+
6969 do {
7070 for i in 0 ..< maxClosures {
7171 // Create closures that capture significant data
@@ -75,14 +75,14 @@ final class StressTests: XCTestCase {
7575 let result = capturedData. count + Int( arguments. first? . number ?? 0 )
7676 return JSValue . number ( Double ( result) )
7777 }
78-
78+
7979 closures. append ( closure)
8080 successCount = i + 1
81-
81+
8282 // Test closure immediately to ensure it works under memory pressure
8383 let result = closure ( [ JSValue . number ( 10 ) ] )
84- XCTAssertEqual ( result. number, 110.0 ) // 100 (capturedData.count) + 10
85-
84+ XCTAssertEqual ( result. number, 110.0 ) // 100 (capturedData.count) + 10
85+
8686 // More frequent GC to stress the system
8787 if i % 500 == 0 {
8888 gc ( )
@@ -92,27 +92,27 @@ final class StressTests: XCTestCase {
9292 } catch {
9393 print ( " JSClosure stress test stopped at \( successCount) closures: \( error) " )
9494 }
95-
95+
9696 // Test random closures still work after extreme memory pressure
9797 for _ in 0 ..< min ( 100 , closures. count) {
9898 let randomIndex = Int . random ( in: 0 ..< closures. count)
9999 let result = closures [ randomIndex] ( [ JSValue . number ( 5 ) ] )
100- XCTAssertTrue ( result. number! > 5 ) // Should be 5 + capturedData.count (100+)
100+ XCTAssertTrue ( result. number! > 5 ) // Should be 5 + capturedData.count (100+)
101101 }
102-
102+
103103 #if JAVASCRIPTKIT_WITHOUT_WEAKREFS
104104 for closure in closures {
105105 closure. release ( )
106106 }
107107 #endif
108-
108+
109109 closures. removeAll ( )
110110 for _ in 0 ..< 20 {
111111 gc ( )
112112 try await Task . sleep ( for: . milliseconds( 10 ) )
113113 }
114114 }
115-
115+
116116 func testMixedAllocationMemoryBoundaries( ) async throws {
117117 guard let gc = JSObject . global. gc. function else {
118118 throw XCTSkip ( " Missing --expose-gc flag " )
@@ -122,33 +122,35 @@ final class StressTests: XCTestCase {
122122 let cycles = 200
123123 var totalObjects = 0
124124 var totalClosures = 0
125-
125+
126126 for cycle in 0 ..< cycles {
127127 var cycleObjects : [ JSObject ] = [ ]
128128 var cycleClosure : [ JSClosure ] = [ ]
129-
129+
130130 // Exponentially increase allocation pressure each cycle
131131 let objectsThisCycle = min ( 100 + cycle, 1000 )
132132 let closuresThisCycle = min ( 50 + cycle / 2 , 500 )
133-
133+
134134 do {
135135 // Allocate objects
136136 for i in 0 ..< objectsThisCycle {
137137 let obj = JSObject ( )
138138 // Create memory-intensive properties
139- obj [ " large_array " ] = JSObject . global. Array. function!. from!(
140- ( 0 ..< 1000 ) . map { JSValue . number ( Double ( $0) ) } . jsValue
141- ) . jsValue
142- obj [ " metadata " ] = [
143- " cycle " : cycle,
144- " index " : i,
145- " timestamp " : Int ( Date ( ) . timeIntervalSince1970)
146- ] . jsValue
147-
139+ obj [ " large_array " ] =
140+ JSObject . global. Array. function!. from!(
141+ ( 0 ..< 1000 ) . map { JSValue . number ( Double ( $0) ) } . jsValue
142+ ) . jsValue
143+ obj [ " metadata " ] =
144+ [
145+ " cycle " : cycle,
146+ " index " : i,
147+ " timestamp " : Int ( Date ( ) . timeIntervalSince1970) ,
148+ ] . jsValue
149+
148150 cycleObjects. append ( obj)
149151 totalObjects += 1
150152 }
151-
153+
152154 // Allocate closures with increasing complexity
153155 for i in 0 ..< closuresThisCycle {
154156 let heavyData = String ( repeating: " data " , count: cycle + 100 )
@@ -159,13 +161,13 @@ final class StressTests: XCTestCase {
159161 cycleClosure. append ( closure)
160162 totalClosures += 1
161163 }
162-
164+
163165 } catch {
164166 print ( " Memory boundary reached at cycle \( cycle) : \( error) " )
165167 print ( " Total objects created: \( totalObjects) , closures: \( totalClosures) " )
166168 break
167169 }
168-
170+
169171 // Test system still works under extreme pressure
170172 if !cycleObjects. isEmpty {
171173 XCTAssertNotNil ( cycleObjects [ 0 ] [ " large_array " ] . object)
@@ -174,16 +176,16 @@ final class StressTests: XCTestCase {
174176 let result = cycleClosure [ 0 ] ( arguments: [ ] )
175177 XCTAssertNotNil ( result. string)
176178 }
177-
179+
178180 #if JAVASCRIPTKIT_WITHOUT_WEAKREFS
179181 for closure in cycleClosure {
180182 closure. release ( )
181183 }
182184 #endif
183-
185+
184186 cycleObjects. removeAll ( )
185187 cycleClosure. removeAll ( )
186-
188+
187189 // Aggressive cleanup every 10 cycles
188190 if cycle % 10 == 0 {
189191 for _ in 0 ..< 10 {
@@ -192,10 +194,10 @@ final class StressTests: XCTestCase {
192194 }
193195 }
194196 }
195-
197+
196198 print ( " Stress test completed: \( totalObjects) objects, \( totalClosures) closures allocated " )
197199 }
198-
200+
199201 func testHeapFragmentationRecovery( ) async throws {
200202 guard let gc = JSObject . global. gc. function else {
201203 throw XCTSkip ( " Missing --expose-gc flag " )
@@ -204,16 +206,16 @@ final class StressTests: XCTestCase {
204206 // Test system recovery from heap fragmentation by creating/destroying
205207 // patterns that stress the memory allocator
206208 let fragmentationCycles = 100
207-
209+
208210 for cycle in 0 ..< fragmentationCycles {
209211 var shortLivedObjects : [ JSObject ] = [ ]
210212 var longLivedObjects : [ JSObject ] = [ ]
211-
213+
212214 // Create fragmentation pattern: many short-lived, few long-lived
213215 for i in 0 ..< 1000 {
214216 let obj = JSObject ( )
215217 obj [ " data " ] = JSValue . string ( String ( repeating: " fragment " , count: 100 ) )
216-
218+
217219 if i % 10 == 0 {
218220 // Long-lived objects
219221 longLivedObjects. append ( obj)
@@ -222,32 +224,32 @@ final class StressTests: XCTestCase {
222224 shortLivedObjects. append ( obj)
223225 }
224226 }
225-
227+
226228 // Immediately release short-lived objects to create fragmentation
227229 shortLivedObjects. removeAll ( )
228-
230+
229231 // Force GC to reclaim fragmented memory
230232 for _ in 0 ..< 5 {
231233 gc ( )
232234 try await Task . sleep ( for: . milliseconds( 1 ) )
233235 }
234-
236+
235237 // Test system can still allocate efficiently after fragmentation
236238 var recoveryTest : [ JSObject ] = [ ]
237239 for i in 0 ..< 500 {
238240 let obj = JSObject ( )
239241 obj [ " recovery_test " ] = JSValue . number ( Double ( i) )
240242 recoveryTest. append ( obj)
241243 }
242-
244+
243245 // Verify recovery objects work correctly
244246 for (i, obj) in recoveryTest. enumerated ( ) {
245247 XCTAssertEqual ( obj [ " recovery_test " ] , JSValue . number ( Double ( i) ) )
246248 }
247-
249+
248250 recoveryTest. removeAll ( )
249251 longLivedObjects. removeAll ( )
250-
252+
251253 if cycle % 20 == 0 {
252254 for _ in 0 ..< 10 {
253255 gc ( )
@@ -256,4 +258,4 @@ final class StressTests: XCTestCase {
256258 }
257259 }
258260 }
259- }
261+ }
0 commit comments