params_builder.js (8357B)
1 /** 2 * AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts 3 **/import { mergeParams, mergeParamsChecked } from '../internal/params_utils.js';import { comparePublicParamsPaths, Ordering } from '../internal/query/compare.js';import { stringifyPublicParams } from '../internal/query/stringify_params.js'; 4 5 import { assert, mapLazy, objectEquals } from '../util/util.js'; 6 7 8 9 // ================================================================ 10 // "Public" ParamsBuilder API / Documentation 11 // ================================================================ 12 13 /** 14 * Provides doc comments for the methods of CaseParamsBuilder and SubcaseParamsBuilder. 15 * (Also enforces rough interface match between them.) 16 */ 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 /** 80 * Determines the resulting parameter object type which would be generated by an object of 81 * the given ParamsBuilder type. 82 */ 83 84 85 86 87 88 89 90 91 92 // ================================================================ 93 // Implementation 94 // ================================================================ 95 96 /** 97 * Iterable over pairs of either: 98 * - `[case params, Iterable<subcase params>]` if there are subcases. 99 * - `[case params, undefined]` if not. 100 */ 101 102 103 104 105 /** 106 * Base class for `CaseParamsBuilder` and `SubcaseParamsBuilder`. 107 */ 108 export class ParamsBuilderBase { 109 110 111 constructor(cases) { 112 this.cases = cases; 113 } 114 115 /** 116 * Hidden from test files. Use `builderIterateCasesWithSubcases` to access this. 117 */ 118 119 120 121 } 122 123 /** 124 * Calls the (normally hidden) `iterateCasesWithSubcases()` method. 125 */ 126 export function builderIterateCasesWithSubcases( 127 builder, 128 caseFilter) 129 { 130 131 132 133 134 return builder.iterateCasesWithSubcases(caseFilter); 135 } 136 137 /** 138 * Builder for combinatorial test **case** parameters. 139 * 140 * CaseParamsBuilder is immutable. Each method call returns a new, immutable object, 141 * modifying the list of cases according to the method called. 142 * 143 * This means, for example, that the `unit` passed into `TestBuilder.params()` can be reused. 144 */ 145 export class CaseParamsBuilder extends 146 ParamsBuilderBase 147 148 { 149 *iterateCasesWithSubcases(caseFilter) { 150 for (const caseP of this.cases(caseFilter)) { 151 if (caseFilter) { 152 // this.cases() only filters out cases which conflict with caseFilter. Now that we have 153 // the final caseP, filter out cases which are missing keys that caseFilter requires. 154 const ordering = comparePublicParamsPaths(caseP, caseFilter); 155 if (ordering === Ordering.StrictSuperset || ordering === Ordering.Unordered) { 156 continue; 157 } 158 } 159 160 yield [caseP, undefined]; 161 } 162 } 163 164 [Symbol.iterator]() { 165 return this.cases(null); 166 } 167 168 /** @inheritDoc */ 169 expandWithParams( 170 expander) 171 { 172 const baseGenerator = this.cases; 173 return new CaseParamsBuilder(function* (caseFilter) { 174 for (const a of baseGenerator(caseFilter)) { 175 for (const b of expander(a)) { 176 if (caseFilter) { 177 // If the expander generated any key-value pair that conflicts with caseFilter, skip. 178 const kvPairs = Object.entries(b); 179 if (kvPairs.some(([k, v]) => k in caseFilter && !objectEquals(caseFilter[k], v))) { 180 continue; 181 } 182 } 183 184 yield mergeParamsChecked(a, b); 185 } 186 } 187 }); 188 } 189 190 /** @inheritDoc */ 191 expand( 192 key, 193 expander) 194 { 195 const baseGenerator = this.cases; 196 return new CaseParamsBuilder(function* (caseFilter) { 197 for (const a of baseGenerator(caseFilter)) { 198 assert(!(key in a), `New key '${key}' already exists in ${JSON.stringify(a)}`); 199 200 for (const v of expander(a)) { 201 // If the expander generated a value for this key that conflicts with caseFilter, skip. 202 if (caseFilter && key in caseFilter) { 203 if (!objectEquals(caseFilter[key], v)) { 204 continue; 205 } 206 } 207 yield { ...a, [key]: v }; 208 } 209 } 210 }); 211 } 212 213 /** @inheritDoc */ 214 combineWithParams( 215 newParams) 216 { 217 assertNotGenerator(newParams); 218 const seenValues = new Set(); 219 for (const params of newParams) { 220 const paramsStr = stringifyPublicParams(params); 221 assert(!seenValues.has(paramsStr), `Duplicate entry in combine[WithParams]: ${paramsStr}`); 222 seenValues.add(paramsStr); 223 } 224 225 return this.expandWithParams(() => newParams); 226 } 227 228 /** @inheritDoc */ 229 combine( 230 key, 231 values) 232 { 233 assertNotGenerator(values); 234 const mapped = mapLazy(values, (v) => ({ [key]: v })); 235 return this.combineWithParams(mapped); 236 } 237 238 /** @inheritDoc */ 239 filter(pred) { 240 const baseGenerator = this.cases; 241 return new CaseParamsBuilder(function* (caseFilter) { 242 for (const a of baseGenerator(caseFilter)) { 243 if (pred(a)) yield a; 244 } 245 }); 246 } 247 248 /** @inheritDoc */ 249 unless(pred) { 250 return this.filter((x) => !pred(x)); 251 } 252 253 /** 254 * "Finalize" the list of cases and begin defining subcases. 255 * Returns a new SubcaseParamsBuilder. Methods called on SubcaseParamsBuilder 256 * generate new subcases instead of new cases. 257 */ 258 beginSubcases() { 259 return new SubcaseParamsBuilder(this.cases, function* () { 260 yield {}; 261 }); 262 } 263 } 264 265 /** 266 * The unit CaseParamsBuilder, representing a single case with no params: `[ {} ]`. 267 * 268 * `punit` is passed to every `.params()`/`.paramsSubcasesOnly()` call, so `kUnitCaseParamsBuilder` 269 * is only explicitly needed if constructing a ParamsBuilder outside of a test builder. 270 */ 271 export const kUnitCaseParamsBuilder = new CaseParamsBuilder(function* () { 272 yield {}; 273 }); 274 275 /** 276 * Builder for combinatorial test _subcase_ parameters. 277 * 278 * SubcaseParamsBuilder is immutable. Each method call returns a new, immutable object, 279 * modifying the list of subcases according to the method called. 280 */ 281 export class SubcaseParamsBuilder extends 282 ParamsBuilderBase 283 284 { 285 286 287 constructor( 288 cases, 289 generator) 290 { 291 super(cases); 292 this.subcases = generator; 293 } 294 295 *iterateCasesWithSubcases(caseFilter) { 296 for (const caseP of this.cases(caseFilter)) { 297 if (caseFilter) { 298 // this.cases() only filters out cases which conflict with caseFilter. Now that we have 299 // the final caseP, filter out cases which are missing keys that caseFilter requires. 300 const ordering = comparePublicParamsPaths(caseP, caseFilter); 301 if (ordering === Ordering.StrictSuperset || ordering === Ordering.Unordered) { 302 continue; 303 } 304 } 305 306 const subcases = Array.from(this.subcases(caseP)); 307 if (subcases.length) { 308 yield [ 309 caseP, 310 subcases]; 311 312 } 313 } 314 } 315 316 /** @inheritDoc */ 317 expandWithParams( 318 expander) 319 { 320 const baseGenerator = this.subcases; 321 return new SubcaseParamsBuilder(this.cases, function* (base) { 322 for (const a of baseGenerator(base)) { 323 for (const b of expander(mergeParams(base, a))) { 324 yield mergeParamsChecked(a, b); 325 } 326 } 327 }); 328 } 329 330 /** @inheritDoc */ 331 expand( 332 key, 333 expander) 334 { 335 const baseGenerator = this.subcases; 336 return new SubcaseParamsBuilder(this.cases, function* (base) { 337 for (const a of baseGenerator(base)) { 338 const before = mergeParams(base, a); 339 assert(!(key in before), () => `Key '${key}' already exists in ${JSON.stringify(before)}`); 340 341 for (const v of expander(before)) { 342 yield { ...a, [key]: v }; 343 } 344 } 345 }); 346 } 347 348 /** @inheritDoc */ 349 combineWithParams( 350 newParams) 351 { 352 assertNotGenerator(newParams); 353 return this.expandWithParams(() => newParams); 354 } 355 356 /** @inheritDoc */ 357 combine( 358 key, 359 values) 360 { 361 assertNotGenerator(values); 362 return this.expand(key, () => values); 363 } 364 365 /** @inheritDoc */ 366 filter(pred) { 367 const baseGenerator = this.subcases; 368 return new SubcaseParamsBuilder(this.cases, function* (base) { 369 for (const a of baseGenerator(base)) { 370 if (pred(mergeParams(base, a))) yield a; 371 } 372 }); 373 } 374 375 /** @inheritDoc */ 376 unless(pred) { 377 return this.filter((x) => !pred(x)); 378 } 379 } 380 381 /** Assert an object is not a Generator (a thing returned from a generator function). */ 382 function assertNotGenerator(x) { 383 if ('constructor' in x) { 384 assert( 385 x.constructor !== function* () {}().constructor, 386 'Argument must not be a generator, as generators are not reusable' 387 ); 388 } 389 }