Skip to content

Commit 511ebc5

Browse files
Split out JSClosure tests from JavaScriptKitTests.swift
1 parent 7b2edbe commit 511ebc5

File tree

3 files changed

+198
-84
lines changed

3 files changed

+198
-84
lines changed
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import JavaScriptKit
2+
import XCTest
3+
4+
class JSClosureAsyncTests: XCTestCase {
5+
@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
6+
final class AnyTaskExecutor: TaskExecutor {
7+
func enqueue(_ job: UnownedJob) {
8+
job.runSynchronously(on: asUnownedTaskExecutor())
9+
}
10+
}
11+
12+
final class UnsafeSendableBox<T>: @unchecked Sendable {
13+
var value: T
14+
init(_ value: T) {
15+
self.value = value
16+
}
17+
}
18+
19+
func testAsyncClosure() async throws {
20+
let closure = JSClosure.async { _ in
21+
return (42.0).jsValue
22+
}.jsValue
23+
let result = try await JSPromise(from: closure.function!())!.value()
24+
XCTAssertEqual(result, 42.0)
25+
}
26+
27+
func testAsyncClosureWithPriority() async throws {
28+
let priority = UnsafeSendableBox<TaskPriority?>(nil)
29+
let closure = JSClosure.async(priority: .high) { _ in
30+
priority.value = Task.currentPriority
31+
return (42.0).jsValue
32+
}.jsValue
33+
let result = try await JSPromise(from: closure.function!())!.value()
34+
XCTAssertEqual(result, 42.0)
35+
XCTAssertEqual(priority.value, .high)
36+
}
37+
38+
@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
39+
func testAsyncClosureWithTaskExecutor() async throws {
40+
let executor = AnyTaskExecutor()
41+
let closure = JSClosure.async(executorPreference: executor) { _ in
42+
return (42.0).jsValue
43+
}.jsValue
44+
let result = try await JSPromise(from: closure.function!())!.value()
45+
XCTAssertEqual(result, 42.0)
46+
}
47+
48+
@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
49+
func testAsyncClosureWithTaskExecutorPreference() async throws {
50+
let executor = AnyTaskExecutor()
51+
let priority = UnsafeSendableBox<TaskPriority?>(nil)
52+
let closure = JSClosure.async(executorPreference: executor, priority: .high) { _ in
53+
priority.value = Task.currentPriority
54+
return (42.0).jsValue
55+
}.jsValue
56+
let result = try await JSPromise(from: closure.function!())!.value()
57+
XCTAssertEqual(result, 42.0)
58+
XCTAssertEqual(priority.value, .high)
59+
}
60+
61+
// TODO: Enable the following tests once:
62+
// - Make JSObject a final-class
63+
// - Unify JSFunction and JSObject into JSValue
64+
// - Make JS(Oneshot)Closure as a wrapper of JSObject, not a subclass
65+
/*
66+
func testAsyncOneshotClosure() async throws {
67+
let closure = JSOneshotClosure.async { _ in
68+
return (42.0).jsValue
69+
}.jsValue
70+
let result = try await JSPromise(
71+
from: closure.function!()
72+
)!.value()
73+
XCTAssertEqual(result, 42.0)
74+
}
75+
76+
func testAsyncOneshotClosureWithPriority() async throws {
77+
let priority = UnsafeSendableBox<TaskPriority?>(nil)
78+
let closure = JSOneshotClosure.async(priority: .high) { _ in
79+
priority.value = Task.currentPriority
80+
return (42.0).jsValue
81+
}.jsValue
82+
let result = try await JSPromise(from: closure.function!())!.value()
83+
XCTAssertEqual(result, 42.0)
84+
XCTAssertEqual(priority.value, .high)
85+
}
86+
87+
@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
88+
func testAsyncOneshotClosureWithTaskExecutor() async throws {
89+
let executor = AnyTaskExecutor()
90+
let closure = JSOneshotClosure.async(executorPreference: executor) { _ in
91+
return (42.0).jsValue
92+
}.jsValue
93+
let result = try await JSPromise(from: closure.function!())!.value()
94+
XCTAssertEqual(result, 42.0)
95+
}
96+
97+
@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
98+
func testAsyncOneshotClosureWithTaskExecutorPreference() async throws {
99+
let executor = AnyTaskExecutor()
100+
let priority = UnsafeSendableBox<TaskPriority?>(nil)
101+
let closure = JSOneshotClosure.async(executorPreference: executor, priority: .high) { _ in
102+
priority.value = Task.currentPriority
103+
return (42.0).jsValue
104+
}.jsValue
105+
let result = try await JSPromise(from: closure.function!())!.value()
106+
XCTAssertEqual(result, 42.0)
107+
XCTAssertEqual(priority.value, .high)
108+
}
109+
*/
110+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import JavaScriptKit
2+
import XCTest
3+
4+
class JSClosureTests: XCTestCase {
5+
func testClosureLifetime() {
6+
let evalClosure = JSObject.global.globalObject1.eval_closure.function!
7+
8+
do {
9+
let c1 = JSClosure { arguments in
10+
return arguments[0]
11+
}
12+
XCTAssertEqual(evalClosure(c1, JSValue.number(1.0)), .number(1.0))
13+
#if JAVASCRIPTKIT_WITHOUT_WEAKREFS
14+
c1.release()
15+
#endif
16+
}
17+
18+
do {
19+
let array = JSObject.global.Array.function!.new()
20+
let c1 = JSClosure { _ in .number(3) }
21+
_ = array.push!(c1)
22+
XCTAssertEqual(array[0].function!().number, 3.0)
23+
#if JAVASCRIPTKIT_WITHOUT_WEAKREFS
24+
c1.release()
25+
#endif
26+
}
27+
28+
do {
29+
let c1 = JSClosure { _ in .undefined }
30+
XCTAssertEqual(c1(), .undefined)
31+
}
32+
33+
do {
34+
let c1 = JSClosure { _ in .number(4) }
35+
XCTAssertEqual(c1(), .number(4))
36+
}
37+
}
38+
39+
func testHostFunctionRegistration() {
40+
// ```js
41+
// global.globalObject1 = {
42+
// ...
43+
// "prop_6": {
44+
// "call_host_1": function() {
45+
// return global.globalObject1.prop_6.host_func_1()
46+
// }
47+
// }
48+
// }
49+
// ```
50+
let globalObject1 = getJSValue(this: .global, name: "globalObject1")
51+
let globalObject1Ref = try! XCTUnwrap(globalObject1.object)
52+
let prop_6 = getJSValue(this: globalObject1Ref, name: "prop_6")
53+
let prop_6Ref = try! XCTUnwrap(prop_6.object)
54+
55+
var isHostFunc1Called = false
56+
let hostFunc1 = JSClosure { (_) -> JSValue in
57+
isHostFunc1Called = true
58+
return .number(1)
59+
}
60+
61+
setJSValue(this: prop_6Ref, name: "host_func_1", value: .object(hostFunc1))
62+
63+
let call_host_1 = getJSValue(this: prop_6Ref, name: "call_host_1")
64+
let call_host_1Func = try! XCTUnwrap(call_host_1.function)
65+
XCTAssertEqual(call_host_1Func(), .number(1))
66+
XCTAssertEqual(isHostFunc1Called, true)
67+
68+
#if JAVASCRIPTKIT_WITHOUT_WEAKREFS
69+
hostFunc1.release()
70+
#endif
71+
72+
let evalClosure = JSObject.global.globalObject1.eval_closure.function!
73+
let hostFunc2 = JSClosure { (arguments) -> JSValue in
74+
if let input = arguments[0].number {
75+
return .number(input * 2)
76+
} else {
77+
return .string(String(describing: arguments[0]))
78+
}
79+
}
80+
81+
XCTAssertEqual(evalClosure(hostFunc2, 3), .number(6))
82+
XCTAssertTrue(evalClosure(hostFunc2, true).string != nil)
83+
84+
#if JAVASCRIPTKIT_WITHOUT_WEAKREFS
85+
hostFunc2.release()
86+
#endif
87+
}
88+
}

Tests/JavaScriptKitTests/JavaScriptKitTests.swift

Lines changed: 0 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -197,90 +197,6 @@ class JavaScriptKitTests: XCTestCase {
197197
XCTAssertEqual(func6(true, "OK", 2), .string("OK"))
198198
}
199199

200-
func testClosureLifetime() {
201-
let evalClosure = JSObject.global.globalObject1.eval_closure.function!
202-
203-
do {
204-
let c1 = JSClosure { arguments in
205-
return arguments[0]
206-
}
207-
XCTAssertEqual(evalClosure(c1, JSValue.number(1.0)), .number(1.0))
208-
#if JAVASCRIPTKIT_WITHOUT_WEAKREFS
209-
c1.release()
210-
#endif
211-
}
212-
213-
do {
214-
let array = JSObject.global.Array.function!.new()
215-
let c1 = JSClosure { _ in .number(3) }
216-
_ = array.push!(c1)
217-
XCTAssertEqual(array[0].function!().number, 3.0)
218-
#if JAVASCRIPTKIT_WITHOUT_WEAKREFS
219-
c1.release()
220-
#endif
221-
}
222-
223-
do {
224-
let c1 = JSClosure { _ in .undefined }
225-
XCTAssertEqual(c1(), .undefined)
226-
}
227-
228-
do {
229-
let c1 = JSClosure { _ in .number(4) }
230-
XCTAssertEqual(c1(), .number(4))
231-
}
232-
}
233-
234-
func testHostFunctionRegistration() {
235-
// ```js
236-
// global.globalObject1 = {
237-
// ...
238-
// "prop_6": {
239-
// "call_host_1": function() {
240-
// return global.globalObject1.prop_6.host_func_1()
241-
// }
242-
// }
243-
// }
244-
// ```
245-
let globalObject1 = getJSValue(this: .global, name: "globalObject1")
246-
let globalObject1Ref = try! XCTUnwrap(globalObject1.object)
247-
let prop_6 = getJSValue(this: globalObject1Ref, name: "prop_6")
248-
let prop_6Ref = try! XCTUnwrap(prop_6.object)
249-
250-
var isHostFunc1Called = false
251-
let hostFunc1 = JSClosure { (_) -> JSValue in
252-
isHostFunc1Called = true
253-
return .number(1)
254-
}
255-
256-
setJSValue(this: prop_6Ref, name: "host_func_1", value: .object(hostFunc1))
257-
258-
let call_host_1 = getJSValue(this: prop_6Ref, name: "call_host_1")
259-
let call_host_1Func = try! XCTUnwrap(call_host_1.function)
260-
XCTAssertEqual(call_host_1Func(), .number(1))
261-
XCTAssertEqual(isHostFunc1Called, true)
262-
263-
#if JAVASCRIPTKIT_WITHOUT_WEAKREFS
264-
hostFunc1.release()
265-
#endif
266-
267-
let evalClosure = JSObject.global.globalObject1.eval_closure.function!
268-
let hostFunc2 = JSClosure { (arguments) -> JSValue in
269-
if let input = arguments[0].number {
270-
return .number(input * 2)
271-
} else {
272-
return .string(String(describing: arguments[0]))
273-
}
274-
}
275-
276-
XCTAssertEqual(evalClosure(hostFunc2, 3), .number(6))
277-
XCTAssertTrue(evalClosure(hostFunc2, true).string != nil)
278-
279-
#if JAVASCRIPTKIT_WITHOUT_WEAKREFS
280-
hostFunc2.release()
281-
#endif
282-
}
283-
284200
func testNewObjectConstruction() {
285201
// ```js
286202
// global.Animal = function(name, age, isCat) {

0 commit comments

Comments
 (0)