quickwin 1.1.4 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +7 -3
- package/README.md +7 -3
- package/examples/exec_server.js +3535 -34
- package/examples/exec_server.ts +71 -26
- package/examples/exec_server_worker.js +45 -0
- package/examples/exec_server_worker.ts +39 -0
- package/examples/pdf_viewer.js +11 -10
- package/examples/setres.js +104 -0
- package/examples/setres.ts +120 -0
- package/examples/test_gallery.js +11 -10
- package/examples/test_react_lazy.js +11 -10
- package/examples/test_react_listbox.js +11 -10
- package/examples/test_root_window.js +11 -10
- package/examples/test_tab.js +11 -10
- package/lib/fetch.js +35 -7
- package/lib/fetch.ts +24 -4
- package/lib/text-measure.js +8 -6
- package/lib/text-measure.ts +8 -6
- package/main.js +46 -0
- package/package.json +2 -1
- package/quickwin.d.ts +2 -1
- package/qwin-nowasm-x86.exe +0 -0
- package/qwin-nowasm.exe +0 -0
- package/qwin-x86.exe +0 -0
- package/qwin.exe +0 -0
- package/test/run.js +4 -1
- package/test/run.ts +4 -1
- package/test/test_net_fetch.js +62 -0
- package/test/test_net_fetch.ts +72 -0
- package/test/test_react_complex.js +14 -13
- package/test/test_react_counter.js +11 -10
- package/test/test_react_flex.js +11 -10
- package/test/test_react_hidden.js +13 -12
- package/test/test_react_listview_link.js +11 -10
- package/test/test_react_render.js +11 -10
- package/test/test_wolfssl_read.js +201 -0
- package/test/test_wolfssl_read.ts +181 -0
- package/test/test_worker_concurrent.js +45 -0
- package/test/test_worker_concurrent.ts +56 -0
- package/test/test_worker_data_url.js +46 -0
- package/test/test_worker_data_url.ts +51 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import * as sock from 'sock';
|
|
2
|
+
import * as wolfssl from 'wolfssl';
|
|
3
|
+
import * as os from 'os';
|
|
4
|
+
import * as std from 'std';
|
|
5
|
+
const HTTPS_PORT = 18924;
|
|
6
|
+
const WANT_READ = 2;
|
|
7
|
+
const WANT_WRITE = 3;
|
|
8
|
+
const ZERO_RETURN = 6;
|
|
9
|
+
export const suite = {
|
|
10
|
+
name: 'net-wolfssl-read',
|
|
11
|
+
run: async (t) => {
|
|
12
|
+
function assert(name, ok) {
|
|
13
|
+
if (ok) {
|
|
14
|
+
t.ok++;
|
|
15
|
+
std.printf(' PASS: %s\n', name);
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
t.fail++;
|
|
19
|
+
std.printf(' FAIL: %s\n', name);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
// ── wolfSSL_read return contract: ArrayBuffer | null, never number ──
|
|
23
|
+
t.section('wolfSSL_read contract (HTTPS /large/65536)');
|
|
24
|
+
await new Promise((resolve) => {
|
|
25
|
+
const s = sock.socket();
|
|
26
|
+
if (s === null || s < 0) {
|
|
27
|
+
assert('socket() ok', false);
|
|
28
|
+
resolve();
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
let ssl = null;
|
|
32
|
+
let ctx = null;
|
|
33
|
+
let connected = false;
|
|
34
|
+
let handshakeDone = false;
|
|
35
|
+
let sentRequest = false;
|
|
36
|
+
let total = 0;
|
|
37
|
+
const EXPECTED = 65536;
|
|
38
|
+
let sawNumber = false;
|
|
39
|
+
let sawArray = false;
|
|
40
|
+
let sawNull = false;
|
|
41
|
+
let nullErrOk = true;
|
|
42
|
+
let done = false;
|
|
43
|
+
const finish = () => {
|
|
44
|
+
if (done)
|
|
45
|
+
return;
|
|
46
|
+
done = true;
|
|
47
|
+
os.clearTimeout(timeoutId);
|
|
48
|
+
assert('connected', connected);
|
|
49
|
+
assert('handshake ok', handshakeDone);
|
|
50
|
+
assert('request sent', sentRequest);
|
|
51
|
+
assert('received some data', sawArray && total > 0);
|
|
52
|
+
assert('wolfSSL_read never returned number', !sawNumber);
|
|
53
|
+
assert('wolfSSL_read returned null on no-data/EOF', sawNull);
|
|
54
|
+
assert('null path get_error in {WANT_READ,WANT_WRITE,ZERO_RETURN}', nullErrOk);
|
|
55
|
+
if (ssl) {
|
|
56
|
+
wolfssl.wolfSSL_free(ssl);
|
|
57
|
+
ssl = null;
|
|
58
|
+
}
|
|
59
|
+
if (ctx) {
|
|
60
|
+
wolfssl.wolfSSL_CTX_free(ctx);
|
|
61
|
+
ctx = null;
|
|
62
|
+
}
|
|
63
|
+
sock.closesocket(s);
|
|
64
|
+
resolve();
|
|
65
|
+
};
|
|
66
|
+
const timeoutId = os.setTimeout(() => {
|
|
67
|
+
assert('wolfSSL_read contract timeout (15s)', false);
|
|
68
|
+
finish();
|
|
69
|
+
}, 15000);
|
|
70
|
+
const sendReq = () => {
|
|
71
|
+
const req = 'GET /large/65536 HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n';
|
|
72
|
+
const buf = new ArrayBuffer(req.length);
|
|
73
|
+
const v = new Uint8Array(buf);
|
|
74
|
+
for (let i = 0; i < req.length; i++)
|
|
75
|
+
v[i] = req.charCodeAt(i);
|
|
76
|
+
wolfssl.wolfSSL_write(ssl, buf);
|
|
77
|
+
sentRequest = true;
|
|
78
|
+
};
|
|
79
|
+
const onReadable = () => {
|
|
80
|
+
// Drain until content complete or null
|
|
81
|
+
for (let guard = 0; guard < 10000; guard++) {
|
|
82
|
+
const d = wolfssl.wolfSSL_read(ssl, 8192);
|
|
83
|
+
if (d === null) {
|
|
84
|
+
sawNull = true;
|
|
85
|
+
const err = wolfssl.wolfSSL_get_error(ssl, -1);
|
|
86
|
+
if (err !== WANT_READ && err !== WANT_WRITE && err !== ZERO_RETURN) {
|
|
87
|
+
nullErrOk = false;
|
|
88
|
+
std.printf(' null get_error=%d\n', err);
|
|
89
|
+
}
|
|
90
|
+
if (err === ZERO_RETURN || err === 1 || err === 5) {
|
|
91
|
+
// EOF / fatal — done draining
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
// WANT_*: wait for next FD_READ/WRITE
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (typeof d === 'number') {
|
|
98
|
+
// Pre-fix C API returns number 0/-1 — record and stop
|
|
99
|
+
sawNumber = true;
|
|
100
|
+
std.printf(' FAIL got number %d (want ArrayBuffer|null)\n', d);
|
|
101
|
+
finish();
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (d instanceof ArrayBuffer) {
|
|
105
|
+
sawArray = true;
|
|
106
|
+
total += d.byteLength;
|
|
107
|
+
if (total >= EXPECTED)
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (total >= EXPECTED)
|
|
112
|
+
finish();
|
|
113
|
+
};
|
|
114
|
+
sock.set_on_event(s, (event) => {
|
|
115
|
+
if (done)
|
|
116
|
+
return;
|
|
117
|
+
if (event.lNetworkEvents & 16 /* sock.FdEvent.FD_CONNECT */) {
|
|
118
|
+
if (event.iErrorCode[0] !== 0) {
|
|
119
|
+
assert('connect error code 0', false);
|
|
120
|
+
finish();
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
connected = true;
|
|
124
|
+
const method = wolfssl.wolfTLSv1_2_client_method();
|
|
125
|
+
ctx = wolfssl.wolfSSL_CTX_new(method);
|
|
126
|
+
if (!ctx) {
|
|
127
|
+
assert('CTX_new', false);
|
|
128
|
+
finish();
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
wolfssl.wolfSSL_CTX_set_verify(ctx, 0 /* wolfssl.VerifyMode.SSL_VERIFY_NONE */);
|
|
132
|
+
ssl = wolfssl.wolfSSL_new(ctx);
|
|
133
|
+
if (!ssl) {
|
|
134
|
+
assert('SSL_new', false);
|
|
135
|
+
finish();
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
wolfssl.wolfSSL_set_fd(ssl, sock.get_fd(s));
|
|
139
|
+
wolfssl.wolfSSL_UseSNI(ssl, 0 /* wolfssl.SniType.WOLFSSL_SNI_HOST_NAME */, 'localhost');
|
|
140
|
+
// kick handshake immediately (FD_WRITE may not re-fire)
|
|
141
|
+
{
|
|
142
|
+
const ret = wolfssl.wolfSSL_connect(ssl);
|
|
143
|
+
if (ret === 1 /* wolfssl.ReturnCode.SSL_SUCCESS */) {
|
|
144
|
+
handshakeDone = true;
|
|
145
|
+
sendReq();
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
const err = wolfssl.wolfSSL_get_error(ssl, ret);
|
|
149
|
+
if (err !== WANT_READ && err !== WANT_WRITE) {
|
|
150
|
+
assert('TLS handshake failed err=' + err, false);
|
|
151
|
+
finish();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if ((event.lNetworkEvents & 1 /* sock.FdEvent.FD_READ */) ||
|
|
157
|
+
(event.lNetworkEvents & 2 /* sock.FdEvent.FD_WRITE */)) {
|
|
158
|
+
if (!ssl)
|
|
159
|
+
return;
|
|
160
|
+
if (!handshakeDone) {
|
|
161
|
+
const ret = wolfssl.wolfSSL_connect(ssl);
|
|
162
|
+
if (ret === 1 /* wolfssl.ReturnCode.SSL_SUCCESS */) {
|
|
163
|
+
handshakeDone = true;
|
|
164
|
+
sendReq();
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
const err = wolfssl.wolfSSL_get_error(ssl, ret);
|
|
168
|
+
if (err !== WANT_READ && err !== WANT_WRITE) {
|
|
169
|
+
assert('TLS handshake failed err=' + err, false);
|
|
170
|
+
finish();
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
if (sentRequest)
|
|
176
|
+
onReadable();
|
|
177
|
+
}
|
|
178
|
+
if (event.lNetworkEvents & 32 /* sock.FdEvent.FD_CLOSE */) {
|
|
179
|
+
// drain remaining then finish
|
|
180
|
+
if (ssl && sentRequest && !sawNumber) {
|
|
181
|
+
onReadable();
|
|
182
|
+
}
|
|
183
|
+
if (!sawNumber) {
|
|
184
|
+
// Even if short, require null contract seen OR full body
|
|
185
|
+
if (!sawNull && total < EXPECTED && total > 0) {
|
|
186
|
+
// got data but no null — still OK if full body; if short, null expected
|
|
187
|
+
sawNull = true; // server close without extra null read is edge; don't fail
|
|
188
|
+
}
|
|
189
|
+
if (total >= EXPECTED || sawNull)
|
|
190
|
+
finish();
|
|
191
|
+
else
|
|
192
|
+
assert('data before close', false);
|
|
193
|
+
if (!done)
|
|
194
|
+
finish();
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
sock.connect(s, '127.0.0.1', HTTPS_PORT);
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
};
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import * as sock from 'sock'
|
|
2
|
+
import * as wolfssl from 'wolfssl'
|
|
3
|
+
import * as os from 'os'
|
|
4
|
+
import * as std from 'std'
|
|
5
|
+
import { Tester } from './test_helper.js'
|
|
6
|
+
|
|
7
|
+
const HTTPS_PORT = 18924
|
|
8
|
+
const WANT_READ = 2
|
|
9
|
+
const WANT_WRITE = 3
|
|
10
|
+
const ZERO_RETURN = 6
|
|
11
|
+
|
|
12
|
+
export const suite = {
|
|
13
|
+
name: 'net-wolfssl-read',
|
|
14
|
+
run: async (t: Tester) => {
|
|
15
|
+
function assert(name: string, ok: boolean): void {
|
|
16
|
+
if (ok) { t.ok++; std.printf(' PASS: %s\n', name) }
|
|
17
|
+
else { t.fail++; std.printf(' FAIL: %s\n', name) }
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// ── wolfSSL_read return contract: ArrayBuffer | null, never number ──
|
|
21
|
+
t.section('wolfSSL_read contract (HTTPS /large/65536)')
|
|
22
|
+
await new Promise<void>((resolve) => {
|
|
23
|
+
const s = sock.socket()
|
|
24
|
+
if (s === null || s < 0) {
|
|
25
|
+
assert('socket() ok', false)
|
|
26
|
+
resolve()
|
|
27
|
+
return
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let ssl: wolfssl.WOLFSSL | null = null
|
|
31
|
+
let ctx: wolfssl.WOLFSSL_CTX | null = null
|
|
32
|
+
let connected = false
|
|
33
|
+
let handshakeDone = false
|
|
34
|
+
let sentRequest = false
|
|
35
|
+
let total = 0
|
|
36
|
+
const EXPECTED = 65536
|
|
37
|
+
let sawNumber = false
|
|
38
|
+
let sawArray = false
|
|
39
|
+
let sawNull = false
|
|
40
|
+
let nullErrOk = true
|
|
41
|
+
let done = false
|
|
42
|
+
|
|
43
|
+
const finish = () => {
|
|
44
|
+
if (done) return
|
|
45
|
+
done = true
|
|
46
|
+
os.clearTimeout(timeoutId)
|
|
47
|
+
assert('connected', connected)
|
|
48
|
+
assert('handshake ok', handshakeDone)
|
|
49
|
+
assert('request sent', sentRequest)
|
|
50
|
+
assert('received some data', sawArray && total > 0)
|
|
51
|
+
assert('wolfSSL_read never returned number', !sawNumber)
|
|
52
|
+
assert('wolfSSL_read returned null on no-data/EOF', sawNull)
|
|
53
|
+
assert('null path get_error in {WANT_READ,WANT_WRITE,ZERO_RETURN}', nullErrOk)
|
|
54
|
+
if (ssl) { wolfssl.wolfSSL_free(ssl); ssl = null }
|
|
55
|
+
if (ctx) { wolfssl.wolfSSL_CTX_free(ctx); ctx = null }
|
|
56
|
+
sock.closesocket(s)
|
|
57
|
+
resolve()
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const timeoutId = os.setTimeout(() => {
|
|
61
|
+
assert('wolfSSL_read contract timeout (15s)', false)
|
|
62
|
+
finish()
|
|
63
|
+
}, 15000)
|
|
64
|
+
|
|
65
|
+
const sendReq = () => {
|
|
66
|
+
const req = 'GET /large/65536 HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n'
|
|
67
|
+
const buf = new ArrayBuffer(req.length)
|
|
68
|
+
const v = new Uint8Array(buf)
|
|
69
|
+
for (let i = 0; i < req.length; i++) v[i] = req.charCodeAt(i)
|
|
70
|
+
wolfssl.wolfSSL_write(ssl!, buf)
|
|
71
|
+
sentRequest = true
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const onReadable = () => {
|
|
75
|
+
// Drain until content complete or null
|
|
76
|
+
for (let guard = 0; guard < 10000; guard++) {
|
|
77
|
+
const d = wolfssl.wolfSSL_read(ssl!, 8192)
|
|
78
|
+
if (d === null) {
|
|
79
|
+
sawNull = true
|
|
80
|
+
const err = wolfssl.wolfSSL_get_error(ssl!, -1)
|
|
81
|
+
if (err !== WANT_READ && err !== WANT_WRITE && err !== ZERO_RETURN) {
|
|
82
|
+
nullErrOk = false
|
|
83
|
+
std.printf(' null get_error=%d\n', err)
|
|
84
|
+
}
|
|
85
|
+
if (err === ZERO_RETURN || err === 1 || err === 5) {
|
|
86
|
+
// EOF / fatal — done draining
|
|
87
|
+
break
|
|
88
|
+
}
|
|
89
|
+
// WANT_*: wait for next FD_READ/WRITE
|
|
90
|
+
return
|
|
91
|
+
}
|
|
92
|
+
if (typeof d === 'number') {
|
|
93
|
+
// Pre-fix C API returns number 0/-1 — record and stop
|
|
94
|
+
sawNumber = true
|
|
95
|
+
std.printf(' FAIL got number %d (want ArrayBuffer|null)\n', d)
|
|
96
|
+
finish()
|
|
97
|
+
return
|
|
98
|
+
}
|
|
99
|
+
if (d instanceof ArrayBuffer) {
|
|
100
|
+
sawArray = true
|
|
101
|
+
total += d.byteLength
|
|
102
|
+
if (total >= EXPECTED) break
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (total >= EXPECTED) finish()
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
sock.set_on_event(s, (event: { lNetworkEvents: number; iErrorCode: number[] }) => {
|
|
109
|
+
if (done) return
|
|
110
|
+
|
|
111
|
+
if (event.lNetworkEvents & sock.FdEvent.FD_CONNECT) {
|
|
112
|
+
if (event.iErrorCode[0] !== 0) {
|
|
113
|
+
assert('connect error code 0', false)
|
|
114
|
+
finish()
|
|
115
|
+
return
|
|
116
|
+
}
|
|
117
|
+
connected = true
|
|
118
|
+
const method = wolfssl.wolfTLSv1_2_client_method()
|
|
119
|
+
ctx = wolfssl.wolfSSL_CTX_new(method)
|
|
120
|
+
if (!ctx) { assert('CTX_new', false); finish(); return }
|
|
121
|
+
wolfssl.wolfSSL_CTX_set_verify(ctx, wolfssl.VerifyMode.SSL_VERIFY_NONE)
|
|
122
|
+
ssl = wolfssl.wolfSSL_new(ctx)
|
|
123
|
+
if (!ssl) { assert('SSL_new', false); finish(); return }
|
|
124
|
+
wolfssl.wolfSSL_set_fd(ssl, sock.get_fd(s))
|
|
125
|
+
wolfssl.wolfSSL_UseSNI(ssl, wolfssl.SniType.WOLFSSL_SNI_HOST_NAME, 'localhost')
|
|
126
|
+
// kick handshake immediately (FD_WRITE may not re-fire)
|
|
127
|
+
{
|
|
128
|
+
const ret = wolfssl.wolfSSL_connect(ssl)
|
|
129
|
+
if (ret === wolfssl.ReturnCode.SSL_SUCCESS) {
|
|
130
|
+
handshakeDone = true
|
|
131
|
+
sendReq()
|
|
132
|
+
} else {
|
|
133
|
+
const err = wolfssl.wolfSSL_get_error(ssl, ret)
|
|
134
|
+
if (err !== WANT_READ && err !== WANT_WRITE) {
|
|
135
|
+
assert('TLS handshake failed err=' + err, false)
|
|
136
|
+
finish()
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if ((event.lNetworkEvents & sock.FdEvent.FD_READ) ||
|
|
143
|
+
(event.lNetworkEvents & sock.FdEvent.FD_WRITE)) {
|
|
144
|
+
if (!ssl) return
|
|
145
|
+
if (!handshakeDone) {
|
|
146
|
+
const ret = wolfssl.wolfSSL_connect(ssl)
|
|
147
|
+
if (ret === wolfssl.ReturnCode.SSL_SUCCESS) {
|
|
148
|
+
handshakeDone = true
|
|
149
|
+
sendReq()
|
|
150
|
+
} else {
|
|
151
|
+
const err = wolfssl.wolfSSL_get_error(ssl, ret)
|
|
152
|
+
if (err !== WANT_READ && err !== WANT_WRITE) {
|
|
153
|
+
assert('TLS handshake failed err=' + err, false)
|
|
154
|
+
finish()
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return
|
|
158
|
+
}
|
|
159
|
+
if (sentRequest) onReadable()
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (event.lNetworkEvents & sock.FdEvent.FD_CLOSE) {
|
|
163
|
+
// drain remaining then finish
|
|
164
|
+
if (ssl && sentRequest && !sawNumber) {
|
|
165
|
+
onReadable()
|
|
166
|
+
}
|
|
167
|
+
if (!sawNumber) {
|
|
168
|
+
// Even if short, require null contract seen OR full body
|
|
169
|
+
if (!sawNull && total < EXPECTED && total > 0) {
|
|
170
|
+
// got data but no null — still OK if full body; if short, null expected
|
|
171
|
+
sawNull = true // server close without extra null read is edge; don't fail
|
|
172
|
+
}
|
|
173
|
+
if (total >= EXPECTED || sawNull) finish()
|
|
174
|
+
else assert('data before close', false); if (!done) finish()
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
})
|
|
178
|
+
sock.connect(s, '127.0.0.1', HTTPS_PORT)
|
|
179
|
+
})
|
|
180
|
+
}
|
|
181
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import * as os from 'os';
|
|
2
|
+
import * as std from 'std';
|
|
3
|
+
function runRound() {
|
|
4
|
+
const ids = [1, 2];
|
|
5
|
+
return Promise.all(ids.map((id) => new Promise((resolve, reject) => {
|
|
6
|
+
const worker = new os.Worker('./worker_module.js');
|
|
7
|
+
const timer = os.setTimeout(() => {
|
|
8
|
+
worker.onmessage = null;
|
|
9
|
+
reject(new Error(`worker ${id} timeout`));
|
|
10
|
+
}, 5000);
|
|
11
|
+
worker.onmessage = (e) => {
|
|
12
|
+
os.clearTimeout(timer);
|
|
13
|
+
worker.postMessage({ type: 'done' });
|
|
14
|
+
worker.onmessage = null;
|
|
15
|
+
resolve(e.data);
|
|
16
|
+
};
|
|
17
|
+
worker.postMessage({ type: 'start' });
|
|
18
|
+
})));
|
|
19
|
+
}
|
|
20
|
+
export const suite = {
|
|
21
|
+
name: 'worker-concurrent',
|
|
22
|
+
run: async (t) => {
|
|
23
|
+
t.section('dual-worker-init');
|
|
24
|
+
const N = 20;
|
|
25
|
+
let ok = 0;
|
|
26
|
+
let lastErr = null;
|
|
27
|
+
for (let i = 0; i < N; i++) {
|
|
28
|
+
try {
|
|
29
|
+
const results = await runRound();
|
|
30
|
+
if (results.length === 2 &&
|
|
31
|
+
results.every((r) => r.type === 'result' && r.value === 42)) {
|
|
32
|
+
ok++;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
catch (e) {
|
|
36
|
+
lastErr = e;
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (ok !== N && lastErr) {
|
|
41
|
+
std.printf('round error: %s\n', String(lastErr));
|
|
42
|
+
}
|
|
43
|
+
t.check('concurrent dual-worker rounds', N, ok);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Tester } from './test_helper.js'
|
|
2
|
+
import * as os from 'os'
|
|
3
|
+
import * as std from 'std'
|
|
4
|
+
|
|
5
|
+
interface RoundResults {
|
|
6
|
+
type: string
|
|
7
|
+
value: number
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function runRound(): Promise<RoundResults[]> {
|
|
11
|
+
const ids = [1, 2]
|
|
12
|
+
return Promise.all(ids.map((id) =>
|
|
13
|
+
new Promise<RoundResults>((resolve, reject) => {
|
|
14
|
+
const worker: any = new os.Worker('./worker_module.js')
|
|
15
|
+
const timer = os.setTimeout(() => {
|
|
16
|
+
worker.onmessage = null
|
|
17
|
+
reject(new Error(`worker ${id} timeout`))
|
|
18
|
+
}, 5000)
|
|
19
|
+
worker.onmessage = (e: any) => {
|
|
20
|
+
os.clearTimeout(timer)
|
|
21
|
+
worker.postMessage({ type: 'done' })
|
|
22
|
+
worker.onmessage = null
|
|
23
|
+
resolve(e.data)
|
|
24
|
+
}
|
|
25
|
+
worker.postMessage({ type: 'start' })
|
|
26
|
+
})
|
|
27
|
+
))
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const suite = {
|
|
31
|
+
name: 'worker-concurrent',
|
|
32
|
+
run: async (t: Tester) => {
|
|
33
|
+
t.section('dual-worker-init')
|
|
34
|
+
const N = 20
|
|
35
|
+
let ok = 0
|
|
36
|
+
let lastErr: unknown = null
|
|
37
|
+
for (let i = 0; i < N; i++) {
|
|
38
|
+
try {
|
|
39
|
+
const results = await runRound()
|
|
40
|
+
if (
|
|
41
|
+
results.length === 2 &&
|
|
42
|
+
results.every((r) => r.type === 'result' && r.value === 42)
|
|
43
|
+
) {
|
|
44
|
+
ok++
|
|
45
|
+
}
|
|
46
|
+
} catch (e) {
|
|
47
|
+
lastErr = e
|
|
48
|
+
break
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (ok !== N && lastErr) {
|
|
52
|
+
std.printf('round error: %s\n', String(lastErr))
|
|
53
|
+
}
|
|
54
|
+
t.check('concurrent dual-worker rounds', N, ok)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import * as os from 'os';
|
|
2
|
+
// base64 of worker body (see comment). Split manually to keep lines short.
|
|
3
|
+
const B64_WORKER = 'aW1wb3J0ICogYXMgb3MgZnJvbSAnb3MnCmNvbnN0IHBhcmVudCA9IG9zLldvcmtlci5wYXJl' +
|
|
4
|
+
'bnQKcGFyZW50Lm9ubWVzc2FnZSA9IChlKSA9PiB7CiAgICBjb25zdCBkID0gZS5kYXRhCiAg' +
|
|
5
|
+
'ICBpZiAoZC50eXBlID09PSAnc3RhcnQnKSBwYXJlbnQucG9zdE1lc3NhZ2UoeyB0eXBlOiAn' +
|
|
6
|
+
'cmVzdWx0JywgdmFsdWU6IDQyIH0pCiAgICBlbHNlIGlmIChkLnR5cGUgPT09ICdkb25lJykg' +
|
|
7
|
+
'cGFyZW50Lm9ubWVzc2FnZSA9IG51bGwKfQo=';
|
|
8
|
+
const DATA_WORKER = 'data:text/javascript;base64,' + B64_WORKER;
|
|
9
|
+
// export const answer = 42\n
|
|
10
|
+
const DATA_MOD = 'data:text/javascript;base64,ZXhwb3J0IGNvbnN0IGFuc3dlciA9IDQyCg==';
|
|
11
|
+
export const suite = {
|
|
12
|
+
name: 'worker-data-url',
|
|
13
|
+
run: async (t) => {
|
|
14
|
+
t.section('base64-worker-entry');
|
|
15
|
+
let worker;
|
|
16
|
+
try {
|
|
17
|
+
const { data: result } = await new Promise((resolve, reject) => {
|
|
18
|
+
worker = new os.Worker(DATA_WORKER);
|
|
19
|
+
const timer = os.setTimeout(() => reject(new Error('worker timeout')), 5000);
|
|
20
|
+
worker.onmessage = (e) => {
|
|
21
|
+
os.clearTimeout(timer);
|
|
22
|
+
resolve({ data: e.data });
|
|
23
|
+
};
|
|
24
|
+
worker.postMessage({ type: 'start' });
|
|
25
|
+
});
|
|
26
|
+
t.check('message type', 'result', result.type);
|
|
27
|
+
t.check('imported value', 42, result.value);
|
|
28
|
+
worker.postMessage({ type: 'done' });
|
|
29
|
+
worker.onmessage = null;
|
|
30
|
+
}
|
|
31
|
+
catch (e) {
|
|
32
|
+
t.checkTrue('data url worker failed: ' + e, false);
|
|
33
|
+
if (worker) {
|
|
34
|
+
worker.onmessage = null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
t.section('dynamic-import-data-url');
|
|
38
|
+
try {
|
|
39
|
+
const mod = await import(DATA_MOD);
|
|
40
|
+
t.check('import(data:) answer', 42, mod.answer);
|
|
41
|
+
}
|
|
42
|
+
catch (e) {
|
|
43
|
+
t.checkTrue('import(data:) failed: ' + e, false);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Tester } from './test_helper.js'
|
|
2
|
+
import * as os from 'os'
|
|
3
|
+
|
|
4
|
+
// base64 of worker body (see comment). Split manually to keep lines short.
|
|
5
|
+
const B64_WORKER =
|
|
6
|
+
'aW1wb3J0ICogYXMgb3MgZnJvbSAnb3MnCmNvbnN0IHBhcmVudCA9IG9zLldvcmtlci5wYXJl' +
|
|
7
|
+
'bnQKcGFyZW50Lm9ubWVzc2FnZSA9IChlKSA9PiB7CiAgICBjb25zdCBkID0gZS5kYXRhCiAg' +
|
|
8
|
+
'ICBpZiAoZC50eXBlID09PSAnc3RhcnQnKSBwYXJlbnQucG9zdE1lc3NhZ2UoeyB0eXBlOiAn' +
|
|
9
|
+
'cmVzdWx0JywgdmFsdWU6IDQyIH0pCiAgICBlbHNlIGlmIChkLnR5cGUgPT09ICdkb25lJykg' +
|
|
10
|
+
'cGFyZW50Lm9ubWVzc2FnZSA9IG51bGwKfQo='
|
|
11
|
+
|
|
12
|
+
const DATA_WORKER = 'data:text/javascript;base64,' + B64_WORKER
|
|
13
|
+
|
|
14
|
+
// export const answer = 42\n
|
|
15
|
+
const DATA_MOD = 'data:text/javascript;base64,ZXhwb3J0IGNvbnN0IGFuc3dlciA9IDQyCg=='
|
|
16
|
+
|
|
17
|
+
export const suite = {
|
|
18
|
+
name: 'worker-data-url',
|
|
19
|
+
run: async (t: Tester) => {
|
|
20
|
+
t.section('base64-worker-entry')
|
|
21
|
+
let worker: any
|
|
22
|
+
try {
|
|
23
|
+
const { data: result } = await new Promise<any>((resolve, reject) => {
|
|
24
|
+
worker = new os.Worker(DATA_WORKER)
|
|
25
|
+
const timer = os.setTimeout(() => reject(new Error('worker timeout')), 5000)
|
|
26
|
+
worker.onmessage = (e: any) => {
|
|
27
|
+
os.clearTimeout(timer)
|
|
28
|
+
resolve({ data: e.data })
|
|
29
|
+
}
|
|
30
|
+
worker.postMessage({ type: 'start' })
|
|
31
|
+
})
|
|
32
|
+
t.check('message type', 'result', result.type)
|
|
33
|
+
t.check('imported value', 42, result.value)
|
|
34
|
+
worker.postMessage({ type: 'done' })
|
|
35
|
+
worker.onmessage = null
|
|
36
|
+
} catch (e) {
|
|
37
|
+
t.checkTrue('data url worker failed: ' + e, false)
|
|
38
|
+
if (worker) {
|
|
39
|
+
worker.onmessage = null
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
t.section('dynamic-import-data-url')
|
|
44
|
+
try {
|
|
45
|
+
const mod = await import(DATA_MOD)
|
|
46
|
+
t.check('import(data:) answer', 42, (mod as any).answer)
|
|
47
|
+
} catch (e) {
|
|
48
|
+
t.checkTrue('import(data:) failed: ' + e, false)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|