file_loader.ts (3764B)
1 import { IterableTestGroup } from '../internal/test_group.js'; 2 import { assert } from '../util/util.js'; 3 4 import { parseQuery } from './query/parseQuery.js'; 5 import { TestQuery } from './query/query.js'; 6 import { TestSuiteListing } from './test_suite_listing.js'; 7 import { loadTreeForQuery, TestTree, TestTreeLeaf } from './tree.js'; 8 9 // A listing file, e.g. either of: 10 // - `src/webgpu/listing.ts` (which is dynamically computed, has a Promise<TestSuiteListing>) 11 // - `out/webgpu/listing.js` (which is pre-baked, has a TestSuiteListing) 12 interface ListingFile { 13 listing: Promise<TestSuiteListing> | TestSuiteListing; 14 } 15 16 // A .spec.ts file, as imported. 17 export interface SpecFile { 18 readonly description: string; 19 readonly g: IterableTestGroup; 20 } 21 22 export interface ImportInfo { 23 url: string; 24 } 25 26 interface TestFileLoaderEventMap { 27 import: MessageEvent<ImportInfo>; 28 imported: MessageEvent<ImportInfo>; 29 finish: MessageEvent<void>; 30 } 31 32 // Override the types for addEventListener/removeEventListener so the callbacks can be used as 33 // strongly-typed. 34 /* eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging */ 35 export interface TestFileLoader extends EventTarget { 36 addEventListener<K extends keyof TestFileLoaderEventMap>( 37 type: K, 38 listener: (this: TestFileLoader, ev: TestFileLoaderEventMap[K]) => void, 39 options?: boolean | AddEventListenerOptions 40 ): void; 41 addEventListener( 42 type: string, 43 listener: EventListenerOrEventListenerObject, 44 options?: boolean | AddEventListenerOptions 45 ): void; 46 removeEventListener<K extends keyof TestFileLoaderEventMap>( 47 type: K, 48 listener: (this: TestFileLoader, ev: TestFileLoaderEventMap[K]) => void, 49 options?: boolean | EventListenerOptions 50 ): void; 51 removeEventListener( 52 type: string, 53 listener: EventListenerOrEventListenerObject, 54 options?: boolean | EventListenerOptions 55 ): void; 56 } 57 58 // Base class for DefaultTestFileLoader and FakeTestFileLoader. 59 /* eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging */ 60 export abstract class TestFileLoader extends EventTarget { 61 abstract listing(suite: string): Promise<TestSuiteListing>; 62 protected abstract import(path: string): Promise<SpecFile>; 63 64 async importSpecFile(suite: string, path: string[]): Promise<SpecFile> { 65 const url = `${suite}/${path.join('/')}.spec.js`; 66 this.dispatchEvent(new MessageEvent<ImportInfo>('import', { data: { url } })); 67 const ret = await this.import(url); 68 this.dispatchEvent(new MessageEvent<ImportInfo>('imported', { data: { url } })); 69 return ret; 70 } 71 72 async loadTree( 73 query: TestQuery, 74 { 75 subqueriesToExpand = [], 76 fullyExpandSubtrees = [], 77 maxChunkTime = Infinity, 78 }: { subqueriesToExpand?: string[]; fullyExpandSubtrees?: string[]; maxChunkTime?: number } = {} 79 ): Promise<TestTree> { 80 const tree = await loadTreeForQuery(this, query, { 81 subqueriesToExpand: subqueriesToExpand.map(s => { 82 const q = parseQuery(s); 83 assert(q.level >= 2, () => `subqueriesToExpand entries should not be multi-file:\n ${q}`); 84 return q; 85 }), 86 fullyExpandSubtrees: fullyExpandSubtrees.map(s => parseQuery(s)), 87 maxChunkTime, 88 }); 89 this.dispatchEvent(new MessageEvent<void>('finish')); 90 return tree; 91 } 92 93 async loadCases(query: TestQuery): Promise<IterableIterator<TestTreeLeaf>> { 94 const tree = await this.loadTree(query); 95 return tree.iterateLeaves(); 96 } 97 } 98 99 export class DefaultTestFileLoader extends TestFileLoader { 100 async listing(suite: string): Promise<TestSuiteListing> { 101 return ((await import(`../../${suite}/listing.js`)) as ListingFile).listing; 102 } 103 104 import(path: string): Promise<SpecFile> { 105 return import(`../../${path}`); 106 } 107 }