'use strict'; const { test } = require('node:test'), assert = require('node:assert/strict'); const fs = require('node:fs'), path = require('node:path'), vm = require('node:vm'), zlib = require('node:zlib'); const root = path.resolve(__dirname, '..'); function runtime() { // No document, Image, canvas, fetch, process, Buffer, global, self, window or // Node module resolution. Only explicitly allowlisted packaged modules are permitted. const context = vm.createContext({ console: { log() {}, warn() {}, error() {} }, setTimeout, clearTimeout }); const cache = {}; function load(file) { file = path.resolve(file); if (cache[file]) return cache[file].exports; const module = { exports: {} }; cache[file] = module; const invoke = vm.runInContext('(function(exports, require, module) {' + fs.readFileSync(file, 'utf8') + '\n})', context, { filename: file }); invoke(module.exports, name => { assert(name.startsWith('.'), 'no runtime Node module: ' + name); const target = path.resolve(path.dirname(file), name + '.js'); assert(target.startsWith(path.join(root, 'vendor/native-face') + path.sep) || ['native-face-cpu.js', 'native-face-webgl.js', 'native-face-webgl-probe.js', 'face-luma.js'].some(name => target === path.join(root, 'utils', name))); return load(target); }, module); return module.exports; } return { api: load(path.join(root, 'utils/native-face.js')), load, context }; } function blank(format = 'RGBA') { return { width: 128, height: 128, pixelFormat: format, data: new Uint8Array(128 * 128 * 4) }; } function fixture() { const value = JSON.parse(fs.readFileSync(path.join(__dirname, 'fixtures/native-face-c3.json'), 'utf8')); return { width: value.width, height: value.height, pixelFormat: value.pixelFormat, data: new Uint8Array(zlib.gunzipSync(Buffer.from(value.gzipBase64, 'base64'))) }; } test('real quantized models load and detect a local reference face with 68 landmarks, without DOM or Node runtime globals', async t => { const r = runtime(), engine = r.api.create({ inputSize: 256, maxSide: 320 }); const started = performance.now(); await engine.init(); const initMs = performance.now() - started; const baseline = engine.stats(); const result = await engine.detect(fixture()); assert.equal(result.backend, 'cpu'); assert(result.detections.length > 0, 'reference photo must exercise actual detection and landmark networks'); assert.equal(result.width, 320); assert.equal(result.height, 240); assert(Number.isFinite(result.luma) && result.luma >= 0 && result.luma <= 255, 'real inference measures its detected face brightness'); const detection = result.detections[0]; assert(detection.detection.score > 0.35); assert(detection.detection.box.width > 20); assert.equal(detection.landmarks.positions.length, 68); assert(detection.landmarks.positions.every(p => Number.isFinite(p.x) && Number.isFinite(p.y) && p.x >= 0 && p.y >= 0 && p.x <= 320 && p.y <= 240)); assert.equal(engine.stats().numTensors, baseline.numTensors); t.diagnostic(JSON.stringify({ initMs: Math.round(initMs), inferenceMs: result.elapsedMs, modelsBytes: baseline.numBytes, modelsTensors: baseline.numTensors })); engine.dispose(); assert.equal(engine.stats().numTensors, 0); assert.equal(engine.stats().numBytes, 0); }); test('downsampled boxes and all 68 landmarks return to original raw frame pixels without mirroring', async () => { const r = runtime(), engine = r.api.create(), small = fixture(); const large = { width: 640, height: 480, pixelFormat: 'RGBA', data: new Uint8Array(640 * 480 * 4) }; for (let y = 0; y < 480; y++) for (let x = 0; x < 640; x++) { const source = (Math.floor(y / 2) * 320 + Math.floor(x / 2)) * 4; large.data.set(small.data.subarray(source, source + 4), (y * 640 + x) * 4); } const a = await engine.detect(small), b = await engine.detect(large); assert.equal(b.width, 640); assert.equal(b.height, 480); assert.equal(a.detections.length, b.detections.length); for (let index = 0; index < a.detections.length; index++) { for (const key of ['x', 'y', 'width', 'height']) assert(Math.abs(a.detections[index].detection.box[key] * 2 - b.detections[index].detection.box[key]) < 0.001); for (let point = 0; point < 68; point++) for (const key of ['x', 'y']) assert(Math.abs(a.detections[index].landmarks.positions[point][key] * 2 - b.detections[index].landmarks.positions[point][key]) < 0.001); } engine.dispose(); assert.equal(engine.stats().numTensors, 0); }); test('supported byte layouts produce the same tensor results, and explicit unsupported formats never use a fallback', async () => { const r = runtime(), engine = r.api.create({ inputSize: 128 }); await engine.init(); const original = fixture(), reduced = { width: 128, height: 96, pixelFormat: 'RGBA', data: new Uint8Array(128 * 96 * 4) }; for (let y = 0; y < 96; y++) for (let x = 0; x < 128; x++) { const at = (Math.floor(y * 240 / 96) * 320 + Math.floor(x * 320 / 128)) * 4; reduced.data.set(original.data.subarray(at, at + 4), (y * 128 + x) * 4); } const expected = await engine.detect(reduced), baseline = engine.stats().numTensors; for (const [layout, order] of [['BGRA', [2, 1, 0, 3]], ['ARGB', [3, 0, 1, 2]], ['ABGR', [3, 2, 1, 0]]]) { const data = new Uint8Array(reduced.data.length); for (let offset = 0; offset < data.length; offset += 4) for (let channel = 0; channel < 4; channel++) data[offset + channel] = reduced.data[offset + order[channel]]; const actual = await engine.detect({ ...reduced, pixelFormat: layout, data }); assert.equal(JSON.stringify(actual.detections) === JSON.stringify(expected.detections), true, 'channel layouts must yield identical normalized results'); assert.equal(actual.luma, expected.luma, 'pixel-layout conversion also preserves measured face brightness'); } await assert.rejects(engine.detect(blank('NV21')), error => error.code === 'NATIVE_FACE_FORMAT'); await assert.rejects(engine.detect({ ...blank(), stride: 516 }), error => error.code === 'NATIVE_FACE_STRIDE'); await assert.rejects(engine.detect({ ...blank(), rotation: 90 }), error => error.code === 'NATIVE_FACE_ROTATION'); const missing = blank(); delete missing.pixelFormat; const noFace = await engine.detect(missing); assert.equal(noFace.faceCount, 0); assert.equal(noFace.luma, null); assert.equal(engine.stats().numTensors, baseline); engine.dispose(); assert.equal(engine.stats().numTensors, 0); }); test('inference is serialized and disposal during init or detection safely rejects without releasing live tensors early', async () => { const r = runtime(), first = r.api.create(), second = r.api.create(); const a = first.init(), b = first.init(); first.dispose(); await Promise.all([assert.rejects(a, error => error.code === 'NATIVE_FACE_DISPOSED'), assert.rejects(b, error => error.code === 'NATIVE_FACE_DISPOSED')]); await second.init(); assert(second.stats().numTensors > 0); const work = second.detect(blank()); await assert.rejects(second.detect(blank()), error => error.code === 'NATIVE_FACE_BUSY'); second.dispose(); await assert.rejects(work, error => error.code === 'NATIVE_FACE_DISPOSED'); assert.equal(second.stats().numTensors, 0); await assert.rejects(second.init(), error => error.code === 'NATIVE_FACE_DISPOSED'); const next = r.api.create({ inputSize: 128 }); await next.init(); for (let index = 0; index < 3; index++) assert.equal((await next.detect(blank())).faceCount, 0); next.dispose(); assert.equal(next.stats().numTensors, 0); }); test('detect snapshots downsampled RGB before yielding, so a recycled native byte buffer cannot change inference', async () => { const r = runtime(), engine = r.api.create({ inputSize: 128 }); await engine.init(); const expected = await engine.detect(fixture()), frame = fixture(); const work = engine.detect(frame); frame.data.fill(0); const actual = await work; assert(actual.detections.length > 0); assert.equal(JSON.stringify(actual.detections) === JSON.stringify(expected.detections), true, 'a recycled source buffer must not change the original inference'); engine.dispose(); assert.equal(engine.stats().numTensors, 0); }); test('generated vendor has no static Node imports and retains source provenance and upstream notices', () => { const bundle = fs.readFileSync(path.join(root, 'vendor/native-face/face-api.js'), 'utf8'); assert.doesNotMatch(bundle, /\brequire\s*\(/); assert.match(bundle, /Copyright 2020 Google LLC/); const provenance = JSON.parse(fs.readFileSync(path.join(root, 'vendor/native-face/provenance.json'), 'utf8')); assert.equal(provenance.quantizedBytes, 627385); for (const name of ['crypto', 'node-fetch', 'util', 'fs', '@tensorflow/tfjs-node']) assert.equal(provenance.nodeOnlyStubs[name], 1); assert.equal(provenance.sourceFiles.length, 7); assert.match(fs.readFileSync(path.join(root, 'vendor/native-face/LICENSE.face-api.txt'), 'utf8'), /Copyright \(c\) 2018 Vincent Mühler/); assert.match(fs.readFileSync(path.join(root, 'vendor/native-face/LICENSE.tensorflow.txt'), 'utf8'), /Apache License/); }); test('observed 540x960 portrait RGBA shape without format survives buffer reuse and returns real landmarks in original dimensions', async t => { const r = runtime(), engine = r.api.create({ defaultPixelFormat: 'RGBA', maxSide: 320, inputSize: 256 }); await engine.init(); const baseline = engine.stats().numTensors, reference = fixture(); // Match the observed iPhone frame shape and byte length. Fit the 4:3 local // reference at 540x405, centered on an opaque 540x960 background: no face // aspect-ratio distortion and no network/device/image decoder dependency. const frame = { width: 540, height: 960, data: new Uint8Array(540 * 960 * 4) }; for (let offset = 3; offset < frame.data.length; offset += 4) frame.data[offset] = 255; const fittedWidth = 540, fittedHeight = 405; const left = Math.floor((frame.width - fittedWidth) / 2), top = Math.floor((frame.height - fittedHeight) / 2); assert.equal(fittedWidth / fittedHeight, reference.width / reference.height); for (let y = 0; y < fittedHeight; y++) for (let x = 0; x < fittedWidth; x++) { const source = (Math.floor(y * reference.height / fittedHeight) * reference.width + Math.floor(x * reference.width / fittedWidth)) * 4; frame.data.set(reference.data.subarray(source, source + 4), ((y + top) * frame.width + x + left) * 4); } assert.equal(frame.data.byteLength, 2073600); assert.equal(frame.pixelFormat, undefined); assert.equal(frame.format, undefined); const pending = engine.detect(frame); frame.data.fill(0); // Reproduce a native camera recycling its storage immediately. const result = await pending; assert.equal(result.width, 540); assert.equal(result.height, 960); assert(result.faceCount > 0, 'real model must detect the centered reference after the source buffer was recycled'); assert(result.detections.length > 0); for (const detected of result.detections) { const box = detected.detection.box; assert(detected.detection.score > 0.35); assert(Number.isFinite(box.x) && Number.isFinite(box.y) && box.x >= 0 && box.y >= 0 && box.width > 0 && box.height > 0); assert(box.x + box.width <= 540 && box.y + box.height <= 960); assert.equal(detected.landmarks.positions.length, 68); assert(detected.landmarks.positions.every(point => Number.isFinite(point.x) && Number.isFinite(point.y) && point.x >= 0 && point.y >= 0 && point.x <= 540 && point.y <= 960)); } assert.equal(engine.stats().numTensors, baseline); t.diagnostic(JSON.stringify({ portraitFrameBytes: 2073600, inferenceMs: result.elapsedMs, faceCount: result.faceCount, landmarkCount: result.detections[0].landmarks.positions.length })); engine.dispose(); assert.equal(engine.stats().numTensors, 0); }); test('pad-copy specialization is bit-exact to the original CPU pad across ranks, dtypes, offsets and constants', async () => { const r = runtime(), face = r.load(path.join(root, 'vendor/native-face/face-api.js')); await face.tf.setBackend('cpu'); const controller = r.load(path.join(root, 'utils/native-face-cpu.js'))(face); const cases = [ { shape: [3], pads: [[2, 1]] }, { shape: [2, 3], pads: [[1, 2], [2, 1]] }, { shape: [2, 3, 4], pads: [[1, 0], [2, 3], [0, 0]] }, { shape: [2, 3, 4, 3], pads: [[0, 0], [1, 2], [2, 1], [0, 0]] }, { shape: [2, 3, 4, 3], pads: [[1, 0], [0, 0], [0, 0], [1, 2]] }, { shape: [2, 3, 4, 3], pads: [[0, 0], [0, 0], [0, 0], [0, 0]] } ]; for (const dtype of ['float32', 'int32', 'bool']) for (const constant of [0, 3]) for (const item of cases) { const size = item.shape.reduce((a, b) => a * b, 1); const input = face.tf.tensor(Array.from({ length: size }, (_, i) => dtype === 'float32' ? (i % 11 - 5) / 7 : i % 2), item.shape, dtype); controller.setEnabled(false); const reference = face.tf.pad(input, item.pads, constant); controller.setEnabled(true); const optimized = face.tf.pad(input, item.pads, constant); const a = reference.dataSync(), b = optimized.dataSync(); assert.equal(a.length, b.length); assert(Array.from(a).every((value, index) => Object.is(value, b[index])), 'pad values must be bit-exact'); input.dispose(); reference.dispose(); optimized.dispose(); } assert.equal(face.tf.memory().numTensors, 0); }); test('tiny retains 68-point geometry, exact pad optimization preserves results, and real multi-face frames stay rejected', async () => { const r = runtime(), full = r.api.create({ inputSize: 160, landmarkModel: 'full', cpuOptimization: false }); const baseline = await full.detect(fixture()); full.dispose(); const fastFull = r.api.create({ inputSize: 160, landmarkModel: 'full' }); const fullResult = await fastFull.detect(fixture()); fastFull.dispose(); assert.equal(JSON.stringify(baseline.detections) === JSON.stringify(fullResult.detections), true, 'pad optimization must preserve complete real-network results exactly'); const tiny = r.api.create({ inputSize: 160, landmarkModel: 'tiny' }), result = await tiny.detect(fixture()); assert.equal(result.cpuOptimization, 'pad-copy'); assert.equal(result.landmarkModel, 'tiny'); assert.equal(result.detections[0].landmarks.positions.length, 68); const old = baseline.detections[0], current = result.detections[0]; const distances = current.landmarks.positions.map((point, index) => Math.hypot(point.x - old.landmarks.positions[index].x, point.y - old.landmarks.positions[index].y)); assert(distances.reduce((sum, value) => sum + value, 0) / 68 / old.detection.box.width < 0.025, 'tiny model must preserve landmark index geometry on the reference face'); const source = fixture(), duo = { width: 320, height: 240, data: new Uint8Array(320 * 240 * 4) }; // Crop a reference face with margin, then fit two copies with the same scale // on both axes. No stretched faces or mocked detector results. const box = baseline.detections[0].detection.box; const roiX = Math.max(0, Math.floor(box.x - box.width * 0.1)), roiY = Math.max(0, Math.floor(box.y - box.height * 0.1)); const roiW = Math.min(320 - roiX, Math.ceil(box.width * 1.2)), roiH = Math.min(240 - roiY, Math.ceil(box.height * 1.2)); const scale = Math.min(140 / roiW, 220 / roiH), fittedWidth = Math.round(roiW * scale), fittedHeight = Math.round(roiH * scale); for (let side = 0; side < 2; side++) for (let y = 0; y < fittedHeight; y++) for (let x = 0; x < fittedWidth; x++) { const at = ((roiY + Math.floor(y / scale)) * 320 + roiX + Math.floor(x / scale)) * 4; const to = ((y + Math.floor((240 - fittedHeight) / 2)) * 320 + x + side * 160 + Math.floor((160 - fittedWidth) / 2)) * 4; duo.data.set(source.data.subarray(at, at + 4), to); } const multiple = await tiny.detect(duo); assert(multiple.faceCount >= 2); assert.equal(multiple.detections.length, 2); assert(multiple.detections.every(item => item.landmarks.positions.length === 68)); const verdict = require('../utils/native-face-quality').evaluate(multiple, 'front', { viewWidth: 320, viewHeight: 240 }); assert.equal(verdict.ok, false); assert.match(verdict.hint, /单人/); tiny.dispose(); assert.equal(tiny.stats().numTensors, 0); }); test('a failed optional full-model load does not poison the ready tiny engine or retain decoded tensors', async () => { const r = runtime(), tiny = r.api.create({ inputSize: 160, landmarkModel: 'tiny' }); await tiny.init(); const baseline = tiny.stats().numTensors; const face = r.load(path.join(root, 'vendor/native-face/face-api.js')); const original = face.nets.faceLandmark68Net.loadFromWeightMap; face.nets.faceLandmark68Net.loadFromWeightMap = function () { throw new Error('synthetic model load failure'); }; const failing = r.api.create({ landmarkModel: 'full' }); await assert.rejects(failing.init(), /synthetic model load failure/); failing.dispose(); assert.equal(tiny.stats().numTensors, baseline); const result = await tiny.detect(fixture()); assert(result.faceCount > 0); assert.equal(result.detections[0].landmarks.positions.length, 68); assert.equal(tiny.stats().numTensors, baseline); face.nets.faceLandmark68Net.loadFromWeightMap = original; const retry = r.api.create({ landmarkModel: 'full' }); await retry.init(); assert(retry.stats().numTensors > baseline); tiny.dispose(); assert(retry.stats().numTensors > 0); retry.dispose(); assert.equal(retry.stats().numTensors, 0); }); test('WebGL admission is exclusive and a missing native Canvas API fails explicitly without a CPU fallback', async () => { const r = runtime(), cpu = r.api.create(); await cpu.init(); const conflict = r.api.create({ backend: 'webgl', platform: {} }); await assert.rejects(conflict.init(), error => error.code === 'NATIVE_FACE_BACKEND_CONFLICT'); await assert.rejects(conflict.detect(blank()), error => error.code === 'NATIVE_FACE_BACKEND_CONFLICT'); conflict.dispose(); assert.equal((await cpu.detect(blank())).backend, 'cpu'); cpu.dispose(); const missing = r.api.create({ backend: 'webgl', platform: {} }); await assert.rejects(missing.init(), error => error.code === 'NATIVE_FACE_WEBGL_API_MISSING' && error.gpu.selftest === 'failed'); assert.equal(missing.stats().backend, ''); assert.equal(missing.stats().gpu.requested, true); assert.equal(missing.stats().gpu.code, 'API_MISSING'); assert.equal(missing.stats().numTensors, 0); missing.dispose(); const next = r.api.create(); await next.init(); next.dispose(); assert.equal(next.stats().numTensors, 0); }); test('native cleanup exceptions cannot preserve a busy lock, prevent backend retirement, or replace the original failure', async () => { const r = runtime(), engine = r.api.create(); await engine.init(); const face = r.load(path.join(root, 'vendor/native-face/face-api.js')), tensorDispose = face.tf.Tensor.prototype.dispose; const netDispose = face.nets.faceLandmark68TinyNet.dispose; face.tf.Tensor.prototype.dispose = function () { tensorDispose.call(this); throw new Error('synthetic native cleanup error'); }; const rejected = engine.detect(blank()); engine.dispose(); await assert.rejects(rejected, error => error.code === 'NATIVE_FACE_DISPOSED'); assert.equal(engine.stats().busy, false); assert.equal(engine.stats().numTensors, 0); face.tf.Tensor.prototype.dispose = tensorDispose; // Each model's native cleanup must be independent of the other two models. const next = r.api.create(); await next.init(); face.nets.faceLandmark68TinyNet.dispose = function (value) { netDispose.call(this, value); throw new Error('synthetic model dispose error'); }; next.dispose(); assert.equal(next.stats().numTensors, 0); face.nets.faceLandmark68TinyNet.dispose = netDispose; const recovered = r.api.create({ inputSize: 128 }); await recovered.init(); assert.equal((await recovered.detect(blank())).faceCount, 0); recovered.dispose(); assert.equal(recovered.stats().numTensors, 0); });