1
1
use crate :: schema:: spec:: * ;
2
2
use crate :: schema:: structure:: * ;
3
3
4
- use crate :: util:: Escaper ;
4
+ use crate :: util:: { escape_fn_code , Escaper } ;
5
5
use codegen:: { Function , Module , Scope } ;
6
6
use ion_rs:: TextWriterBuilder ;
7
7
8
8
use ion_rs:: element:: writer:: ElementWriter ;
9
9
use ion_rs:: element:: { Element , Struct } ;
10
+ use quote:: __private:: TokenStream ;
10
11
use quote:: quote;
11
12
use std:: collections:: { HashMap , HashSet } ;
12
13
@@ -344,20 +345,8 @@ impl Generator {
344
345
name
345
346
}
346
347
347
- fn gen_test ( & mut self , scope : & mut Scope , test_case : & TestCase ) {
348
- let escaped_name = test_case. name . escape_test_name ( ) ;
349
- let name = self . intern_test_name ( escaped_name) ;
350
-
351
- let test_fn: & mut Function = scope. new_fn ( & name) ;
352
- test_fn. attr ( "test" ) ;
353
- test_fn. attr ( "allow(text_direction_codepoint_in_literal)" ) ;
354
-
355
- let doc = format ! ( "Generated test for test named `{}`" , & test_case. name) ;
356
- test_fn. doc ( & doc) ;
357
-
358
- let mut ignore_test = false ;
359
- let mut has_env = false ;
360
- let test_case_expr = |gen : & dyn Fn ( & str ) -> _ | match & test_case. statement {
348
+ fn test_case_stmts ( & self , test_case : & TestCase ) -> Vec < String > {
349
+ match & test_case. statement {
361
350
TestStatement :: EquivalenceClass ( equiv_id) => {
362
351
let stmts = self
363
352
. curr_equivs
@@ -367,159 +356,177 @@ impl Generator {
367
356
. next ( ) ;
368
357
369
358
let stmts = stmts. expect ( "equivalence class named" ) ;
370
- stmts. iter ( ) . map ( |s| gen ( s ) ) . collect :: < Vec < _ > > ( )
359
+ stmts. to_vec ( )
371
360
}
372
- TestStatement :: Statement ( s) => vec ! [ gen ( s) ] ,
361
+ TestStatement :: Statement ( s) => vec ! [ s. clone( ) ] ,
362
+ }
363
+ }
364
+
365
+ fn create_test < ' a > (
366
+ & mut self ,
367
+ scope : & ' a mut Scope ,
368
+ test_case : & TestCase ,
369
+ name_prefix : Option < & str > ,
370
+ needs_env : bool ,
371
+ ) -> & ' a mut Function {
372
+ let bare_name = & test_case. name ;
373
+ let prefixed_name = if let Some ( prefix) = name_prefix {
374
+ format ! ( "{prefix}_{bare_name}" )
375
+ } else {
376
+ bare_name. clone ( )
373
377
} ;
378
+ let escaped_name = prefixed_name. escape_test_name ( ) ;
379
+ let name = self . intern_test_name ( escaped_name) ;
374
380
375
- if let Some ( env) = & test_case. env {
381
+ let test_fn: & mut Function = scope. new_fn ( & name) ;
382
+ test_fn. attr ( "test" ) ;
383
+ test_fn. attr ( "allow(text_direction_codepoint_in_literal)" ) ;
384
+
385
+ let doc = format ! ( "Generated test for test named `{}`" , & test_case. name) ;
386
+ test_fn. doc ( & doc) ;
387
+
388
+ let env = if let Some ( env) = & test_case. env {
376
389
let env = struct_to_string ( env) ;
377
- let env = quote ! {
390
+ quote ! {
378
391
let env_ion_text = #env;
379
392
let env = Some ( env_ion_text. into( ) ) ;
380
393
}
381
- . to_string ( )
382
- . replace ( "\\ n" , "\n " ) ;
383
- test_fn. line ( env) ;
384
- has_env = true ;
394
+ } else if needs_env {
395
+ quote ! {
396
+ let env = environment( ) ;
397
+ }
398
+ } else {
399
+ quote ! { }
400
+ } ;
401
+
402
+ test_fn. line ( escape_fn_code ( env) ) ;
403
+
404
+ test_fn
405
+ }
406
+
407
+ fn write_aside_expected ( & mut self , test_case : & TestCase , expected : String ) -> ( String , bool ) {
408
+ if expected. lines ( ) . count ( ) > EXPECTED_INLINE_LOWER_BOUND_LINE_COUNT {
409
+ let expected_file = self
410
+ . curr_mod_path
411
+ . iter ( )
412
+ . map ( |s| s. escape_path ( ) )
413
+ . chain ( std:: iter:: once ( test_case. name . escape_path ( ) ) )
414
+ . collect :: < Vec < _ > > ( )
415
+ . join ( "___" )
416
+ + ".expected.ion" ;
417
+
418
+ let td_dir = TEST_DATA_DIR . to_string ( ) ;
419
+ let expected_path: Vec < _ > = self
420
+ . curr_path
421
+ . iter ( )
422
+ . chain ( std:: iter:: once ( & td_dir) )
423
+ . chain ( std:: iter:: once ( & expected_file) )
424
+ . collect ( ) ;
425
+
426
+ self . result . insert (
427
+ expected_path. as_slice ( ) ,
428
+ Node :: Value ( TestValueNode { value : expected } ) ,
429
+ ) ;
430
+
431
+ ( format ! ( "{TEST_DATA_DIR}/{expected_file}" ) , true )
432
+ } else {
433
+ ( expected, false )
434
+ }
435
+ }
436
+
437
+ fn gen_test ( & mut self , scope : & mut Scope , test_case : & TestCase ) {
438
+ let stmts = self . test_case_stmts ( test_case) ;
439
+ let test_case_expr =
440
+ |gen : & dyn Fn ( & str ) -> _ | stmts. iter ( ) . map ( |s| gen ( s) ) . collect :: < Vec < _ > > ( ) ;
441
+
442
+ fn mode_data ( eval_mode : & EvaluationMode ) -> ( & ' static str , TokenStream ) {
443
+ match eval_mode {
444
+ EvaluationMode :: EvalModeError => ( "strict" , quote ! { EvaluationMode :: Error } ) ,
445
+ EvaluationMode :: EvalModeCoerce => ( "permissive" , quote ! { EvaluationMode :: Coerce } ) ,
446
+ }
385
447
}
386
448
387
449
for assertion in & test_case. assert {
388
450
match assertion {
389
451
Assertion :: SyntaxSuccess ( _) => {
452
+ let test_fn = self . create_test ( scope, test_case, None , false ) ;
390
453
let stmts = test_case_expr ( & |stmt : & str | quote ! { pass_syntax( #stmt) ; } ) ;
391
- let tokens = quote ! {
454
+ test_fn . line ( escape_fn_code ( quote ! {
392
455
#( #stmts) *
393
- } ;
394
- test_fn. line ( tokens. to_string ( ) . replace ( "\\ n" , "\n " ) ) ;
456
+ } ) ) ;
395
457
}
396
458
Assertion :: SyntaxFail ( _) => {
459
+ let test_fn = self . create_test ( scope, test_case, None , false ) ;
397
460
let stmts = test_case_expr ( & |stmt : & str | quote ! { fail_syntax( #stmt) ; } ) ;
398
- let tokens = quote ! {
461
+ test_fn . line ( escape_fn_code ( quote ! {
399
462
#( #stmts) *
400
- } ;
401
- test_fn. line ( tokens. to_string ( ) . replace ( "\\ n" , "\n " ) ) ;
463
+ } ) ) ;
402
464
}
403
465
Assertion :: StaticAnalysisFail ( _) => {
404
- // TODO semantics tests are not yet implemented
405
- ignore_test = true ;
466
+ let test_fn = self . create_test ( scope, test_case, None , false ) ;
406
467
407
468
let stmts = test_case_expr ( & |stmt : & str | quote ! { fail_semantics( #stmt) ; } ) ;
408
- let tokens = quote ! {
469
+ test_fn . line ( escape_fn_code ( quote ! {
409
470
#( #stmts) *
410
- } ;
411
- test_fn. line ( tokens. to_string ( ) . replace ( "\\ n" , "\n " ) ) ;
471
+ } ) ) ;
412
472
}
413
473
Assertion :: EvaluationSuccess ( EvaluationSuccessAssertion {
414
474
output,
415
475
eval_mode,
416
476
..
417
477
} ) => {
418
- if !std:: mem:: replace ( & mut has_env, true ) {
419
- test_fn. line ( "let env = environment();\n \n " ) ;
420
- }
421
- test_fn. line ( "\n //**** evaluation success test case(s) ****//" ) ;
422
-
423
- let expected = elt_to_string ( output) ;
424
- let expected =
425
- if expected. lines ( ) . count ( ) > EXPECTED_INLINE_LOWER_BOUND_LINE_COUNT {
426
- let expected_file = self
427
- . curr_mod_path
428
- . iter ( )
429
- . map ( |s| s. escape_path ( ) )
430
- . chain ( std:: iter:: once ( test_case. name . escape_path ( ) ) )
431
- . collect :: < Vec < _ > > ( )
432
- . join ( "___" )
433
- + ".expected.ion" ;
434
-
435
- let td_dir = TEST_DATA_DIR . to_string ( ) ;
436
- let expected_path: Vec < _ > = self
437
- . curr_path
438
- . iter ( )
439
- . chain ( std:: iter:: once ( & td_dir) )
440
- . chain ( std:: iter:: once ( & expected_file) )
441
- . collect ( ) ;
442
-
443
- self . result . insert (
444
- expected_path. as_slice ( ) ,
445
- Node :: Value ( TestValueNode { value : expected } ) ,
446
- ) ;
447
-
448
- let data_file = format ! ( "{TEST_DATA_DIR}/{expected_file}" ) ;
449
- quote ! { include_str!( #data_file) }
478
+ for mode in eval_mode {
479
+ let ( prefix, mode) = mode_data ( mode) ;
480
+
481
+ let test_fn = self . create_test ( scope, test_case, Some ( prefix) , true ) ;
482
+ test_fn. line ( "\n //**** evaluation success test case(s) ****//" ) ;
483
+
484
+ let ( expected, is_file) =
485
+ self . write_aside_expected ( test_case, elt_to_string ( output) ) ;
486
+ let expected = if is_file {
487
+ quote ! { include_str!( #expected) }
450
488
} else {
451
489
quote ! { #expected}
452
490
} ;
453
491
454
- let modes: Vec < _ > = eval_mode
455
- . into_iter ( )
456
- . map ( |mode| match mode {
457
- EvaluationMode :: EvalModeError => quote ! { EvaluationMode :: Error } ,
458
- EvaluationMode :: EvalModeCoerce => quote ! { EvaluationMode :: Coerce } ,
459
- } )
460
- . collect ( ) ;
461
-
462
- // emit asserts for all statements X all modes
463
- let stmts = test_case_expr ( & |stmt : & str | {
464
- // emit one assert statement per evaluation mode
465
- let asserts = modes. iter ( ) . map ( |mode| {
492
+ // emit asserts for all statements
493
+ let stmts = test_case_expr ( & |stmt : & str | {
494
+ // emit PartiQL statement and evaluation mode assert
466
495
quote ! {
496
+ let stmt = #stmt;
467
497
pass_eval( stmt, #mode, & env, & expected) ;
468
498
}
469
499
} ) ;
470
- // emit PartiQL statement and evaluation mode asserts
471
- quote ! {
472
- let stmt = #stmt;
473
- #( #asserts) *
474
- }
475
- } ) ;
476
-
477
- let tokens = quote ! {
478
- let expected = #expected. into( ) ;
479
- #( #stmts) *
480
- } ;
481
- test_fn. line ( tokens. to_string ( ) . replace ( "\\ n" , "\n " ) ) ;
500
+
501
+ test_fn. line ( escape_fn_code ( quote ! {
502
+ let expected = #expected. into( ) ;
503
+ #( #stmts) *
504
+ } ) ) ;
505
+ }
482
506
}
483
507
Assertion :: EvaluationFail ( EvaluationFailAssertion { eval_mode, .. } ) => {
484
- if !std:: mem:: replace ( & mut has_env, true ) {
485
- test_fn. line ( "let env = environment();\n \n " ) ;
486
- }
487
- test_fn. line ( "\n //**** evaluation failure test case(s) ****//" ) ;
488
-
489
- let modes: Vec < _ > = eval_mode
490
- . into_iter ( )
491
- . map ( |mode| match mode {
492
- EvaluationMode :: EvalModeError => quote ! { EvaluationMode :: Error } ,
493
- EvaluationMode :: EvalModeCoerce => quote ! { EvaluationMode :: Coerce } ,
494
- } )
495
- . collect ( ) ;
496
-
497
- // emit asserts for all statements X all modes
498
- let stmts = test_case_expr ( & |stmt : & str | {
499
- // emit one assert statement per evaluation mode
500
- let asserts = modes. iter ( ) . map ( |mode| {
508
+ for mode in eval_mode {
509
+ let ( prefix, mode) = mode_data ( mode) ;
510
+
511
+ let test_fn = self . create_test ( scope, test_case, Some ( prefix) , true ) ;
512
+ test_fn. line ( "\n //**** evaluation failure test case(s) ****//" ) ;
513
+
514
+ // emit asserts for all statements
515
+ let stmts = test_case_expr ( & |stmt : & str | {
516
+ // emit PartiQL statement and evaluation mode assert
501
517
quote ! {
518
+ let stmt = #stmt;
502
519
fail_eval( stmt, #mode, & env) ;
503
520
}
504
521
} ) ;
505
- // emit PartiQL statement and evaluation mode asserts
506
- quote ! {
507
- let stmt = #stmt;
508
- #( #asserts) *
509
- }
510
- } ) ;
511
-
512
- let tokens = quote ! {
513
- #( #stmts) *
514
- } ;
515
- test_fn. line ( tokens. to_string ( ) . replace ( "\\ n" , "\n " ) ) ;
522
+
523
+ test_fn. line ( escape_fn_code ( quote ! {
524
+ #( #stmts) *
525
+ } ) ) ;
526
+ }
516
527
}
517
528
}
518
529
}
519
-
520
- if ignore_test {
521
- test_fn. attr ( "ignore = \" not yet implemented\" " ) ;
522
- }
523
530
}
524
531
}
525
532
0 commit comments