75 lines
3.8 KiB
JavaScript
75 lines
3.8 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import {readFileSync} from 'node:fs';
|
|
import vm from 'node:vm';
|
|
|
|
function camera() {
|
|
const workers = [];
|
|
const context = {setTimeout, clearTimeout, Subprocess: {async call(options) {
|
|
let finish;
|
|
const done = new Promise(resolve => { finish = resolve; });
|
|
const worker = {closed:false, writes:[], options,
|
|
stdin:{async close() {worker.closed=true;finish();}, async write(text) {worker.writes.push(text);}},
|
|
wait:()=>done, kill:()=>finish()};
|
|
workers.push(worker); return worker;
|
|
}}};
|
|
const source = readFileSync(new URL('../host/NativeCamera.sys.mjs', import.meta.url),'utf8')
|
|
.replace(/^import .*;\n/gm,'').replace('export class NativeCamera','globalThis.NativeCamera = class NativeCamera');
|
|
vm.runInNewContext(source,context);
|
|
const service = new context.NativeCamera('beatles');
|
|
service.packet = async () => ({ready:true,format:'nv21',...service.previewSize});
|
|
return {service,workers};
|
|
}
|
|
|
|
test('a delayed stop or frame from the old camera cannot close its replacement',async()=>{
|
|
const {service,workers}=camera(), actor={};
|
|
const old=await service.request(actor,{action:'start',camera:'back'});
|
|
const next=await service.request(actor,{action:'start',camera:'back'});
|
|
assert.notEqual(old.session,next.session);assert.equal(workers[0].closed,true);
|
|
await service.request(actor,{action:'stop',session:old.session});
|
|
assert.equal(workers[1].closed,false);
|
|
await assert.rejects(service.request(actor,{action:'frame',session:old.session}),/CAMERA_NOT_OPEN/);
|
|
assert.equal(workers[1].closed,false);
|
|
await service.request(actor,{action:'frame',session:next.session});
|
|
assert.deepEqual(workers[1].writes,['frame\n']);
|
|
await service.request(actor,{action:'stop',session:next.session});
|
|
assert.equal(workers[1].closed,true);
|
|
});
|
|
|
|
test('Beatles uses the stock KaiOS 320x240 preview while other devices keep 640x480',async()=>{
|
|
const {service,workers}=camera(), actor={};
|
|
const metadata=await service.request(actor,{action:'start',camera:'back'});
|
|
assert.equal(service.previewSize.width,320);
|
|
assert.equal(service.previewSize.height,240);
|
|
assert.equal(metadata.width,320);
|
|
assert.equal(metadata.height,240);
|
|
assert.equal(metadata.frameRate,5);
|
|
assert.deepEqual([...metadata.cameras],['back']);
|
|
assert.equal(workers[0].options.command,'/usr/bin/python3.11');
|
|
assert.equal(workers[0].options.arguments[1],'/opt/vulpes/tundra/runtime/beatles-camera-client.py');
|
|
await service.request(actor,{action:'stop',session:metadata.session});
|
|
|
|
const sargo=new service.constructor('sargo');
|
|
assert.equal(sargo.previewSize.width,640);
|
|
assert.equal(sargo.previewSize.height,480);
|
|
sargo.packet=async()=>({ready:true,format:'nv21',...sargo.previewSize});
|
|
const sargoMetadata=await sargo.request(actor,{action:'start',camera:'back'});
|
|
assert.equal(sargoMetadata.frameRate,30);
|
|
await sargo.request(actor,{action:'stop',session:sargoMetadata.session});
|
|
});
|
|
|
|
test('Beatles reports the missing front sensor instead of opening its rear sensor twice',async()=>{
|
|
const {service,workers}=camera(), actor={};
|
|
await assert.rejects(service.request(actor,{action:'start',camera:'front'}),/CAMERA_UNAVAILABLE/);
|
|
assert.equal(workers.length,0);
|
|
});
|
|
|
|
test('a different actor cannot stop or use the current camera',async()=>{
|
|
const {service,workers}=camera(), owner={}, other={};
|
|
const meta=await service.request(owner,{action:'start'});
|
|
await service.request(other,{action:'stop',session:meta.session});
|
|
await assert.rejects(service.request(other,{action:'frame',session:meta.session}),/CAMERA_NOT_OPEN/);
|
|
await assert.rejects(service.request(other,{action:'start'}),/CAMERA_BUSY/);
|
|
assert.equal(workers[0].closed,false);
|
|
await service.request(owner,{action:'stop',session:meta.session});
|
|
});
|