quickwin 3.0.1 → 3.0.3
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 +3 -0
- package/README.md +2 -0
- package/package.json +2 -3
- 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/examples/PdfCanvas.js +0 -38
- package/examples/PdfCanvas.tsx +0 -54
- package/examples/execd/main.js +0 -23379
- package/examples/execd/tray.js +0 -192
- package/examples/execd/worker.js +0 -122
- package/examples/pdf_preview2.js +0 -398
- package/examples/pdf_preview2.ts +0 -353
- package/examples/pdf_viewer.js +0 -22515
- package/examples/pdf_viewer.tsx +0 -228
- package/examples/test_gallery.js +0 -18692
- package/examples/test_gallery.tsx +0 -291
- package/examples/test_react_lazy.js +0 -18560
- package/examples/test_react_lazy.tsx +0 -65
- package/examples/test_react_listbox.js +0 -18562
- package/examples/test_react_listbox.tsx +0 -73
- package/examples/test_root_window.js +0 -18532
- package/examples/test_root_window.tsx +0 -41
- package/examples/test_tab.js +0 -18563
- package/examples/test_tab.tsx +0 -77
- package/examples/tray_demo.js +0 -86
- package/examples/tray_demo.tsx +0 -89
- package/test/add.wasm +0 -0
- package/test/bench_ffi_callraw.js +0 -235
- package/test/bench_ffi_callraw.ts +0 -235
- package/test/bench_ffi_overhead.js +0 -139
- package/test/bench_ffi_overhead.ts +0 -141
- package/test/bench_sendmessage.js +0 -104
- package/test/bench_sendmessage.ts +0 -100
- package/test/bench_sendmessage_nonnull.js +0 -187
- package/test/bench_sendmessage_nonnull.ts +0 -188
- package/test/complex.wasm +0 -0
- package/test/complex_imports.wasm +0 -0
- package/test/debug/http_server_e2e.js +0 -31
- package/test/debug/minwasm.js +0 -52
- package/test/debug/mupdf_debug.js +0 -86
- package/test/debug/wasm.js +0 -122
- package/test/debug/wasm_export_debug.js +0 -54
- package/test/debug/wasm_names.js +0 -36
- package/test/example.pdf +0 -0
- package/test/global_imports.wasm +0 -0
- package/test/import_func.wasm +0 -0
- package/test/imports.wasm +0 -0
- package/test/mupdf-cdn.js +0 -40
- package/test/mupdf-cdn.ts +0 -43
- package/test/run.js +0 -111
- package/test/run.ts +0 -116
- package/test/sjlj.wasm +0 -0
- package/test/test_basic.js +0 -7
- package/test/test_basic.ts +0 -9
- package/test/test_basic_fetch.js +0 -63
- package/test/test_basic_fetch.ts +0 -59
- package/test/test_brotli.js +0 -48
- package/test/test_brotli.ts +0 -52
- package/test/test_fetch_cache.js +0 -169
- package/test/test_fetch_cache.ts +0 -187
- package/test/test_ffi.js +0 -157
- package/test/test_ffi.ts +0 -170
- package/test/test_ffi_abi.js +0 -290
- package/test/test_ffi_abi.ts +0 -274
- package/test/test_ffi_array.js +0 -116
- package/test/test_ffi_array.ts +0 -141
- package/test/test_ffi_bind.js +0 -656
- package/test/test_ffi_bind.ts +0 -670
- package/test/test_ffi_struct.js +0 -779
- package/test/test_ffi_struct.ts +0 -828
- package/test/test_ffi_struct_layout.js +0 -495
- package/test/test_ffi_struct_layout.ts +0 -543
- package/test/test_ffi_user32.js +0 -103
- package/test/test_ffi_user32.ts +0 -113
- package/test/test_ffi_windows.js +0 -172
- package/test/test_ffi_windows.ts +0 -187
- package/test/test_files.js +0 -102
- package/test/test_files.ts +0 -121
- package/test/test_frame_encoding.js +0 -128
- package/test/test_frame_encoding.ts +0 -132
- package/test/test_gdi_draw.js +0 -681
- package/test/test_gdi_draw.ts +0 -725
- package/test/test_helper.js +0 -91
- package/test/test_helper.ts +0 -88
- package/test/test_http_import.js +0 -96
- package/test/test_http_import.ts +0 -93
- package/test/test_http_server.js +0 -214
- package/test/test_http_server.ts +0 -211
- package/test/test_import_dir.js +0 -4
- package/test/test_import_dir.ts +0 -5
- package/test/test_ipv6.js +0 -134
- package/test/test_ipv6.ts +0 -130
- package/test/test_jsx.js +0 -1314
- package/test/test_meta_url.js +0 -3
- package/test/test_meta_url.ts +0 -4
- package/test/test_mupdf_render.js +0 -51
- package/test/test_mupdf_render.ts +0 -55
- package/test/test_mupdf_twice.js +0 -62
- package/test/test_mupdf_twice.ts +0 -65
- package/test/test_mupdf_wasm.js +0 -39
- package/test/test_mupdf_wasm.ts +0 -32
- package/test/test_net_event.js +0 -57
- package/test/test_net_event.ts +0 -56
- package/test/test_net_fetch.js +0 -384
- package/test/test_net_fetch.ts +0 -373
- package/test/test_net_websocket.js +0 -180
- package/test/test_net_websocket.ts +0 -164
- package/test/test_polyfill.js +0 -144
- package/test/test_polyfill.ts +0 -159
- package/test/test_printer.js +0 -137
- package/test/test_printer.ts +0 -148
- package/test/test_proc.js +0 -141
- package/test/test_proc.ts +0 -163
- package/test/test_react_complex.js +0 -18678
- package/test/test_react_counter.js +0 -18555
- package/test/test_react_flex.js +0 -18871
- package/test/test_react_hidden.js +0 -18560
- package/test/test_react_listview_link.js +0 -18590
- package/test/test_react_notify.js +0 -18797
- package/test/test_react_render.js +0 -18550
- package/test/test_registry.js +0 -74
- package/test/test_registry.ts +0 -95
- package/test/test_sysinfo.js +0 -122
- package/test/test_sysinfo.ts +0 -141
- package/test/test_url.js +0 -173
- package/test/test_url.ts +0 -183
- package/test/test_wasm_basic.js +0 -85
- package/test/test_wasm_basic.ts +0 -73
- package/test/test_wasm_import_global.js +0 -41
- package/test/test_wasm_import_global.ts +0 -45
- package/test/test_wasm_sjlj.js +0 -145
- package/test/test_wasm_sjlj.ts +0 -125
- package/test/test_wasm_types.js +0 -97
- package/test/test_wasm_types.ts +0 -124
- package/test/test_wolfssl_read.js +0 -203
- package/test/test_wolfssl_read.ts +0 -184
- package/test/test_worker.js +0 -21
- package/test/test_worker.ts +0 -24
- package/test/test_worker_concurrent.js +0 -45
- package/test/test_worker_concurrent.ts +0 -56
- package/test/test_worker_data_url.js +0 -46
- package/test/test_worker_data_url.ts +0 -51
- package/test/test_worker_fetch.js +0 -26
- package/test/test_worker_fetch.ts +0 -27
- package/test/test_worker_net.js +0 -69
- package/test/test_worker_net.ts +0 -66
- package/test/test_worker_wasm_concurrent.js +0 -44
- package/test/test_worker_wasm_concurrent.ts +0 -45
- package/test/types.wasm +0 -0
- package/test/worker_fetch.js +0 -13
- package/test/worker_fetch.ts +0 -13
- package/test/worker_helper.js +0 -1
- package/test/worker_helper.ts +0 -1
- package/test/worker_http.js +0 -18
- package/test/worker_http.ts +0 -20
- package/test/worker_import_lib.js +0 -12
- package/test/worker_import_lib.ts +0 -13
- package/test/worker_module.js +0 -16
- package/test/worker_module.ts +0 -17
- package/test/worker_wasm_concurrent.js +0 -22
- package/test/worker_wasm_concurrent.ts +0 -22
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
import '../lib/websocket.js'
|
|
2
|
-
import * as std from 'std'
|
|
3
|
-
import * as os from 'os'
|
|
4
|
-
import { Tester } from './test_helper.js'
|
|
5
|
-
|
|
6
|
-
export const suite = {
|
|
7
|
-
name: 'net-websocket',
|
|
8
|
-
run: async (t: Tester) => {
|
|
9
|
-
function assert(name: string, ok: boolean): void {
|
|
10
|
-
if (ok) { t.ok++; std.printf(' PASS: %s\n', name) }
|
|
11
|
-
else { t.fail++; std.printf(' FAIL: %s\n', name) }
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
let testId = 0
|
|
15
|
-
const resolvers: Record<number, () => void> = {}
|
|
16
|
-
|
|
17
|
-
function waitForTest(): { promise: Promise<void>; id: number } {
|
|
18
|
-
const id = ++testId
|
|
19
|
-
const promise = new Promise<void>((resolve) => { resolvers[id] = resolve })
|
|
20
|
-
return { promise, id }
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function finishTest(id: number) {
|
|
24
|
-
const r = resolvers[id]
|
|
25
|
-
if (r) { delete resolvers[id]; r() }
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// ── wss basic ──
|
|
29
|
-
t.section('wss basic text message')
|
|
30
|
-
const p1 = waitForTest()
|
|
31
|
-
let opened = false, received = false, closed = false
|
|
32
|
-
const ws1 = new WebSocket("ws://localhost:18923")
|
|
33
|
-
ws1.onopen = () => { opened = true; ws1.send("Hello WebSocket!") }
|
|
34
|
-
ws1.onmessage = () => { received = true; ws1.close(1000, "done") }
|
|
35
|
-
ws1.onclose = (e) => {
|
|
36
|
-
closed = true
|
|
37
|
-
assert('onopen fired', opened)
|
|
38
|
-
assert('onmessage received', received)
|
|
39
|
-
assert('close code=1000', e.code === 1000)
|
|
40
|
-
assert('wasClean', e.wasClean)
|
|
41
|
-
finishTest(p1.id)
|
|
42
|
-
}
|
|
43
|
-
ws1.onerror = () => { assert('no error', false); finishTest(p1.id) }
|
|
44
|
-
const timer1 = os.setTimeout(() => { if (!closed) { assert('no timeout', false); finishTest(p1.id) } }, 10000)
|
|
45
|
-
await p1.promise
|
|
46
|
-
os.clearTimeout(timer1)
|
|
47
|
-
|
|
48
|
-
// ── binary send ──
|
|
49
|
-
t.section('binary message (ArrayBuffer)')
|
|
50
|
-
const pBin = waitForTest()
|
|
51
|
-
const wsBin = new WebSocket("ws://localhost:18923")
|
|
52
|
-
let binClose = false
|
|
53
|
-
wsBin.onopen = () => {
|
|
54
|
-
const buf = new ArrayBuffer(5)
|
|
55
|
-
const v = new Uint8Array(buf)
|
|
56
|
-
v[0] = 0x48; v[1] = 0x65; v[2] = 0x6C; v[3] = 0x6C; v[4] = 0x6F
|
|
57
|
-
wsBin.send(buf)
|
|
58
|
-
wsBin.send("binary-send-check")
|
|
59
|
-
}
|
|
60
|
-
let binMsgCount = 0
|
|
61
|
-
wsBin.onmessage = () => { binMsgCount++; if (binMsgCount >= 2) wsBin.close(1000, "done") }
|
|
62
|
-
wsBin.onclose = () => { binClose = true; assert('binary send completed', true); finishTest(pBin.id) }
|
|
63
|
-
wsBin.onerror = () => { assert('no error', false); finishTest(pBin.id) }
|
|
64
|
-
const timerBin = os.setTimeout(() => { if (!binClose) assert('binary no crash (timeout)', true); finishTest(pBin.id) }, 10000)
|
|
65
|
-
await pBin.promise
|
|
66
|
-
os.clearTimeout(timerBin)
|
|
67
|
-
|
|
68
|
-
// ── extended length frame ──
|
|
69
|
-
t.section('extended length frame (>65535 bytes)')
|
|
70
|
-
const pExt = waitForTest()
|
|
71
|
-
const wsExt = new WebSocket("ws://localhost:18923")
|
|
72
|
-
let extClose = false
|
|
73
|
-
wsExt.onopen = () => { wsExt.send("X".repeat(66000)) }
|
|
74
|
-
wsExt.onmessage = () => { wsExt.close(1000, "done") }
|
|
75
|
-
wsExt.onclose = () => { extClose = true; assert('extended frame sent', true); finishTest(pExt.id) }
|
|
76
|
-
wsExt.onerror = () => { assert('no error', false); finishTest(pExt.id) }
|
|
77
|
-
const timerExt = os.setTimeout(() => { if (!extClose) assert('extended frame timeout', true); finishTest(pExt.id) }, 10000)
|
|
78
|
-
await pExt.promise
|
|
79
|
-
os.clearTimeout(timerExt)
|
|
80
|
-
|
|
81
|
-
// ── large message ──
|
|
82
|
-
t.section('large message (>125 bytes)')
|
|
83
|
-
const p2 = waitForTest()
|
|
84
|
-
const ws2 = new WebSocket("ws://localhost:18923")
|
|
85
|
-
let largeOk = false, closed2 = false
|
|
86
|
-
const largeStr = "A".repeat(1000)
|
|
87
|
-
ws2.onopen = () => { ws2.send(largeStr) }
|
|
88
|
-
ws2.onmessage = (e) => { largeOk = (e.data as string).length === 1000; ws2.close(1000, "done") }
|
|
89
|
-
ws2.onclose = () => { closed2 = true; assert('large msg echo 1000 bytes', largeOk); finishTest(p2.id) }
|
|
90
|
-
ws2.onerror = () => { assert('no error', false); finishTest(p2.id) }
|
|
91
|
-
const timer2 = os.setTimeout(() => { if (!closed2) { assert('large msg no timeout', false); finishTest(p2.id) } }, 15000)
|
|
92
|
-
await p2.promise
|
|
93
|
-
os.clearTimeout(timer2)
|
|
94
|
-
|
|
95
|
-
// ── multiple messages ──
|
|
96
|
-
t.section('multiple messages')
|
|
97
|
-
const p3 = waitForTest()
|
|
98
|
-
const ws3 = new WebSocket("ws://localhost:18923")
|
|
99
|
-
let count = 0, closed3 = false
|
|
100
|
-
ws3.onopen = () => { ws3.send("a"); ws3.send("b"); ws3.send("c") }
|
|
101
|
-
ws3.onmessage = () => { count++; if (count === 3) ws3.close(1000, "done") }
|
|
102
|
-
ws3.onclose = () => { closed3 = true; assert('received 3 messages', count === 3); finishTest(p3.id) }
|
|
103
|
-
ws3.onerror = () => { assert('no error', false); finishTest(p3.id) }
|
|
104
|
-
const timer3 = os.setTimeout(() => { if (!closed3) { assert('no timeout', false); finishTest(p3.id) } }, 15000)
|
|
105
|
-
await p3.promise
|
|
106
|
-
os.clearTimeout(timer3)
|
|
107
|
-
|
|
108
|
-
// ── invalid URL ──
|
|
109
|
-
t.section('invalid URL')
|
|
110
|
-
const p4 = waitForTest()
|
|
111
|
-
const ws4 = new WebSocket("not-a-websocket")
|
|
112
|
-
let err4 = false
|
|
113
|
-
ws4.onerror = () => { err4 = true }
|
|
114
|
-
ws4.onclose = () => { assert('onerror fired', err4); finishTest(p4.id) }
|
|
115
|
-
const timer4 = os.setTimeout(() => { assert('invalid url error', err4); finishTest(p4.id) }, 3000)
|
|
116
|
-
await p4.promise
|
|
117
|
-
os.clearTimeout(timer4)
|
|
118
|
-
|
|
119
|
-
// ── invalid scheme ──
|
|
120
|
-
t.section('invalid scheme (http://)')
|
|
121
|
-
const p5 = waitForTest()
|
|
122
|
-
const ws5 = new WebSocket("http://example.com/ws")
|
|
123
|
-
let err5 = false
|
|
124
|
-
ws5.onerror = () => { err5 = true }
|
|
125
|
-
ws5.onclose = () => { assert('http scheme onerror', err5); finishTest(p5.id) }
|
|
126
|
-
const timer5 = os.setTimeout(() => { assert('http scheme error guard', err5); finishTest(p5.id) }, 3000)
|
|
127
|
-
await p5.promise
|
|
128
|
-
os.clearTimeout(timer5)
|
|
129
|
-
|
|
130
|
-
// ── early send/close ──
|
|
131
|
-
t.section('send/close before onopen')
|
|
132
|
-
const p6 = waitForTest()
|
|
133
|
-
const ws6 = new WebSocket("ws://localhost:18923")
|
|
134
|
-
ws6.send("noop")
|
|
135
|
-
ws6.close(1000, "early")
|
|
136
|
-
let close6 = false
|
|
137
|
-
ws6.onopen = () => { assert('onopen after early close', false) }
|
|
138
|
-
ws6.onclose = () => { close6 = true; assert('early close ok', true); finishTest(p6.id) }
|
|
139
|
-
ws6.onerror = () => {}
|
|
140
|
-
const timer6 = os.setTimeout(() => { if (!close6) assert('no crash', true); finishTest(p6.id) }, 5000)
|
|
141
|
-
await p6.promise
|
|
142
|
-
os.clearTimeout(timer6)
|
|
143
|
-
|
|
144
|
-
// ── WSS (Secure WebSocket) ──
|
|
145
|
-
t.section('wss basic text message')
|
|
146
|
-
const p7 = waitForTest()
|
|
147
|
-
let opened7 = false, received7 = false, closed7 = false
|
|
148
|
-
const ws7 = new WebSocket("wss://localhost:18924", { rejectUnauthorized: false })
|
|
149
|
-
ws7.onopen = () => { opened7 = true; ws7.send("Hello WSS!") }
|
|
150
|
-
ws7.onmessage = () => { received7 = true; ws7.close(1000, "done") }
|
|
151
|
-
ws7.onclose = (e) => {
|
|
152
|
-
closed7 = true
|
|
153
|
-
assert('wss onopen fired', opened7)
|
|
154
|
-
assert('wss onmessage received', received7)
|
|
155
|
-
assert('wss close code=1000', e.code === 1000)
|
|
156
|
-
assert('wss wasClean', e.wasClean)
|
|
157
|
-
finishTest(p7.id)
|
|
158
|
-
}
|
|
159
|
-
ws7.onerror = () => { assert('wss no error', false); finishTest(p7.id) }
|
|
160
|
-
const timer7 = os.setTimeout(() => { if (!closed7) { assert('wss no timeout', false); finishTest(p7.id) } }, 10000)
|
|
161
|
-
await p7.promise
|
|
162
|
-
os.clearTimeout(timer7)
|
|
163
|
-
}
|
|
164
|
-
}
|
package/test/test_polyfill.js
DELETED
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
import '../lib/polyfill.js';
|
|
2
|
-
import * as std from 'std';
|
|
3
|
-
function assert(t, name, ok) {
|
|
4
|
-
if (ok) {
|
|
5
|
-
t.ok++;
|
|
6
|
-
std.printf(' PASS: %s\n', name);
|
|
7
|
-
}
|
|
8
|
-
else {
|
|
9
|
-
t.fail++;
|
|
10
|
-
std.printf(' FAIL: %s\n', name);
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
export const suite = {
|
|
14
|
-
name: 'polyfill',
|
|
15
|
-
run: async (t) => {
|
|
16
|
-
t.section('btoa/atob');
|
|
17
|
-
const orig = "Hello\u0000World";
|
|
18
|
-
const b64 = btoa(orig);
|
|
19
|
-
const back = atob(b64);
|
|
20
|
-
assert(t, 'btoa/atob roundtrip', back === orig);
|
|
21
|
-
assert(t, 'btoa produces base64 chars', /^[A-Za-z0-9+/=]+$/.test(b64));
|
|
22
|
-
const hash = new Uint8Array([0x2f, 0xd4, 0xe1, 0xc6, 0x7a, 0x2d, 0x28, 0xfc, 0xed, 0x84, 0x9e, 0xe1, 0xbb, 0x76, 0xe7, 0x39, 0x1b, 0x93, 0xeb, 0x12]);
|
|
23
|
-
assert(t, 'btoa(SHA-1 sample) matches known value', btoa(String.fromCharCode(...hash)) === "L9ThxnotKPzthJ7hu3bnORuT6xI=");
|
|
24
|
-
t.section('crypto.getRandomValues');
|
|
25
|
-
const buf = new Uint8Array(32);
|
|
26
|
-
crypto.getRandomValues(buf);
|
|
27
|
-
assert(t, 'length=32', buf.length === 32);
|
|
28
|
-
assert(t, 'not all zeros', buf.some(b => b !== 0));
|
|
29
|
-
t.section('crypto.subtle.digest SHA-1');
|
|
30
|
-
const data = new TextEncoder().encode("Hello");
|
|
31
|
-
const hashBuf = await crypto.subtle.digest("SHA-1", data);
|
|
32
|
-
const hb = new Uint8Array(hashBuf);
|
|
33
|
-
assert(t, 'output length=20', hb.length === 20);
|
|
34
|
-
assert(t, 'SHA-1(\'Hello\') matches', hb[0] === 0xF7 && hb[1] === 0xFF && hb[19] === 0xF0);
|
|
35
|
-
t.section('TextEncoder utf-8');
|
|
36
|
-
const enc = new TextEncoder();
|
|
37
|
-
assert(t, 'default encoding', enc.encoding === 'utf-8');
|
|
38
|
-
const encoded = enc.encode("Hello");
|
|
39
|
-
assert(t, 'encode length', encoded.length === 5);
|
|
40
|
-
assert(t, 'encode bytes', encoded[0] === 0x48 && encoded[4] === 0x6F);
|
|
41
|
-
assert(t, 'encoding property', enc.encoding === 'utf-8');
|
|
42
|
-
const enc2 = new TextEncoder('utf-8');
|
|
43
|
-
assert(t, 'utf-8 label', enc2.encoding === 'utf-8');
|
|
44
|
-
const enc3 = new TextEncoder('utf8');
|
|
45
|
-
assert(t, 'utf8 alias', enc3.encoding === 'utf-8');
|
|
46
|
-
let threw = false;
|
|
47
|
-
try {
|
|
48
|
-
new TextEncoder('gbk');
|
|
49
|
-
}
|
|
50
|
-
catch (e) {
|
|
51
|
-
threw = e instanceof RangeError;
|
|
52
|
-
}
|
|
53
|
-
assert(t, 'unsupported label throws RangeError', threw);
|
|
54
|
-
t.section('TextEncoder utf-16le');
|
|
55
|
-
const enc16le = new TextEncoder('utf-16le');
|
|
56
|
-
assert(t, 'encoding property', enc16le.encoding === 'utf-16le');
|
|
57
|
-
const hello16le = enc16le.encode('AB');
|
|
58
|
-
assert(t, 'length = 4', hello16le.length === 4);
|
|
59
|
-
assert(t, 'A = 0x41 0x00', hello16le[0] === 0x41 && hello16le[1] === 0x00);
|
|
60
|
-
assert(t, 'B = 0x42 0x00', hello16le[2] === 0x42 && hello16le[3] === 0x00);
|
|
61
|
-
const null16le = enc16le.encode('\0');
|
|
62
|
-
assert(t, 'null char = 0x00 0x00', null16le.length === 2 && null16le[0] === 0 && null16le[1] === 0);
|
|
63
|
-
const chinese16le = enc16le.encode('\u4e2d'); // 中 = U+4E2D
|
|
64
|
-
assert(t, '中 = 0x2D 0x4E', chinese16le[0] === 0x2D && chinese16le[1] === 0x4E);
|
|
65
|
-
t.section('TextEncoder utf-16be');
|
|
66
|
-
const enc16be = new TextEncoder('utf-16be');
|
|
67
|
-
assert(t, 'encoding property', enc16be.encoding === 'utf-16be');
|
|
68
|
-
const hello16be = enc16be.encode('AB');
|
|
69
|
-
assert(t, 'length = 4', hello16be.length === 4);
|
|
70
|
-
assert(t, 'A = 0x00 0x41', hello16be[0] === 0x00 && hello16be[1] === 0x41);
|
|
71
|
-
assert(t, 'B = 0x00 0x42', hello16be[2] === 0x00 && hello16be[3] === 0x42);
|
|
72
|
-
t.section('TextDecoder utf-8');
|
|
73
|
-
const dec = new TextDecoder();
|
|
74
|
-
assert(t, 'default encoding', dec.encoding === 'utf-8');
|
|
75
|
-
assert(t, 'default fatal', dec.fatal === false);
|
|
76
|
-
assert(t, 'default ignoreBOM', dec.ignoreBOM === false);
|
|
77
|
-
assert(t, 'decode utf-8 bytes', dec.decode(encoded) === 'Hello');
|
|
78
|
-
assert(t, 'decode empty', dec.decode() === '');
|
|
79
|
-
assert(t, 'decode null', dec.decode(null) === '');
|
|
80
|
-
const decFatal = new TextDecoder('utf-8', { fatal: true });
|
|
81
|
-
assert(t, 'fatal encoding', decFatal.encoding === 'utf-8');
|
|
82
|
-
assert(t, 'fatal true', decFatal.fatal === true);
|
|
83
|
-
let threw2 = false;
|
|
84
|
-
try {
|
|
85
|
-
decFatal.decode(new Uint8Array([0xFF]));
|
|
86
|
-
}
|
|
87
|
-
catch (e) {
|
|
88
|
-
threw2 = e instanceof TypeError;
|
|
89
|
-
}
|
|
90
|
-
assert(t, 'fatal decode throws TypeError', threw2);
|
|
91
|
-
const nonFatal = new TextDecoder();
|
|
92
|
-
const replaced = nonFatal.decode(new Uint8Array([0xFF]));
|
|
93
|
-
assert(t, 'non-fatal replaces with U+FFFD', replaced === '\uFFFD');
|
|
94
|
-
t.section('TextDecoder utf-16le');
|
|
95
|
-
const dec16le = new TextDecoder('utf-16le');
|
|
96
|
-
assert(t, 'encoding = utf-16le', dec16le.encoding === 'utf-16le');
|
|
97
|
-
const ab = new Uint8Array([0x41, 0x00, 0x42, 0x00]);
|
|
98
|
-
assert(t, 'AB from utf-16le', dec16le.decode(ab) === 'AB');
|
|
99
|
-
const chn = new Uint8Array([0x2D, 0x4E]);
|
|
100
|
-
assert(t, '中 from utf-16le', dec16le.decode(chn) === '\u4e2d');
|
|
101
|
-
const withBom = new Uint8Array([0xFF, 0xFE, 0x41, 0x00]);
|
|
102
|
-
assert(t, 'BOM stripped by default', dec16le.decode(withBom) === 'A');
|
|
103
|
-
const dec16leNoBom = new TextDecoder('utf-16le', { ignoreBOM: true });
|
|
104
|
-
assert(t, 'ignoreBOM keeps BOM', dec16leNoBom.decode(withBom) === '\uFEFFA');
|
|
105
|
-
t.section('TextDecoder utf-16be');
|
|
106
|
-
const dec16be = new TextDecoder('utf-16be');
|
|
107
|
-
const be = new Uint8Array([0x00, 0x41, 0x00, 0x42]);
|
|
108
|
-
assert(t, 'AB from utf-16be', dec16be.decode(be) === 'AB');
|
|
109
|
-
const chnBe = new Uint8Array([0x4E, 0x2D]);
|
|
110
|
-
assert(t, '中 from utf-16be', dec16be.decode(chnBe) === '\u4e2d');
|
|
111
|
-
t.section('TextDecoder roundtrip');
|
|
112
|
-
const texts = ['Hello', 'Hello 世界', '\u4e2d\u6587', 'A\0B', '\uD83D\uDE00']; // emoji
|
|
113
|
-
for (const txt of texts) {
|
|
114
|
-
const enc8 = new TextEncoder();
|
|
115
|
-
const bytes = enc8.encode(txt);
|
|
116
|
-
const dec8 = new TextDecoder();
|
|
117
|
-
assert(t, `utf-8 roundtrip: ${txt}`, dec8.decode(bytes) === txt);
|
|
118
|
-
const enc16le = new TextEncoder('utf-16le');
|
|
119
|
-
const bytes16 = enc16le.encode(txt);
|
|
120
|
-
const dec16le = new TextDecoder('utf-16le');
|
|
121
|
-
assert(t, `utf-16le roundtrip: ${txt}`, dec16le.decode(bytes16) === txt);
|
|
122
|
-
const enc16be = new TextEncoder('utf-16be');
|
|
123
|
-
const bytes16be = enc16be.encode(txt);
|
|
124
|
-
const dec16be = new TextDecoder('utf-16be');
|
|
125
|
-
assert(t, `utf-16be roundtrip: ${txt}`, dec16be.decode(bytes16be) === txt);
|
|
126
|
-
}
|
|
127
|
-
t.section('SHA-1 + btoa (WebSocket accept key)');
|
|
128
|
-
const MAGIC_GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
|
|
129
|
-
const testKey = 'dGhlIHNhbXBsZSBub25jZQ==';
|
|
130
|
-
const concat = testKey + MAGIC_GUID;
|
|
131
|
-
const acceptBuf = await crypto.subtle.digest("SHA-1", new TextEncoder().encode(concat));
|
|
132
|
-
const acceptBytes = new Uint8Array(acceptBuf);
|
|
133
|
-
const acceptB64 = btoa(String.fromCharCode(...acceptBytes));
|
|
134
|
-
assert(t, 'RFC 6455 accept key matches', acceptB64 === "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=");
|
|
135
|
-
let rejected = false;
|
|
136
|
-
try {
|
|
137
|
-
await crypto.subtle.digest("SHA-256", new Uint8Array(1));
|
|
138
|
-
}
|
|
139
|
-
catch {
|
|
140
|
-
rejected = true;
|
|
141
|
-
}
|
|
142
|
-
assert(t, 'unsupported algorithm rejected', rejected);
|
|
143
|
-
}
|
|
144
|
-
};
|
package/test/test_polyfill.ts
DELETED
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
import '../lib/polyfill.js'
|
|
2
|
-
import * as std from 'std'
|
|
3
|
-
import { Tester } from './test_helper.js'
|
|
4
|
-
|
|
5
|
-
function assert(t: Tester, name: string, ok: boolean): void {
|
|
6
|
-
if (ok) { t.ok++; std.printf(' PASS: %s\n', name) }
|
|
7
|
-
else { t.fail++; std.printf(' FAIL: %s\n', name) }
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export const suite = {
|
|
11
|
-
name: 'polyfill',
|
|
12
|
-
run: async (t: Tester) => {
|
|
13
|
-
t.section('btoa/atob')
|
|
14
|
-
const orig = "Hello\u0000World"
|
|
15
|
-
const b64 = btoa(orig)
|
|
16
|
-
const back = atob(b64)
|
|
17
|
-
assert(t, 'btoa/atob roundtrip', back === orig)
|
|
18
|
-
assert(t, 'btoa produces base64 chars', /^[A-Za-z0-9+/=]+$/.test(b64))
|
|
19
|
-
|
|
20
|
-
const hash = new Uint8Array([0x2f, 0xd4, 0xe1, 0xc6, 0x7a, 0x2d, 0x28, 0xfc, 0xed, 0x84, 0x9e, 0xe1, 0xbb, 0x76, 0xe7, 0x39, 0x1b, 0x93, 0xeb, 0x12])
|
|
21
|
-
assert(t, 'btoa(SHA-1 sample) matches known value', btoa(String.fromCharCode(...hash)) === "L9ThxnotKPzthJ7hu3bnORuT6xI=")
|
|
22
|
-
|
|
23
|
-
t.section('crypto.getRandomValues')
|
|
24
|
-
const buf = new Uint8Array(32)
|
|
25
|
-
crypto.getRandomValues(buf)
|
|
26
|
-
assert(t, 'length=32', buf.length === 32)
|
|
27
|
-
assert(t, 'not all zeros', buf.some(b => b !== 0))
|
|
28
|
-
|
|
29
|
-
t.section('crypto.subtle.digest SHA-1')
|
|
30
|
-
const data = new TextEncoder().encode("Hello")
|
|
31
|
-
const hashBuf = await crypto.subtle.digest("SHA-1", data)
|
|
32
|
-
const hb = new Uint8Array(hashBuf)
|
|
33
|
-
assert(t, 'output length=20', hb.length === 20)
|
|
34
|
-
assert(t, 'SHA-1(\'Hello\') matches', hb[0] === 0xF7 && hb[1] === 0xFF && hb[19] === 0xF0)
|
|
35
|
-
|
|
36
|
-
t.section('TextEncoder utf-8')
|
|
37
|
-
const enc = new TextEncoder()
|
|
38
|
-
assert(t, 'default encoding', enc.encoding === 'utf-8')
|
|
39
|
-
const encoded = enc.encode("Hello")
|
|
40
|
-
assert(t, 'encode length', encoded.length === 5)
|
|
41
|
-
assert(t, 'encode bytes', encoded[0] === 0x48 && encoded[4] === 0x6F)
|
|
42
|
-
assert(t, 'encoding property', enc.encoding === 'utf-8')
|
|
43
|
-
|
|
44
|
-
const enc2 = new TextEncoder('utf-8')
|
|
45
|
-
assert(t, 'utf-8 label', enc2.encoding === 'utf-8')
|
|
46
|
-
|
|
47
|
-
const enc3 = new TextEncoder('utf8')
|
|
48
|
-
assert(t, 'utf8 alias', enc3.encoding === 'utf-8')
|
|
49
|
-
|
|
50
|
-
let threw = false
|
|
51
|
-
try { new TextEncoder('gbk' as any) } catch (e) { threw = e instanceof RangeError }
|
|
52
|
-
assert(t, 'unsupported label throws RangeError', threw)
|
|
53
|
-
|
|
54
|
-
t.section('TextEncoder utf-16le')
|
|
55
|
-
const enc16le = new TextEncoder('utf-16le')
|
|
56
|
-
assert(t, 'encoding property', enc16le.encoding === 'utf-16le')
|
|
57
|
-
|
|
58
|
-
const hello16le = enc16le.encode('AB')
|
|
59
|
-
assert(t, 'length = 4', hello16le.length === 4)
|
|
60
|
-
assert(t, 'A = 0x41 0x00', hello16le[0] === 0x41 && hello16le[1] === 0x00)
|
|
61
|
-
assert(t, 'B = 0x42 0x00', hello16le[2] === 0x42 && hello16le[3] === 0x00)
|
|
62
|
-
|
|
63
|
-
const null16le = enc16le.encode('\0')
|
|
64
|
-
assert(t, 'null char = 0x00 0x00', null16le.length === 2 && null16le[0] === 0 && null16le[1] === 0)
|
|
65
|
-
|
|
66
|
-
const chinese16le = enc16le.encode('\u4e2d') // 中 = U+4E2D
|
|
67
|
-
assert(t, '中 = 0x2D 0x4E', chinese16le[0] === 0x2D && chinese16le[1] === 0x4E)
|
|
68
|
-
|
|
69
|
-
t.section('TextEncoder utf-16be')
|
|
70
|
-
const enc16be = new TextEncoder('utf-16be')
|
|
71
|
-
assert(t, 'encoding property', enc16be.encoding === 'utf-16be')
|
|
72
|
-
|
|
73
|
-
const hello16be = enc16be.encode('AB')
|
|
74
|
-
assert(t, 'length = 4', hello16be.length === 4)
|
|
75
|
-
assert(t, 'A = 0x00 0x41', hello16be[0] === 0x00 && hello16be[1] === 0x41)
|
|
76
|
-
assert(t, 'B = 0x00 0x42', hello16be[2] === 0x00 && hello16be[3] === 0x42)
|
|
77
|
-
|
|
78
|
-
t.section('TextDecoder utf-8')
|
|
79
|
-
const dec = new TextDecoder()
|
|
80
|
-
assert(t, 'default encoding', dec.encoding === 'utf-8')
|
|
81
|
-
assert(t, 'default fatal', dec.fatal === false)
|
|
82
|
-
assert(t, 'default ignoreBOM', dec.ignoreBOM === false)
|
|
83
|
-
assert(t, 'decode utf-8 bytes', dec.decode(encoded) === 'Hello')
|
|
84
|
-
assert(t, 'decode empty', dec.decode() === '')
|
|
85
|
-
assert(t, 'decode null', dec.decode(null) === '')
|
|
86
|
-
|
|
87
|
-
const decFatal = new TextDecoder('utf-8', { fatal: true })
|
|
88
|
-
assert(t, 'fatal encoding', decFatal.encoding === 'utf-8')
|
|
89
|
-
assert(t, 'fatal true', decFatal.fatal === true)
|
|
90
|
-
|
|
91
|
-
let threw2 = false
|
|
92
|
-
try { decFatal.decode(new Uint8Array([0xFF])) } catch (e) { threw2 = e instanceof TypeError }
|
|
93
|
-
assert(t, 'fatal decode throws TypeError', threw2)
|
|
94
|
-
|
|
95
|
-
const nonFatal = new TextDecoder()
|
|
96
|
-
const replaced = nonFatal.decode(new Uint8Array([0xFF]))
|
|
97
|
-
assert(t, 'non-fatal replaces with U+FFFD', replaced === '\uFFFD')
|
|
98
|
-
|
|
99
|
-
t.section('TextDecoder utf-16le')
|
|
100
|
-
const dec16le = new TextDecoder('utf-16le')
|
|
101
|
-
assert(t, 'encoding = utf-16le', dec16le.encoding === 'utf-16le')
|
|
102
|
-
|
|
103
|
-
const ab = new Uint8Array([0x41, 0x00, 0x42, 0x00])
|
|
104
|
-
assert(t, 'AB from utf-16le', dec16le.decode(ab) === 'AB')
|
|
105
|
-
|
|
106
|
-
const chn = new Uint8Array([0x2D, 0x4E])
|
|
107
|
-
assert(t, '中 from utf-16le', dec16le.decode(chn) === '\u4e2d')
|
|
108
|
-
|
|
109
|
-
const withBom = new Uint8Array([0xFF, 0xFE, 0x41, 0x00])
|
|
110
|
-
assert(t, 'BOM stripped by default', dec16le.decode(withBom) === 'A')
|
|
111
|
-
|
|
112
|
-
const dec16leNoBom = new TextDecoder('utf-16le', { ignoreBOM: true })
|
|
113
|
-
assert(t, 'ignoreBOM keeps BOM', dec16leNoBom.decode(withBom) === '\uFEFFA')
|
|
114
|
-
|
|
115
|
-
t.section('TextDecoder utf-16be')
|
|
116
|
-
const dec16be = new TextDecoder('utf-16be')
|
|
117
|
-
const be = new Uint8Array([0x00, 0x41, 0x00, 0x42])
|
|
118
|
-
assert(t, 'AB from utf-16be', dec16be.decode(be) === 'AB')
|
|
119
|
-
|
|
120
|
-
const chnBe = new Uint8Array([0x4E, 0x2D])
|
|
121
|
-
assert(t, '中 from utf-16be', dec16be.decode(chnBe) === '\u4e2d')
|
|
122
|
-
|
|
123
|
-
t.section('TextDecoder roundtrip')
|
|
124
|
-
const texts = ['Hello', 'Hello 世界', '\u4e2d\u6587', 'A\0B', '\uD83D\uDE00'] // emoji
|
|
125
|
-
for (const txt of texts) {
|
|
126
|
-
const enc8 = new TextEncoder()
|
|
127
|
-
const bytes = enc8.encode(txt)
|
|
128
|
-
const dec8 = new TextDecoder()
|
|
129
|
-
assert(t, `utf-8 roundtrip: ${txt}`, dec8.decode(bytes) === txt)
|
|
130
|
-
|
|
131
|
-
const enc16le = new TextEncoder('utf-16le')
|
|
132
|
-
const bytes16 = enc16le.encode(txt)
|
|
133
|
-
const dec16le = new TextDecoder('utf-16le')
|
|
134
|
-
assert(t, `utf-16le roundtrip: ${txt}`, dec16le.decode(bytes16) === txt)
|
|
135
|
-
|
|
136
|
-
const enc16be = new TextEncoder('utf-16be')
|
|
137
|
-
const bytes16be = enc16be.encode(txt)
|
|
138
|
-
const dec16be = new TextDecoder('utf-16be')
|
|
139
|
-
assert(t, `utf-16be roundtrip: ${txt}`, dec16be.decode(bytes16be) === txt)
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
t.section('SHA-1 + btoa (WebSocket accept key)')
|
|
143
|
-
const MAGIC_GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
|
|
144
|
-
const testKey = 'dGhlIHNhbXBsZSBub25jZQ=='
|
|
145
|
-
const concat = testKey + MAGIC_GUID
|
|
146
|
-
const acceptBuf = await crypto.subtle.digest("SHA-1", new TextEncoder().encode(concat))
|
|
147
|
-
const acceptBytes = new Uint8Array(acceptBuf)
|
|
148
|
-
const acceptB64 = btoa(String.fromCharCode(...acceptBytes))
|
|
149
|
-
assert(t, 'RFC 6455 accept key matches', acceptB64 === "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=")
|
|
150
|
-
|
|
151
|
-
let rejected = false
|
|
152
|
-
try {
|
|
153
|
-
await crypto.subtle.digest("SHA-256" as any, new Uint8Array(1))
|
|
154
|
-
} catch {
|
|
155
|
-
rejected = true
|
|
156
|
-
}
|
|
157
|
-
assert(t, 'unsupported algorithm rejected', rejected)
|
|
158
|
-
}
|
|
159
|
-
}
|
package/test/test_printer.js
DELETED
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
import * as ffi from 'ffi';
|
|
2
|
-
import * as os from 'os';
|
|
3
|
-
import { WCHAR } from '../lib/ffi/bind.js';
|
|
4
|
-
import { structArray } from '../lib/ffi/struct.js';
|
|
5
|
-
import { NULL, PtrArrayBuffer } from '../lib/ffi/ctype.js';
|
|
6
|
-
import { CloseHandle, CreateFile, DeleteFile, GetLastError, GetTempPath, GetFileSize, ReadFile } from '../lib/windows/kernel32.js';
|
|
7
|
-
import { ClosePrinter, DeviceCapabilities, EnumPrinters, EndDocPrinter, EndPagePrinter, GetDefaultPrinter, OpenPrinter, StartDocPrinter, StartPagePrinter, WritePrinter, } from '../lib/windows/winspool.js';
|
|
8
|
-
import { PRINTER_INFO_2W } from '../lib/windows/structs.js';
|
|
9
|
-
// PrinterEnum.LOCAL(0x2) | CONNECTIONS(0x4):可组合 flags 组合 OR 裸传
|
|
10
|
-
const LOCAL_OR_CONNECTIONS = 0x6;
|
|
11
|
-
const MAX_WCHARS = 4096;
|
|
12
|
-
// 指针字段跟进读宽字符串 / 跨架构读指针(test_ffi.ts 同型)
|
|
13
|
-
function decodeWideAtPtr(ptr) {
|
|
14
|
-
if (!ptr)
|
|
15
|
-
return '';
|
|
16
|
-
const chars = [];
|
|
17
|
-
let pos = ptr;
|
|
18
|
-
while (chars.length < MAX_WCHARS) {
|
|
19
|
-
const ch = ffi.readByte(pos) + ffi.readByte(pos + 1) * 256;
|
|
20
|
-
if (ch === 0)
|
|
21
|
-
break;
|
|
22
|
-
chars.push(ch);
|
|
23
|
-
pos += 2;
|
|
24
|
-
}
|
|
25
|
-
return String.fromCharCode(...chars);
|
|
26
|
-
}
|
|
27
|
-
function readPtr(dv, offset) {
|
|
28
|
-
if (os.arch === 'ia32')
|
|
29
|
-
return dv.getUint32(offset, true);
|
|
30
|
-
return dv.getUint32(offset, true) + dv.getUint32(offset + 4, true) * 4294967296;
|
|
31
|
-
}
|
|
32
|
-
export const suite = {
|
|
33
|
-
name: 'printer',
|
|
34
|
-
run: (t) => {
|
|
35
|
-
t.section('EnumPrinters: two-pass LOCAL|CONNECTIONS');
|
|
36
|
-
const neededB = new PtrArrayBuffer(4);
|
|
37
|
-
const returnedB = new PtrArrayBuffer(4);
|
|
38
|
-
const neededU = new Uint32Array(neededB);
|
|
39
|
-
const returnedU = new Uint32Array(returnedB);
|
|
40
|
-
EnumPrinters(LOCAL_OR_CONNECTIONS, NULL, 2, NULL, 0, neededB, returnedB);
|
|
41
|
-
if (neededU[0] === 0) {
|
|
42
|
-
// 无打印机环境(XP VM):整段优雅跳过——属环境差异非缺陷(test_ffi 先例)
|
|
43
|
-
t.skipCase('printer suite (no printers on this VM)');
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
t.checkTrue(`pcbNeeded > 0 (${neededU[0]})`, neededU[0] > 0);
|
|
47
|
-
const buf = new PtrArrayBuffer(neededU[0]);
|
|
48
|
-
t.checkTrue('EnumPrinters full buffer', EnumPrinters(LOCAL_OR_CONNECTIONS, NULL, 2, buf, neededU[0], neededB, returnedB) !== 0);
|
|
49
|
-
const count = returnedU[0];
|
|
50
|
-
t.checkTrue(`found printers (${count})`, count > 0);
|
|
51
|
-
// 出参数组读回:结构数组区 = 前 count*size 字节,尾部是字符串数据
|
|
52
|
-
// (byteLength 非 size 整数倍)→ 必须显式 count;指针字段跟进读字符串
|
|
53
|
-
const infos = structArray(PRINTER_INFO_2W).decode(buf, count);
|
|
54
|
-
t.check('decode count == pcReturned', count, infos.length);
|
|
55
|
-
// 单值 decode 与数组 decode 首元素一致(布局单一来源互证,无硬编码 offset)
|
|
56
|
-
t.checkTrue('array/single decode agree', PRINTER_INFO_2W.decode(buf).pPrinterName === infos[0].pPrinterName);
|
|
57
|
-
const names = [];
|
|
58
|
-
const ports = {};
|
|
59
|
-
for (const inf of infos) {
|
|
60
|
-
const name = decodeWideAtPtr(inf.pPrinterName);
|
|
61
|
-
names.push(name);
|
|
62
|
-
ports[name] = decodeWideAtPtr(inf.pPortName);
|
|
63
|
-
}
|
|
64
|
-
t.section('GetDefaultPrinter');
|
|
65
|
-
const defW = WCHAR.alloc(260);
|
|
66
|
-
const lenB = new PtrArrayBuffer(4);
|
|
67
|
-
new Uint32Array(lenB)[0] = 260;
|
|
68
|
-
t.checkTrue('GetDefaultPrinter ok', GetDefaultPrinter(defW, lenB) !== 0);
|
|
69
|
-
const defName = WCHAR.decode(defW);
|
|
70
|
-
t.checkTrue(`default in enum list (${defName})`, names.includes(defName));
|
|
71
|
-
t.section('DeviceCapabilities: numeric caps on default printer');
|
|
72
|
-
const defPort = ports[defName] ?? NULL;
|
|
73
|
-
const horz = DeviceCapabilities(defName, defPort, 8 /* DeviceCap.HORZRES */, NULL, NULL);
|
|
74
|
-
t.checkTrue(`HORZRES sane (${horz})`, horz > 0);
|
|
75
|
-
const vert = DeviceCapabilities(defName, defPort, 10 /* DeviceCap.VERTRES */, NULL, NULL);
|
|
76
|
-
t.checkTrue(`VERTRES sane (${vert})`, vert > 0);
|
|
77
|
-
t.section('raw job chain on default printer (FILE: port -> .prn file, no UI)');
|
|
78
|
-
// 输出文件放系统 temp(GetTempPath 尾带反斜杠)——FILE: 端口把作业写到 pOutputFile
|
|
79
|
-
const tmpW = WCHAR.alloc(260);
|
|
80
|
-
GetTempPath(260, tmpW);
|
|
81
|
-
const outPrn = `${WCHAR.decode(tmpW)}qw_job.prn`;
|
|
82
|
-
const slot = new PtrArrayBuffer(8);
|
|
83
|
-
t.checkTrue('OpenPrinter ok', OpenPrinter(defName, slot, NULL) !== 0);
|
|
84
|
-
const hPrinter = readPtr(new DataView(slot), 0);
|
|
85
|
-
t.checkTrue(`hPrinter valid (${hPrinter})`, hPrinter !== 0);
|
|
86
|
-
// pDatatype 省略(NULL)→ spooler 用打印机默认 datatype;显式传 RAW/XPS_PASS 反而
|
|
87
|
-
// 可能被驱动拒——1804 = ERROR_INVALID_DATATYPE(官方错误码表;注意 StartDocPrinter
|
|
88
|
-
// 不设置 last error,GetLastError 读到的常是残留值)
|
|
89
|
-
const jobId = StartDocPrinter(hPrinter, 1, {
|
|
90
|
-
pDocName: WCHAR.encode('QuickWin Raw Job').ptr, // <>ptr 字段收裸地址(number 子类型)
|
|
91
|
-
pOutputFile: WCHAR.encode(outPrn).ptr,
|
|
92
|
-
});
|
|
93
|
-
if (jobId === 0) {
|
|
94
|
-
// 失败即暴露(1804 = ERROR_INVALID_DATATYPE——datatype 显式 RAW/XPS_PASS 会被
|
|
95
|
-
// XPS 驱动拒,NULL 让 spooler 用默认即通;StartDocPrinter 不设置 last error,
|
|
96
|
-
// 下方诊断值常是残留)
|
|
97
|
-
t.checkTrue(`StartDocPrinter job id > 0 (lastError=${GetLastError()})`, false);
|
|
98
|
-
ClosePrinter(hPrinter);
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
t.checkTrue(`StartDocPrinter job id > 0`, jobId > 0);
|
|
102
|
-
const sp = StartPagePrinter(hPrinter);
|
|
103
|
-
t.checkTrue(`StartPagePrinter (lastError=${GetLastError()})`, sp !== 0);
|
|
104
|
-
const job = 'hello printer';
|
|
105
|
-
const jBuf = new PtrArrayBuffer(job.length);
|
|
106
|
-
const jDv = new DataView(jBuf);
|
|
107
|
-
for (let i = 0; i < job.length; i++)
|
|
108
|
-
jDv.setUint8(i, job.charCodeAt(i));
|
|
109
|
-
const pWr = new PtrArrayBuffer(4);
|
|
110
|
-
const wr = WritePrinter(hPrinter, jBuf, job.length, pWr);
|
|
111
|
-
t.checkTrue(`WritePrinter ok (lastError=${GetLastError()})`, wr !== 0);
|
|
112
|
-
t.check('bytes written', job.length, new Uint32Array(pWr)[0]);
|
|
113
|
-
const ep = EndPagePrinter(hPrinter);
|
|
114
|
-
t.checkTrue(`EndPagePrinter (lastError=${GetLastError()})`, ep !== 0);
|
|
115
|
-
const ed = EndDocPrinter(hPrinter);
|
|
116
|
-
t.checkTrue(`EndDocPrinter (lastError=${GetLastError()})`, ed !== 0);
|
|
117
|
-
t.checkTrue('ClosePrinter', ClosePrinter(hPrinter) !== 0);
|
|
118
|
-
// 作业落盘断言:FILE: 端口把 RAW 数据写到 pOutputFile
|
|
119
|
-
const hOut = CreateFile(outPrn, 0x80000000 /* GENERIC_READ */, 0, NULL, 3 /* FileCreation.OPEN_EXISTING */, 0, NULL);
|
|
120
|
-
t.checkTrue(`job file exists (${outPrn})`, hOut !== -1);
|
|
121
|
-
if (hOut !== -1) {
|
|
122
|
-
const fSize = GetFileSize(hOut, NULL);
|
|
123
|
-
t.checkTrue(`job file size > 0 (${fSize})`, fSize > 0);
|
|
124
|
-
const fBuf = new PtrArrayBuffer(Math.max(fSize, 1));
|
|
125
|
-
const pRd = new PtrArrayBuffer(4);
|
|
126
|
-
ReadFile(hOut, fBuf, fSize, pRd, NULL);
|
|
127
|
-
// RAW 通道(winprint + RAW)应原样落盘——若 XPS 驱动重渲染则 FAIL 揭示实际内容头
|
|
128
|
-
let back = '';
|
|
129
|
-
const fDv = new DataView(fBuf);
|
|
130
|
-
for (let i = 0; i < Math.min(fSize, job.length); i++)
|
|
131
|
-
back += String.fromCharCode(fDv.getUint8(i));
|
|
132
|
-
t.check('job data roundtrip (RAW passthrough)', job, back);
|
|
133
|
-
CloseHandle(hOut);
|
|
134
|
-
}
|
|
135
|
-
DeleteFile(outPrn);
|
|
136
|
-
},
|
|
137
|
-
};
|