Repository navigation
Expand file tree
/
Copy pathEscapingClosuresTest.java
More file actions
226 lines (181 loc) · 8.25 KB
/
Copy pathEscapingClosuresTest.java
File metadata and controls
226 lines (181 loc) · 8.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2024 Apple Inc. and the Swift.org project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift.org project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
package com.example.swift;
import org.junit.jupiter.api.Test;
import org.swift.swiftkit.core.SwiftArena;
import java.util.OptionalLong;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import static org.junit.jupiter.api.Assertions.*;
public class EscapingClosuresTest {
@Test
void testCallbackManager_singleCallback() {
// snippet.escapingClosureUsageJava
try (var arena = SwiftArena.ofConfined()) {
CallbackManager manager = CallbackManager.init(arena);
AtomicBoolean wasCalled = new AtomicBoolean(false);
// Create an escaping closure (no try-with-resources needed - cleanup is automatic via Swift ARC)
CallbackManager.setCallback.callback callback = () -> {
wasCalled.set(true);
};
// Set the callback
manager.setCallback(callback);
// Trigger it
manager.triggerCallback();
assertTrue(wasCalled.get(), "Callback should have been called");
// Trigger again to ensure it's still stored
wasCalled.set(false);
manager.triggerCallback();
assertTrue(wasCalled.get(), "Callback should be called multiple times");
// Clear the callback - this releases the closure on Swift side, triggering GlobalRef cleanup
manager.clearCallback();
}
// snippet.end
}
@Test
void testCallbackManager_intCallback() {
try (var arena = SwiftArena.ofConfined()) {
CallbackManager manager = CallbackManager.init(arena);
CallbackManager.setIntCallback.callback callback = (value) -> {
return value * 2;
};
manager.setIntCallback(callback);
// Trigger the callback - returns OptionalLong since Swift returns Int64?
OptionalLong result = manager.triggerIntCallback(21);
assertTrue(result.isPresent(), "Result should be present");
assertEquals(42, result.getAsLong(), "Callback should double the input");
}
}
@Test
void testCallbackManager_sendableCallback() {
try (var arena = SwiftArena.ofConfined()) {
CallbackManager manager = CallbackManager.init(arena);
// Verify that the generated interface is annotated with @ThreadSafe
assertTrue(
CallbackManager.setSendableCallback.callback.class.isAnnotationPresent(
org.swift.swiftkit.core.annotations.ThreadSafe.class
),
"Sendable callback interface should be annotated with @ThreadSafe"
);
CallbackManager.setSendableCallback.callback callback = (value) -> {
return value * 3;
};
manager.setSendableCallback(callback);
OptionalLong result = manager.triggerSendableCallback(14);
assertTrue(result.isPresent(), "Result should be present");
assertEquals(42, result.getAsLong(), "Sendable callback should multiply input by 3");
}
}
@Test
void testCallbackManager_sendableCallbackConcurrently() throws Exception {
var arena = SwiftArena.ofAuto();
CallbackManager manager = CallbackManager.init(arena);
CallbackManager.setSendableCallback.callback callback = (value) -> {
return value * 10;
};
manager.setSendableCallback(callback);
int threadCount = 4;
var executor = java.util.concurrent.Executors.newFixedThreadPool(threadCount);
var futures = new java.util.ArrayList<java.util.concurrent.Future<Long>>();
for (int i = 0; i < 20; i++) {
final long input = i;
futures.add(executor.submit(() -> {
OptionalLong res = manager.triggerSendableCallback(input);
assertTrue(res.isPresent());
return res.getAsLong();
}));
}
for (int i = 0; i < 20; i++) {
assertEquals(i * 10L, futures.get(i).get());
}
executor.shutdown();
assertTrue(executor.awaitTermination(5, java.util.concurrent.TimeUnit.SECONDS));
}
@Test
void testClosureStore() {
try (var arena = SwiftArena.ofConfined()) {
ClosureStore store = ClosureStore.init(arena);
AtomicLong counter = new AtomicLong(0);
// Add multiple closures
ClosureStore.addClosure.closure closure1 = () -> {
counter.incrementAndGet();
};
ClosureStore.addClosure.closure closure2 = () -> {
counter.addAndGet(10);
};
ClosureStore.addClosure.closure closure3 = () -> {
counter.addAndGet(100);
};
store.addClosure(closure1);
store.addClosure(closure2);
store.addClosure(closure3);
assertEquals(3, store.count(), "Should have 3 closures stored");
// Execute all closures
store.executeAll();
assertEquals(111, counter.get(), "All closures should be executed");
// Execute again
counter.set(0);
store.executeAll();
assertEquals(111, counter.get(), "Closures should be reusable");
// Clear - this releases closures on Swift side, triggering GlobalRef cleanup
store.clear();
assertEquals(0, store.count(), "Store should be empty after clear");
}
}
@Test
void testMultipleEscapingClosures() {
AtomicLong successValue = new AtomicLong(0);
AtomicLong failureValue = new AtomicLong(0);
MySwiftLibrary.multipleEscapingClosures.onSuccess onSuccess = (value) -> {
successValue.set(value);
};
MySwiftLibrary.multipleEscapingClosures.onFailure onFailure = (value) -> {
failureValue.set(value);
};
// Test success case
MySwiftLibrary.multipleEscapingClosures(onSuccess, onFailure, true);
assertEquals(42, successValue.get(), "Success callback should be called");
assertEquals(0, failureValue.get(), "Failure callback should not be called");
// Reset and test failure case
successValue.set(0);
failureValue.set(0);
MySwiftLibrary.multipleEscapingClosures(onSuccess, onFailure, false);
assertEquals(0, successValue.get(), "Success callback should not be called");
assertEquals(-1, failureValue.get(), "Failure callback should be called");
}
@Test
void testMultipleManagersWithDifferentClosures() {
try (var arena = SwiftArena.ofConfined()) {
CallbackManager manager1 = CallbackManager.init(arena);
CallbackManager manager2 = CallbackManager.init(arena);
AtomicBoolean called1 = new AtomicBoolean(false);
AtomicBoolean called2 = new AtomicBoolean(false);
CallbackManager.setCallback.callback callback1 = () -> {
called1.set(true);
};
CallbackManager.setCallback.callback callback2 = () -> {
called2.set(true);
};
manager1.setCallback(callback1);
manager2.setCallback(callback2);
// Trigger first manager
manager1.triggerCallback();
assertTrue(called1.get(), "First callback should be called");
assertFalse(called2.get(), "Second callback should not be called");
// Trigger second manager
manager2.triggerCallback();
assertTrue(called2.get(), "Second callback should be called");
}
}
}