Back to home page

EIC code displayed by LXR

 
 

    


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

0001 /**
0002  * The routing contract: the control carries facts, the loaders decide.
0003  *
0004  * These specs pin the part that is easy to get wrong later - that a `.root`
0005  * file goes to whichever loader recognizes what is INSIDE it, and that no
0006  * format knowledge leaked into the router itself.
0007  */
0008 
0009 import { TestBed } from '@angular/core/testing';
0010 import { describe, expect, it, beforeEach, vi } from 'vitest';
0011 import type {
0012   DataSource,
0013   EventDataLoader,
0014   FileContentProbe,
0015   GeometryDataLoader,
0016 } from '@dexvis/firebird-core';
0017 import { EVENT_LOADERS, GEOMETRY_LOADERS } from '@dexvis/firebird-ng/api';
0018 import { FileOpenRouterService } from './file-open-router.service';
0019 import { RootFileService } from './root-file.service';
0020 
0021 const GEOMETRY_PROBE: FileContentProbe = {
0022   name: 'detector.root',
0023   entries: [
0024     { name: 'Default', className: 'TGeoManager' },
0025     { name: 'StreamerInfo', className: 'TList' },
0026   ],
0027 };
0028 
0029 const EVENTS_PROBE: FileContentProbe = {
0030   name: 'reco.edm4eic.root',
0031   entries: [
0032     { name: 'events', className: 'TTree' },
0033     { name: 'StreamerInfo', className: 'TList' },
0034   ],
0035 };
0036 
0037 const HISTOGRAM_PROBE: FileContentProbe = {
0038   name: 'plots.root',
0039   entries: [{ name: 'hMass', className: 'TH1F' }],
0040 };
0041 
0042 class FakeGeometryLoader implements GeometryDataLoader {
0043   readonly meta = { id: 'fake-geometry', label: 'Fake geometry', fileExtensions: ['.root'] };
0044   load = vi.fn(() => Promise.reject(new Error('not called in this spec')));
0045   canLoad(source: DataSource): boolean {
0046     return name(source).endsWith('.root');
0047   }
0048   canLoadContent(probe: FileContentProbe): boolean {
0049     return probe.entries.some(entry => entry.className === 'TGeoManager');
0050   }
0051 }
0052 
0053 class FakeEventLoader implements EventDataLoader {
0054   readonly meta = { id: 'fake-events', label: 'Fake events', fileExtensions: ['.root'] };
0055   loadEvents = vi.fn(() => Promise.reject(new Error('not called in this spec')));
0056   canLoad(source: DataSource): boolean {
0057     return name(source).endsWith('.root');
0058   }
0059   canLoadContent(probe: FileContentProbe): boolean {
0060     return probe.entries.some(entry => entry.name === 'events' && entry.className === 'TTree');
0061   }
0062 }
0063 
0064 /** A loader that claims a name nothing else does, and cannot be probed. */
0065 class FakeDexLoader implements EventDataLoader {
0066   readonly meta = { id: 'fake-dex', label: 'Fake DEX', fileExtensions: ['.firebird.json'] };
0067   loadEvents = vi.fn(() => Promise.reject(new Error('not called in this spec')));
0068   canLoad(source: DataSource): boolean {
0069     return name(source).endsWith('.firebird.json');
0070   }
0071 }
0072 
0073 function name(source: DataSource): string {
0074   return typeof source === 'string' ? source : source.name;
0075 }
0076 
0077 describe('FileOpenRouterService', () => {
0078   let probe: ReturnType<typeof vi.fn>;
0079 
0080   function setup(probeResult: FileContentProbe): FileOpenRouterService {
0081     probe = vi.fn(async () => probeResult);
0082     TestBed.configureTestingModule({
0083       providers: [
0084         { provide: GEOMETRY_LOADERS, useValue: [new FakeGeometryLoader()], multi: false },
0085         { provide: EVENT_LOADERS, useValue: [new FakeDexLoader(), new FakeEventLoader()], multi: false },
0086         { provide: RootFileService, useValue: { probe } },
0087       ],
0088     });
0089     return TestBed.inject(FileOpenRouterService);
0090   }
0091 
0092   beforeEach(() => TestBed.resetTestingModule());
0093 
0094   it('sends a ROOT file holding a TGeoManager to the geometry loader', async () => {
0095     const router = setup(GEOMETRY_PROBE);
0096     const route = await router.route('detector.root');
0097     expect(route.kind).toBe('geometry');
0098     expect(probe).toHaveBeenCalledOnce();
0099   });
0100 
0101   it('sends a ROOT file holding an events tree to the event loader', async () => {
0102     const router = setup(EVENTS_PROBE);
0103     const route = await router.route('reco.edm4eic.root');
0104     expect(route.kind).toBe('events');
0105   });
0106 
0107   it('routes a dropped File the same way as a URL', async () => {
0108     const router = setup(EVENTS_PROBE);
0109     const file = new File([new Uint8Array(4)], 'dropped.root');
0110     const route = await router.route(file);
0111     expect(route.kind).toBe('events');
0112     expect(probe).toHaveBeenCalledWith(file);
0113   });
0114 
0115   it('reports what an unrecognized ROOT file holds instead of guessing', async () => {
0116     const router = setup(HISTOGRAM_PROBE);
0117     const route = await router.route('plots.root');
0118     expect(route.kind).toBe('unknown');
0119     if (route.kind === 'unknown') {
0120       expect(route.message).toContain('hMass (TH1F)');
0121       // StreamerInfo is noise in a message meant for a human
0122       expect(route.message).not.toContain('StreamerInfo');
0123     }
0124   });
0125 
0126   it('does not probe when the name is already unambiguous', async () => {
0127     const router = setup(EVENTS_PROBE);
0128     const route = await router.route('sample.firebird.json');
0129     expect(route.kind).toBe('events');
0130     if (route.kind === 'events') expect(route.loader.meta.id).toBe('fake-dex');
0131     expect(probe).not.toHaveBeenCalled();
0132   });
0133 
0134   it('says so when nothing claims the source', async () => {
0135     const router = setup(EVENTS_PROBE);
0136     const route = await router.route('notes.txt');
0137     expect(route.kind).toBe('unknown');
0138     if (route.kind === 'unknown') expect(route.message).toContain('No loader claims');
0139     expect(probe).not.toHaveBeenCalled();
0140   });
0141 });