@@ -51,6 +51,7 @@ struct TestCtxt<'a> {
51
51
ext_cx : ExtCtxt < ' a > ,
52
52
testfns : Vec < Test > ,
53
53
reexport_mod_ident : ast:: Ident ,
54
+ reexport_test_harness_main : Option < InternedString > ,
54
55
is_test_crate : bool ,
55
56
config : ast:: CrateConfig ,
56
57
}
@@ -64,8 +65,16 @@ pub fn modify_for_testing(sess: &Session,
64
65
// command line options.
65
66
let should_test = attr:: contains_name ( krate. config . as_slice ( ) , "test" ) ;
66
67
68
+ // Check for #[reexport_test_harness_main = "some_name"] which
69
+ // creates a `use some_name = __test::main;`. This needs to be
70
+ // unconditional, so that the attribute is still marked as used in
71
+ // non-test builds.
72
+ let reexport_test_harness_main =
73
+ attr:: first_attr_value_str_by_name ( krate. attrs . as_slice ( ) ,
74
+ "reexport_test_harness_main" ) ;
75
+
67
76
if should_test {
68
- generate_test_harness ( sess, krate)
77
+ generate_test_harness ( sess, reexport_test_harness_main , krate)
69
78
} else {
70
79
strip_test_functions ( krate)
71
80
}
@@ -79,14 +88,17 @@ struct TestHarnessGenerator<'a> {
79
88
80
89
impl < ' a > fold:: Folder for TestHarnessGenerator < ' a > {
81
90
fn fold_crate ( & mut self , c : ast:: Crate ) -> ast:: Crate {
82
- let folded = fold:: noop_fold_crate ( c, self ) ;
91
+ let mut folded = fold:: noop_fold_crate ( c, self ) ;
83
92
84
93
// Add a special __test module to the crate that will contain code
85
94
// generated for the test harness
86
- ast:: Crate {
87
- module : add_test_module ( & self . cx , & folded. module ) ,
88
- .. folded
95
+ let ( mod_, reexport) = mk_test_module ( & self . cx , & self . cx . reexport_test_harness_main ) ;
96
+ folded. module . items . push ( mod_) ;
97
+ match reexport {
98
+ Some ( re) => folded. module . view_items . push ( re) ,
99
+ None => { }
89
100
}
101
+ folded
90
102
}
91
103
92
104
fn fold_item ( & mut self , i : Gc < ast:: Item > ) -> SmallVector < Gc < ast:: Item > > {
@@ -196,7 +208,9 @@ fn mk_reexport_mod(cx: &mut TestCtxt, tests: Vec<ast::Ident>,
196
208
}
197
209
}
198
210
199
- fn generate_test_harness ( sess : & Session , krate : ast:: Crate ) -> ast:: Crate {
211
+ fn generate_test_harness ( sess : & Session ,
212
+ reexport_test_harness_main : Option < InternedString > ,
213
+ krate : ast:: Crate ) -> ast:: Crate {
200
214
let mut cx: TestCtxt = TestCtxt {
201
215
sess : sess,
202
216
ext_cx : ExtCtxt :: new ( & sess. parse_sess , sess. opts . cfg . clone ( ) ,
@@ -206,7 +220,8 @@ fn generate_test_harness(sess: &Session, krate: ast::Crate) -> ast::Crate {
206
220
} ) ,
207
221
path : Vec :: new ( ) ,
208
222
testfns : Vec :: new ( ) ,
209
- reexport_mod_ident : token:: str_to_ident ( "__test_reexports" ) ,
223
+ reexport_mod_ident : token:: gensym_ident ( "__test_reexports" ) ,
224
+ reexport_test_harness_main : reexport_test_harness_main,
210
225
is_test_crate : is_test_crate ( & krate) ,
211
226
config : krate. config . clone ( ) ,
212
227
} ;
@@ -314,14 +329,6 @@ fn should_fail(i: Gc<ast::Item>) -> bool {
314
329
attr:: contains_name ( i. attrs . as_slice ( ) , "should_fail" )
315
330
}
316
331
317
- fn add_test_module ( cx : & TestCtxt , m : & ast:: Mod ) -> ast:: Mod {
318
- let testmod = mk_test_module ( cx) ;
319
- ast:: Mod {
320
- items : m. items . clone ( ) . append_one ( testmod) ,
321
- ..( * m) . clone ( )
322
- }
323
- }
324
-
325
332
/*
326
333
327
334
We're going to be building a module that looks more or less like:
@@ -359,7 +366,8 @@ fn mk_std(cx: &TestCtxt) -> ast::ViewItem {
359
366
}
360
367
}
361
368
362
- fn mk_test_module ( cx : & TestCtxt ) -> Gc < ast:: Item > {
369
+ fn mk_test_module ( cx : & TestCtxt , reexport_test_harness_main : & Option < InternedString > )
370
+ -> ( Gc < ast:: Item > , Option < ast:: ViewItem > ) {
363
371
// Link to test crate
364
372
let view_items = vec ! ( mk_std( cx) ) ;
365
373
@@ -383,18 +391,35 @@ fn mk_test_module(cx: &TestCtxt) -> Gc<ast::Item> {
383
391
} ;
384
392
let item_ = ast:: ItemMod ( testmod) ;
385
393
394
+ let mod_ident = token:: gensym_ident ( "__test" ) ;
386
395
let item = ast:: Item {
387
- ident : token :: str_to_ident ( "__test" ) ,
396
+ ident : mod_ident ,
388
397
attrs : Vec :: new ( ) ,
389
398
id : ast:: DUMMY_NODE_ID ,
390
399
node : item_,
391
400
vis : ast:: Public ,
392
401
span : DUMMY_SP ,
393
- } ;
402
+ } ;
403
+ let reexport = reexport_test_harness_main. as_ref ( ) . map ( |s| {
404
+ // building `use <ident> = __test::main`
405
+ let reexport_ident = token:: str_to_ident ( s. get ( ) ) ;
406
+
407
+ let use_path =
408
+ nospan ( ast:: ViewPathSimple ( reexport_ident,
409
+ path_node ( vec ! [ mod_ident, token:: str_to_ident( "main" ) ] ) ,
410
+ ast:: DUMMY_NODE_ID ) ) ;
411
+
412
+ ast:: ViewItem {
413
+ node : ast:: ViewItemUse ( box ( GC ) use_path) ,
414
+ attrs : vec ! [ ] ,
415
+ vis : ast:: Inherited ,
416
+ span : DUMMY_SP
417
+ }
418
+ } ) ;
394
419
395
420
debug ! ( "Synthetic test module:\n {}\n " , pprust:: item_to_string( & item) ) ;
396
421
397
- box ( GC ) item
422
+ ( box ( GC ) item, reexport )
398
423
}
399
424
400
425
fn nospan < T > ( t : T ) -> codemap:: Spanned < T > {
@@ -417,11 +442,27 @@ fn mk_tests(cx: &TestCtxt) -> Gc<ast::Item> {
417
442
// The vector of test_descs for this crate
418
443
let test_descs = mk_test_descs ( cx) ;
419
444
420
- ( quote_item ! ( & cx. ext_cx,
421
- pub static TESTS : & ' static [ self :: test:: TestDescAndFn ] =
422
- $test_descs
423
- ;
424
- ) ) . unwrap ( )
445
+ // FIXME #15962: should be using quote_item, but that stringifies
446
+ // __test_reexports, causing it to be reinterned, losing the
447
+ // gensym information.
448
+ let sp = DUMMY_SP ;
449
+ let ecx = & cx. ext_cx ;
450
+ let struct_type = ecx. ty_path ( ecx. path ( sp, vec ! [ ecx. ident_of( "self" ) ,
451
+ ecx. ident_of( "test" ) ,
452
+ ecx. ident_of( "TestDescAndFn" ) ] ) ,
453
+ None ) ;
454
+ let static_lt = ecx. lifetime ( sp, token:: special_idents:: static_lifetime. name ) ;
455
+ // &'static [self::test::TestDescAndFn]
456
+ let static_type = ecx. ty_rptr ( sp,
457
+ ecx. ty ( sp, ast:: TyVec ( struct_type) ) ,
458
+ Some ( static_lt) ,
459
+ ast:: MutImmutable ) ;
460
+ // static TESTS: $static_type = &[...];
461
+ ecx. item_static ( sp,
462
+ ecx. ident_of ( "TESTS" ) ,
463
+ static_type,
464
+ ast:: MutImmutable ,
465
+ test_descs)
425
466
}
426
467
427
468
fn is_test_crate ( krate : & ast:: Crate ) -> bool {
@@ -448,59 +489,58 @@ fn mk_test_descs(cx: &TestCtxt) -> Gc<ast::Expr> {
448
489
}
449
490
450
491
fn mk_test_desc_and_fn_rec ( cx : & TestCtxt , test : & Test ) -> Gc < ast:: Expr > {
492
+ // FIXME #15962: should be using quote_expr, but that stringifies
493
+ // __test_reexports, causing it to be reinterned, losing the
494
+ // gensym information.
495
+
451
496
let span = test. span ;
452
497
let path = test. path . clone ( ) ;
498
+ let ecx = & cx. ext_cx ;
499
+ let self_id = ecx. ident_of ( "self" ) ;
500
+ let test_id = ecx. ident_of ( "test" ) ;
501
+
502
+ // creates self::test::$name
503
+ let test_path = |name| {
504
+ ecx. path ( span, vec ! [ self_id, test_id, ecx. ident_of( name) ] )
505
+ } ;
506
+ // creates $name: $expr
507
+ let field = |name, expr| ecx. field_imm ( span, ecx. ident_of ( name) , expr) ;
453
508
454
509
debug ! ( "encoding {}" , ast_util:: path_name_i( path. as_slice( ) ) ) ;
455
510
456
- let name_lit: ast:: Lit =
457
- nospan ( ast:: LitStr ( token:: intern_and_get_ident (
458
- ast_util:: path_name_i ( path. as_slice ( ) ) . as_slice ( ) ) ,
459
- ast:: CookedStr ) ) ;
511
+ // path to the #[test] function: "foo::bar::baz"
512
+ let path_string = ast_util:: path_name_i ( path. as_slice ( ) ) ;
513
+ let name_expr = ecx. expr_str ( span, token:: intern_and_get_ident ( path_string. as_slice ( ) ) ) ;
460
514
461
- let name_expr = box ( GC ) ast:: Expr {
462
- id : ast:: DUMMY_NODE_ID ,
463
- node : ast:: ExprLit ( box ( GC ) name_lit) ,
464
- span : span
465
- } ;
515
+ // self::test::StaticTestName($name_expr)
516
+ let name_expr = ecx. expr_call ( span,
517
+ ecx. expr_path ( test_path ( "StaticTestName" ) ) ,
518
+ vec ! [ name_expr] ) ;
466
519
467
- let mut visible_path = vec ! [ cx. reexport_mod_ident. clone( ) ] ;
468
- visible_path. extend ( path. move_iter ( ) ) ;
469
- let fn_path = cx. ext_cx . path_global ( DUMMY_SP , visible_path) ;
520
+ let ignore_expr = ecx. expr_bool ( span, test. ignore ) ;
521
+ let fail_expr = ecx. expr_bool ( span, test. should_fail ) ;
470
522
471
- let fn_expr = box ( GC ) ast:: Expr {
472
- id : ast:: DUMMY_NODE_ID ,
473
- node : ast:: ExprPath ( fn_path) ,
474
- span : span,
475
- } ;
523
+ // self::test::TestDesc { ... }
524
+ let desc_expr = ecx. expr_struct (
525
+ span,
526
+ test_path ( "TestDesc" ) ,
527
+ vec ! [ field( "name" , name_expr) ,
528
+ field( "ignore" , ignore_expr) ,
529
+ field( "should_fail" , fail_expr) ] ) ;
476
530
477
- let t_expr = if test. bench {
478
- quote_expr ! ( & cx. ext_cx, self :: test:: StaticBenchFn ( $fn_expr) )
479
- } else {
480
- quote_expr ! ( & cx. ext_cx, self :: test:: StaticTestFn ( $fn_expr) )
481
- } ;
482
531
483
- let ignore_expr = if test. ignore {
484
- quote_expr ! ( & cx. ext_cx, true )
485
- } else {
486
- quote_expr ! ( & cx. ext_cx, false )
487
- } ;
532
+ let mut visible_path = vec ! [ cx. reexport_mod_ident. clone( ) ] ;
533
+ visible_path. extend ( path. move_iter ( ) ) ;
488
534
489
- let fail_expr = if test. should_fail {
490
- quote_expr ! ( & cx. ext_cx, true )
491
- } else {
492
- quote_expr ! ( & cx. ext_cx, false )
493
- } ;
535
+ let fn_expr = ecx. expr_path ( ecx. path_global ( span, visible_path) ) ;
494
536
495
- let e = quote_expr ! ( & cx. ext_cx,
496
- self :: test:: TestDescAndFn {
497
- desc: self :: test:: TestDesc {
498
- name: self :: test:: StaticTestName ( $name_expr) ,
499
- ignore: $ignore_expr,
500
- should_fail: $fail_expr
501
- } ,
502
- testfn: $t_expr,
503
- }
504
- ) ;
505
- e
537
+ let variant_name = if test. bench { "StaticBenchFn" } else { "StaticTestFn" } ;
538
+ // self::test::$variant_name($fn_expr)
539
+ let testfn_expr = ecx. expr_call ( span, ecx. expr_path ( test_path ( variant_name) ) , vec ! [ fn_expr] ) ;
540
+
541
+ // self::test::TestDescAndFn { ... }
542
+ ecx. expr_struct ( span,
543
+ test_path ( "TestDescAndFn" ) ,
544
+ vec ! [ field( "desc" , desc_expr) ,
545
+ field( "testfn" , testfn_expr) ] )
506
546
}
0 commit comments