Back to home page

EIC code displayed by LXR

 
 

    


Warning, /firebird/packages/firebird-ng/display/src/lib/services/root-file.service.spec.ts is written in an unsupported language. File is not indexed.

0001 /**
0002  * RootFileService handles: each client works on its own handle id, closing a
0003  * handle touches only that handle, and overlapping opens show the latest.
0004  * The worker is replaced by a fake that records requests and answers on
0005  * demand.
0006  */
0007 
0008 import { TestBed } from '@angular/core/testing';
0009 import { provideHttpClient } from '@angular/common/http';
0010 import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
0011 import { ServerConfigService } from '@dexvis/app-features';
0012 import { RootFileService } from './root-file.service';
0013 import { withWorkers } from '@dexvis/firebird-ng/api';
0014 import type { RootFileRequest, RootFileResponse } from '@dexvis/firebird-ng/workers/root-file';
0015 
0016 class FakeWorker {
0017   static instances: FakeWorker[] = [];
0018   readonly requests: RootFileRequest[] = [];
0019   onmessage: ((event: { data: RootFileResponse }) => void) | null = null;
0020   onerror: ((event: { message: string }) => void) | null = null;
0021   terminated = false;
0022 
0023   constructor() {
0024     FakeWorker.instances.push(this);
0025   }
0026 
0027   postMessage(request: RootFileRequest): void {
0028     this.requests.push(request);
0029   }
0030 
0031   terminate(): void {
0032     this.terminated = true;
0033   }
0034 
0035   /** Answers an open request. */
0036   opened(request: RootFileRequest, entryCount: number): void {
0037     this.onmessage?.({
0038       data: {
0039         type: 'opened',
0040         requestId: request.requestId,
0041         sourceName: `file-${request.requestId}`,
0042         model: 'edm4eic',
0043         entryCount,
0044         collectionGroups: ['tracker_hits'],
0045       },
0046     });
0047   }
0048 }
0049 
0050 describe('RootFileService handles', () => {
0051   let service: RootFileService;
0052   const worker = () => FakeWorker.instances[0];
0053 
0054   beforeEach(() => {
0055     FakeWorker.instances = [];
0056     vi.stubGlobal('Worker', FakeWorker);
0057     TestBed.resetTestingModule();
0058     const startFake = () => new FakeWorker() as unknown as Worker;
0059     TestBed.configureTestingModule({
0060       providers: [provideHttpClient(), ...withWorkers({ geometry: startFake, rootFile: startFake }).providers],
0061     });
0062     TestBed.inject(ServerConfigService).setUnitTestConfig({});
0063     service = TestBed.inject(RootFileService);
0064   });
0065 
0066   afterEach(() => vi.unstubAllGlobals());
0067 
0068   it('sends each handle its own id, and closing one rejects only its own requests', async () => {
0069     const picker = service.createHandle();
0070     const display = service.createHandle();
0071     const pickerOpen = picker.open('https://h/a.root');
0072     const displayOpen = display.open('https://h/b.root');
0073     const [openA, openB] = worker().requests;
0074     expect(openA).toEqual(expect.objectContaining({ type: 'open', handle: picker.id }));
0075     expect(openB).toEqual(expect.objectContaining({ type: 'open', handle: display.id }));
0076     expect(picker.id).not.toBe(display.id);
0077 
0078     picker.close();
0079     await expect(pickerOpen).rejects.toThrow('The ROOT file was closed');
0080     expect(worker().requests[2]).toEqual(expect.objectContaining({ type: 'close', handle: picker.id }));
0081     expect(worker().terminated).toBe(false);
0082 
0083     worker().opened(openB, 7);
0084     expect((await displayOpen).entryCount).toBe(7);
0085     expect(display.openedFile()?.entryCount).toBe(7);
0086     expect(display.busy()).toBe(false);
0087   });
0088 
0089   it('sends the conversion options with the convert request', () => {
0090     const handle = service.createHandle();
0091     void handle.convert('0-2', { collections: ['mc_trajectories'], trajectoryExcludedCollections: ['PhotonHits'] });
0092     expect(worker().requests[0]).toEqual(expect.objectContaining({
0093       type: 'convert',
0094       handle: handle.id,
0095       entries: '0-2',
0096       collections: ['mc_trajectories'],
0097       trajectoryExcludedCollections: ['PhotonHits'],
0098     }));
0099   });
0100 
0101   it('shows the latest open of a handle when an older one answers later', async () => {
0102     const handle = service.createHandle();
0103     const first = handle.open('https://h/old.root');
0104     const second = handle.open('https://h/new.root');
0105     const [openOld, openNew] = worker().requests;
0106     worker().opened(openNew, 2);
0107     await second;
0108     worker().opened(openOld, 99);
0109     await first;
0110     expect(handle.openedFile()?.entryCount).toBe(2);
0111   });
0112 
0113   it('starts a fresh worker after the worker failed', async () => {
0114     const handle = service.createHandle();
0115     const open = handle.open('https://h/a.root');
0116     worker().onerror?.({ message: 'chunk failed to load' });
0117     await expect(open).rejects.toThrow('ROOT file worker error: chunk failed to load');
0118     expect(worker().terminated).toBe(true);
0119     void handle.open('https://h/a.root').catch(() => undefined);
0120     expect(FakeWorker.instances.length).toBe(2);
0121   });
0122 
0123   it('names the missing feature when the application registered no workers', async () => {
0124     TestBed.resetTestingModule();
0125     TestBed.configureTestingModule({ providers: [provideHttpClient()] });
0126     TestBed.inject(ServerConfigService).setUnitTestConfig({});
0127     const bare = TestBed.inject(RootFileService);
0128     await expect(bare.probe('https://h/a.root')).rejects.toThrow('add withWorkers');
0129   });
0130 });