Files
StarPilot/openpilot/starpilot/galaxy/tests/test_model_laboratory.mjs
T
firestar5683 80fbb4f6db Feature registration
Register vehicle controls, planner modes, UI actions and Galaxy services after their providers exist. Wire the shared runtime entrypoints and cover their combined behavior.

Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com>
Co-authored-by: Daniel Koepping <elkoled@gmail.com>
Co-authored-by: Danny <dannyirwin31@yahoo.com>
Co-authored-by: DimitriChrysafis <Dimitri.chrysafis@gmail.com>
Co-authored-by: Dom <168790843+firestar5683@users.noreply.github.com>
Co-authored-by: Harald Schäfer <harald.the.engineer@gmail.com>
Co-authored-by: Kacper Rączy <gfw.kra@gmail.com>
Co-authored-by: Prabhaav Pillai <prabhaav@msu.edu>
Co-authored-by: dirwin31 <83434411+dirwin31@users.noreply.github.com>
Co-authored-by: firestarsdog <229254897+firestarsdog@users.noreply.github.com>
Co-authored-by: stef <19478336+stefpi@users.noreply.github.com>
Co-authored-by: whoisdomi <dcantu83@gmail.com>

Co-authored-by: Intelli <contact@intelli.software>
Co-authored-by: haraschax <6804392+haraschax@users.noreply.github.com>
Co-authored-by: prabhaavp <prabhaav@msu.edu>
2026-10-14 20:02:39 -05:00

71 lines
6.2 KiB
JavaScript

import assert from "node:assert/strict"
import { LaboratoryFeed, LaboratoryPage, laboratoryModels, laboratoryReady, laboratorySelectionError, laboratoryActionAllowed, laboratoryDraft, laboratoryStatusLabel } from "../web/js/model-laboratory.js"
const model = (value, version = 'v9') => ({ value, label: `Model ${value}`, version, small: true, modelLabEligible: true, modelLabArtifactAvailable: true, modelLabArtifactInstalled: true })
const payload = () => ({ schemaVersion: 1, isOnroad: false, chestnutReady: true, runtimeSupported: true,
configuration: { enabled: false, lateralModel: 'a', longitudinalModel: 'b' }, runtime: { active: false, requested: false },
download: { downloading: false, jobId: null, progress: '' }, models: [model('a','v8'), model('b','v9')],
capabilities: { configure: true, download: true, delete: true, cancel: true, refresh: true } })
const p = payload()
assert.equal(laboratorySelectionError(p,p.configuration), '') // mixed versions allowed
assert.equal(laboratoryActionAllowed(p,'enable',null,p.configuration), true)
assert.match(laboratorySelectionError({...p,runtimeSupported:false,runtimeUnavailableReason:'Pair runtime pending'},p.configuration), /Pair runtime pending/)
assert.match(laboratorySelectionError({...p,chestnutReady:false},p.configuration), /Chestnut/)
assert.match(laboratorySelectionError({...p,isOnroad:true},p.configuration), /Turn off the vehicle/)
assert.match(laboratorySelectionError(p,{lateralModel:'a',longitudinalModel:'a'}), /different/)
assert.match(laboratorySelectionError(p,{lateralModel:'a',longitudinalModel:'unknown'}), /downloaded/)
assert.equal(laboratorySelectionError(null,p.configuration), 'Waiting for parked device status')
const incompatible = [model('big'), model('qcom'), model('old')]
incompatible[0].small=false; incompatible[1].modelLabArtifactAvailable=false; incompatible[2].modelLabEligible=false
assert.equal(laboratoryModels({...p,models:incompatible}).length,3)
assert.equal(laboratoryReady({...p,models:incompatible}).length,0)
for (const row of incompatible) assert.equal(laboratoryActionAllowed(p,'download',row),false)
assert.equal(laboratoryReady({...p,models:[{...model('a'),modelLabArtifactInstalled:false}]}).length,0)
assert.equal(laboratoryActionAllowed({...p,configuration:{...p.configuration,enabled:true}},'delete',p.models[0]),false)
assert.equal(laboratoryActionAllowed(p,'delete',p.models[0]),true)
const missing = {...model('c'),modelLabArtifactInstalled:false}
assert.equal(laboratoryActionAllowed({...p,chestnutReady:false,models:[missing]},'download',missing),true)
assert.equal(laboratoryActionAllowed({...p,download:{downloading:true,jobId:'job'}},'cancel'),true)
assert.equal(laboratoryActionAllowed({...p,download:{downloading:true,jobId:'job'}},'enable'),false)
assert.deepEqual(laboratoryDraft(p,{lateralModel:'b',longitudinalModel:'a'},true),{enabled:false,lateralModel:'b',longitudinalModel:'a'})
assert.deepEqual(laboratoryDraft({...p,configuration:{enabled:false,lateralModel:'',longitudinalModel:''}},null,false),p.configuration)
function fixture() {
const requests=[],updates=[],timers=new Map();let next=0,unauthorized=0
const feed=new LaboratoryFeed({publish:u=>updates.push(u),unauthorized:()=>unauthorized++,
fetcher:(url,options)=>new Promise(resolve=>requests.push({url,options,resolve})),
later:(fn,ms)=>{timers.set(++next,{fn,ms});return next},cancel:id=>timers.delete(id)})
const flush=async()=>{for(let i=0;i<12;i++)await Promise.resolve()}
const reply=async(index,body,status=200)=>{requests[index].resolve({ok:status===200,status,json:async()=>body});await flush()}
return {feed,requests,updates,timers,reply,flush,get unauthorized(){return unauthorized}}
}
const f=fixture();f.feed.start();assert.equal(f.requests[0].url,'./api/models/laboratory');await f.reply(0,p)
assert.equal(f.feed.data.runtimeSupported,true)
const action=f.feed.action('enable',null,{enabled:false,lateralModel:'b',longitudinalModel:'a'})
assert.equal(f.requests[1].url,'./api/models/laboratory')
assert.equal(f.requests[1].options.method,'POST')
assert.equal(f.requests[1].options.credentials,'same-origin')
assert.deepEqual(JSON.parse(f.requests[1].options.body),{enabled:true,lateralModel:'b',longitudinalModel:'a'})
await f.reply(1,{...p,message:'Pair requested'});assert.equal(f.requests[2].url,'./api/models/laboratory');await f.reply(2,p);await action
const expiry=[...f.timers.values()].find(t=>t.ms===6000);expiry.fn()
assert.equal(f.feed.data,null);assert.match(f.updates.at(-1).error,/refresh delayed/)
assert.equal(await f.feed.action('enable',null,p.configuration),null)
f.feed.stop()
const invalid=fixture();invalid.feed.start();await invalid.reply(0,{...p,runtimeSupported:undefined});assert.equal(invalid.feed.data,null)
const retired=fixture();retired.feed.start();retired.feed.stop();await retired.reply(0,p);assert.equal(retired.feed.data,null)
const auth=fixture();auth.feed.start();await auth.reply(0,{message:'Unauthorized'},401);assert.equal(auth.unauthorized,1);assert.equal(auth.feed.active,false)
const download=fixture();download.feed.start();await download.reply(0,{...p,models:[missing]})
const downloading=download.feed.action('download',missing)
assert.equal(download.requests[1].url,'./api/models/laboratory/download');assert.deepEqual(JSON.parse(download.requests[1].options.body),{model:'c'})
await download.reply(1,{message:'Starting download'});await download.reply(2,{...p,download:{downloading:true,jobId:'job',progress:'40%'}});await downloading
const canceling=download.feed.action('cancel');assert.equal(download.requests[3].url,'./api/models/cancel');assert.deepEqual(JSON.parse(download.requests[3].options.body),{jobId:'job'})
await download.reply(3,{message:'Cancelling'});await download.reply(4,p);await canceling;download.feed.stop()
assert.ok(LaboratoryPage.template.indexOf('Available models')<LaboratoryPage.template.indexOf('Compose a pair'))
assert.match(LaboratoryPage.template,/GalaxySelect/)
assert.match(LaboratoryPage.template,/Catalog models/)
assert.match(LaboratoryPage.template,/modelLabReason/)
assert.equal(laboratoryStatusLabel({modelLabStatus:"runtime-unavailable"}), "Downloaded")
assert.equal(laboratoryStatusLabel({modelLabStatus:"unsupported"}), "Not supported")
assert.doesNotMatch(LaboratoryPage.template,/20 Hz/)
console.log('Model Laboratory contracts passed')