params_builder_and_utils.spec.ts (14024B)
1 export const description = ` 2 Unit tests for parameterization helpers. 3 `; 4 5 import { TestParams } from '../common/framework/fixture.js'; 6 import { 7 kUnitCaseParamsBuilder, 8 CaseSubcaseIterable, 9 ParamsBuilderBase, 10 builderIterateCasesWithSubcases, 11 } from '../common/framework/params_builder.js'; 12 import { makeTestGroup } from '../common/framework/test_group.js'; 13 import { 14 mergeParams, 15 mergeParamsChecked, 16 publicParamsEquals, 17 } from '../common/internal/params_utils.js'; 18 import { assert, objectEquals } from '../common/util/util.js'; 19 20 import { UnitTest } from './unit_test.js'; 21 22 class ParamsTest extends UnitTest { 23 expectParams<CaseP extends {}, SubcaseP extends {}>( 24 act: ParamsBuilderBase<CaseP, SubcaseP>, 25 exp: CaseSubcaseIterable<{}, {}>, 26 caseFilter: TestParams | null = null 27 ): void { 28 const a = Array.from(builderIterateCasesWithSubcases(act, caseFilter)).map( 29 ([caseP, subcases]) => [caseP, subcases ? Array.from(subcases) : undefined] 30 ); 31 const e = Array.from(exp); 32 this.expect( 33 objectEquals(a, e), 34 ` 35 got ${JSON.stringify(a)} 36 expected ${JSON.stringify(e)}` 37 ); 38 } 39 } 40 41 export const g = makeTestGroup(ParamsTest); 42 43 const u = kUnitCaseParamsBuilder; 44 45 g.test('combine').fn(t => { 46 t.expectParams<{ hello: number }, {}>(u.combine('hello', [1, 2, 3]), [ 47 [{ hello: 1 }, undefined], 48 [{ hello: 2 }, undefined], 49 [{ hello: 3 }, undefined], 50 ]); 51 t.expectParams<{ hello: number }, {}>( 52 u.combine('hello', [1, 2, 3]), 53 [ 54 [{ hello: 1 }, undefined], 55 [{ hello: 2 }, undefined], 56 [{ hello: 3 }, undefined], 57 ], 58 {} 59 ); 60 t.expectParams<{ hello: number }, {}>( 61 u.combine('hello', [1, 2, 3]), 62 [[{ hello: 2 }, undefined]], 63 { hello: 2 } 64 ); 65 t.expectParams<{ hello: 1 | 2 | 3 }, {}>(u.combine('hello', [1, 2, 3] as const), [ 66 [{ hello: 1 }, undefined], 67 [{ hello: 2 }, undefined], 68 [{ hello: 3 }, undefined], 69 ]); 70 t.expectParams<{}, { hello: number }>(u.beginSubcases().combine('hello', [1, 2, 3]), [ 71 [{}, [{ hello: 1 }, { hello: 2 }, { hello: 3 }]], 72 ]); 73 t.expectParams<{}, { hello: number }>( 74 u.beginSubcases().combine('hello', [1, 2, 3]), 75 [[{}, [{ hello: 1 }, { hello: 2 }, { hello: 3 }]]], 76 {} 77 ); 78 t.expectParams<{}, { hello: number }>(u.beginSubcases().combine('hello', [1, 2, 3]), [], { 79 hello: 2, 80 }); 81 t.expectParams<{}, { hello: 1 | 2 | 3 }>(u.beginSubcases().combine('hello', [1, 2, 3] as const), [ 82 [{}, [{ hello: 1 }, { hello: 2 }, { hello: 3 }]], 83 ]); 84 }); 85 86 g.test('empty').fn(t => { 87 t.expectParams<{}, {}>(u, [ 88 [{}, undefined], // 89 ]); 90 t.expectParams<{}, {}>(u.beginSubcases(), [ 91 [{}, [{}]], // 92 ]); 93 }); 94 95 g.test('combine,zeroes_and_ones').fn(t => { 96 t.expectParams<{}, {}>(u.combineWithParams([]).combineWithParams([]), []); 97 t.expectParams<{}, {}>(u.combineWithParams([]).combineWithParams([{}]), []); 98 t.expectParams<{}, {}>(u.combineWithParams([{}]).combineWithParams([]), []); 99 t.expectParams<{}, {}>(u.combineWithParams([{}]).combineWithParams([{}]), [ 100 [{}, undefined], // 101 ]); 102 103 t.expectParams<{}, {}>(u.combine('x', []).combine('y', []), []); 104 t.expectParams<{}, {}>(u.combine('x', []).combine('y', [1]), []); 105 t.expectParams<{}, {}>(u.combine('x', [1]).combine('y', []), []); 106 t.expectParams<{}, {}>(u.combine('x', [1]).combine('y', [1]), [ 107 [{ x: 1, y: 1 }, undefined], // 108 ]); 109 }); 110 111 g.test('combine,mixed').fn(t => { 112 t.expectParams<{ x: number; y: string; p: number | undefined; q: number | undefined }, {}>( 113 u 114 .combine('x', [1, 2]) 115 .combine('y', ['a', 'b']) 116 .combineWithParams([{ p: 4 }, { q: 5 }]) 117 .combineWithParams([{}]), 118 [ 119 [{ x: 1, y: 'a', p: 4 }, undefined], 120 [{ x: 1, y: 'a', q: 5 }, undefined], 121 [{ x: 1, y: 'b', p: 4 }, undefined], 122 [{ x: 1, y: 'b', q: 5 }, undefined], 123 [{ x: 2, y: 'a', p: 4 }, undefined], 124 [{ x: 2, y: 'a', q: 5 }, undefined], 125 [{ x: 2, y: 'b', p: 4 }, undefined], 126 [{ x: 2, y: 'b', q: 5 }, undefined], 127 ] 128 ); 129 }); 130 131 g.test('filter').fn(t => { 132 t.expectParams<{ a: boolean; x: number | undefined; y: number | undefined }, {}>( 133 u 134 .combineWithParams([ 135 { a: true, x: 1 }, 136 { a: false, y: 2 }, 137 ]) 138 .filter(p => p.a), 139 [ 140 [{ a: true, x: 1 }, undefined], // 141 ] 142 ); 143 144 t.expectParams<{ a: boolean; x: number | undefined; y: number | undefined }, {}>( 145 u 146 .combineWithParams([ 147 { a: true, x: 1 }, 148 { a: false, y: 2 }, 149 ]) 150 .beginSubcases() 151 .filter(p => p.a), 152 [ 153 [{ a: true, x: 1 }, [{}]], // 154 // Case with no subcases is filtered out. 155 ] 156 ); 157 158 t.expectParams<{}, { a: boolean; x: number | undefined; y: number | undefined }>( 159 u 160 .beginSubcases() 161 .combineWithParams([ 162 { a: true, x: 1 }, 163 { a: false, y: 2 }, 164 ]) 165 .filter(p => p.a), 166 [ 167 [{}, [{ a: true, x: 1 }]], // 168 ] 169 ); 170 }); 171 172 g.test('unless').fn(t => { 173 t.expectParams<{ a: boolean; x: number | undefined; y: number | undefined }, {}>( 174 u 175 .combineWithParams([ 176 { a: true, x: 1 }, 177 { a: false, y: 2 }, 178 ]) 179 .unless(p => p.a), 180 [ 181 [{ a: false, y: 2 }, undefined], // 182 ] 183 ); 184 185 t.expectParams<{ a: boolean; x: number | undefined; y: number | undefined }, {}>( 186 u 187 .combineWithParams([ 188 { a: true, x: 1 }, 189 { a: false, y: 2 }, 190 ]) 191 .beginSubcases() 192 .unless(p => p.a), 193 [ 194 // Case with no subcases is filtered out. 195 [{ a: false, y: 2 }, [{}]], // 196 ] 197 ); 198 199 t.expectParams<{}, { a: boolean; x: number | undefined; y: number | undefined }>( 200 u 201 .beginSubcases() 202 .combineWithParams([ 203 { a: true, x: 1 }, 204 { a: false, y: 2 }, 205 ]) 206 .unless(p => p.a), 207 [ 208 [{}, [{ a: false, y: 2 }]], // 209 ] 210 ); 211 }); 212 213 g.test('expandP').fn(t => { 214 // simple 215 t.expectParams<{}, {}>( 216 u.expandWithParams(function* () {}), 217 [] 218 ); 219 t.expectParams<{}, {}>( 220 u.expandWithParams(function* () { 221 yield {}; 222 }), 223 [[{}, undefined]] 224 ); 225 t.expectParams<{ z: number | undefined; w: number | undefined }, {}>( 226 u.expandWithParams(function* () { 227 yield* kUnitCaseParamsBuilder.combine('z', [3, 4]); 228 yield { w: 5 }; 229 }), 230 [ 231 [{ z: 3 }, undefined], 232 [{ z: 4 }, undefined], 233 [{ w: 5 }, undefined], 234 ] 235 ); 236 t.expectParams<{ z: number | undefined; w: number | undefined }, {}>( 237 u.expandWithParams(function* () { 238 yield* kUnitCaseParamsBuilder.combine('z', [3, 4]); 239 yield { w: 5 }; 240 }), 241 [ 242 [{ z: 3 }, undefined], 243 [{ z: 4 }, undefined], 244 [{ w: 5 }, undefined], 245 ], 246 {} 247 ); 248 t.expectParams<{ z: number | undefined; w: number | undefined }, {}>( 249 u.expandWithParams(function* () { 250 yield* kUnitCaseParamsBuilder.combine('z', [3, 4]); 251 yield { w: 5 }; 252 }), 253 [[{ z: 4 }, undefined]], 254 { z: 4 } 255 ); 256 t.expectParams<{ z: number | undefined; w: number | undefined }, {}>( 257 u.expandWithParams(function* () { 258 yield* kUnitCaseParamsBuilder.combine('z', [3, 4]); 259 yield { w: 5 }; 260 }), 261 [[{ z: 3 }, undefined]], 262 { z: 3 } 263 ); 264 t.expectParams<{}, { z: number | undefined; w: number | undefined }>( 265 u.beginSubcases().expandWithParams(function* () { 266 yield* kUnitCaseParamsBuilder.combine('z', [3, 4]); 267 yield { w: 5 }; 268 }), 269 [[{}, [{ z: 3 }, { z: 4 }, { w: 5 }]]] 270 ); 271 272 t.expectParams<{ x: [] | {} }, {}>( 273 u.expand('x', () => [[], {}] as const), 274 [ 275 [{ x: [] }, undefined], 276 [{ x: {} }, undefined], 277 ] 278 ); 279 t.expectParams<{ x: [] | {} }, {}>( 280 u.expand('x', () => [[], {}] as const), 281 [[{ x: [] }, undefined]], 282 { x: [] } 283 ); 284 t.expectParams<{ x: [] | {} }, {}>( 285 u.expand('x', () => [[], {}] as const), 286 [[{ x: {} }, undefined]], 287 { x: {} } 288 ); 289 290 // more complex 291 { 292 const p = u 293 .combineWithParams([ 294 { a: true, x: 1 }, 295 { a: false, y: 2 }, 296 ]) 297 .expandWithParams(function* (p) { 298 if (p.a) { 299 yield { z: 3 }; 300 yield { z: 4 }; 301 } else { 302 yield { w: 5 }; 303 } 304 }); 305 type T = { 306 a: boolean; 307 x: number | undefined; 308 y: number | undefined; 309 z: number | undefined; 310 w: number | undefined; 311 }; 312 t.expectParams<T, {}>(p, [ 313 [{ a: true, x: 1, z: 3 }, undefined], 314 [{ a: true, x: 1, z: 4 }, undefined], 315 [{ a: false, y: 2, w: 5 }, undefined], 316 ]); 317 t.expectParams<T, {}>( 318 p, 319 [ 320 [{ a: true, x: 1, z: 3 }, undefined], 321 [{ a: true, x: 1, z: 4 }, undefined], 322 [{ a: false, y: 2, w: 5 }, undefined], 323 ], 324 {} 325 ); 326 t.expectParams<T, {}>( 327 p, 328 [ 329 [{ a: true, x: 1, z: 3 }, undefined], 330 [{ a: true, x: 1, z: 4 }, undefined], 331 ], 332 { a: true } 333 ); 334 t.expectParams<T, {}>(p, [[{ a: false, y: 2, w: 5 }, undefined]], { a: false }); 335 } 336 337 t.expectParams< 338 { a: boolean; x: number | undefined; y: number | undefined }, 339 { z: number | undefined; w: number | undefined } 340 >( 341 u 342 .combineWithParams([ 343 { a: true, x: 1 }, 344 { a: false, y: 2 }, 345 ]) 346 .beginSubcases() 347 .expandWithParams(function* (p) { 348 if (p.a) { 349 yield { z: 3 }; 350 yield { z: 4 }; 351 } else { 352 yield { w: 5 }; 353 } 354 }), 355 [ 356 [{ a: true, x: 1 }, [{ z: 3 }, { z: 4 }]], 357 [{ a: false, y: 2 }, [{ w: 5 }]], 358 ] 359 ); 360 }); 361 362 g.test('expand').fn(t => { 363 // simple 364 t.expectParams<{}, {}>( 365 u.expand('x', function* () {}), 366 [] 367 ); 368 t.expectParams<{ z: number }, {}>( 369 u.expand('z', function* () { 370 yield 3; 371 yield 4; 372 }), 373 [ 374 [{ z: 3 }, undefined], 375 [{ z: 4 }, undefined], 376 ] 377 ); 378 t.expectParams<{ z: number }, {}>( 379 u.expand('z', function* () { 380 yield 3; 381 yield 4; 382 }), 383 [ 384 [{ z: 3 }, undefined], 385 [{ z: 4 }, undefined], 386 ], 387 {} 388 ); 389 t.expectParams<{ z: number }, {}>( 390 u.expand('z', function* () { 391 yield 3; 392 yield 4; 393 }), 394 [[{ z: 3 }, undefined]], 395 { z: 3 } 396 ); 397 t.expectParams<{}, { z: number }>( 398 u.beginSubcases().expand('z', function* () { 399 yield 3; 400 yield 4; 401 }), 402 [[{}, [{ z: 3 }, { z: 4 }]]] 403 ); 404 405 // more complex 406 t.expectParams<{ a: boolean; x: number | undefined; y: number | undefined; z: number }, {}>( 407 u 408 .combineWithParams([ 409 { a: true, x: 1 }, 410 { a: false, y: 2 }, 411 ]) 412 .expand('z', function* (p) { 413 if (p.a) { 414 yield 3; 415 } else { 416 yield 5; 417 } 418 }), 419 [ 420 [{ a: true, x: 1, z: 3 }, undefined], 421 [{ a: false, y: 2, z: 5 }, undefined], 422 ] 423 ); 424 t.expectParams<{ a: boolean; x: number | undefined; y: number | undefined }, { z: number }>( 425 u 426 .combineWithParams([ 427 { a: true, x: 1 }, 428 { a: false, y: 2 }, 429 ]) 430 .beginSubcases() 431 .expand('z', function* (p) { 432 if (p.a) { 433 yield 3; 434 } else { 435 yield 5; 436 } 437 }), 438 [ 439 [{ a: true, x: 1 }, [{ z: 3 }]], 440 [{ a: false, y: 2 }, [{ z: 5 }]], 441 ] 442 ); 443 }); 444 445 g.test('invalid,shadowing').fn(t => { 446 // Existing CaseP is shadowed by a new CaseP. 447 { 448 const p = u 449 .combineWithParams([ 450 { a: true, x: 1 }, 451 { a: false, x: 2 }, 452 ]) 453 .expandWithParams(function* (p) { 454 if (p.a) { 455 yield { x: 3 }; 456 } else { 457 yield { w: 5 }; 458 } 459 }); 460 // Iterating causes merging e.g. ({x:1}, {x:3}), which fails. 461 t.shouldThrow('Error', () => { 462 Array.from(p.iterateCasesWithSubcases(null)); 463 }); 464 } 465 // Existing SubcaseP is shadowed by a new SubcaseP. 466 { 467 const p = u 468 .beginSubcases() 469 .combineWithParams([ 470 { a: true, x: 1 }, 471 { a: false, x: 2 }, 472 ]) 473 .expandWithParams(function* (p) { 474 if (p.a) { 475 yield { x: 3 }; 476 } else { 477 yield { w: 5 }; 478 } 479 }); 480 // Iterating causes merging e.g. ({x:1}, {x:3}), which fails. 481 t.shouldThrow('Error', () => { 482 Array.from(p.iterateCasesWithSubcases(null)); 483 }); 484 } 485 // Existing CaseP is shadowed by a new SubcaseP. 486 { 487 const p = u 488 .combineWithParams([ 489 { a: true, x: 1 }, 490 { a: false, x: 2 }, 491 ]) 492 .beginSubcases() 493 .expandWithParams(function* (p) { 494 if (p.a) { 495 yield { x: 3 }; 496 } else { 497 yield { w: 5 }; 498 } 499 }); 500 const cases = Array.from(p.iterateCasesWithSubcases(null)); 501 // Iterating cases is fine... 502 for (const [caseP, subcases] of cases) { 503 assert(subcases !== undefined); 504 // Iterating subcases is fine... 505 for (const subcaseP of subcases) { 506 if (caseP.a) { 507 assert(subcases !== undefined); 508 509 // Only errors once we try to merge e.g. ({x:1}, {x:3}). 510 mergeParams(caseP, subcaseP); 511 t.shouldThrow('Error', () => { 512 mergeParamsChecked(caseP, subcaseP); 513 }); 514 } 515 } 516 } 517 } 518 }); 519 520 g.test('undefined').fn(t => { 521 t.expect(!publicParamsEquals({ a: undefined }, {})); 522 t.expect(!publicParamsEquals({}, { a: undefined })); 523 }); 524 525 g.test('private').fn(t => { 526 t.expect(publicParamsEquals({ _a: 0 }, {})); 527 t.expect(publicParamsEquals({}, { _a: 0 })); 528 }); 529 530 g.test('value,array').fn(t => { 531 t.expectParams<{ a: number[] }, {}>(u.combineWithParams([{ a: [1, 2] }]), [ 532 [{ a: [1, 2] }, undefined], // 533 ]); 534 t.expectParams<{}, { a: number[] }>(u.beginSubcases().combineWithParams([{ a: [1, 2] }]), [ 535 [{}, [{ a: [1, 2] }]], // 536 ]); 537 }); 538 539 g.test('value,object').fn(t => { 540 t.expectParams<{ a: { [k: string]: number } }, {}>(u.combineWithParams([{ a: { x: 1 } }]), [ 541 [{ a: { x: 1 } }, undefined], // 542 ]); 543 t.expectParams<{}, { a: { [k: string]: number } }>( 544 u.beginSubcases().combineWithParams([{ a: { x: 1 } }]), 545 [ 546 [{}, [{ a: { x: 1 } }]], // 547 ] 548 ); 549 });