@@ -1267,6 +1267,166 @@ pub(crate) trait Tester {
12671267 fn register_header ( & mut self , _name : & str , _level : u32 ) { }
12681268}
12691269
1270+ /// If there are too many doctests than can be compiled at once, we need to limit the size
1271+ /// of the generated test to prevent everything to break down.
1272+ ///
1273+ /// We add all doctests one by one through [`DocTestRunner::add_test`] and when it reaches
1274+ /// `TEST_BATCH_SIZE` size or is dropped, it runs all stored doctests at once.
1275+ struct DocTestRunner < ' a > {
1276+ nb_errors : & ' a mut usize ,
1277+ ran_edition_tests : & ' a mut usize ,
1278+ standalone : & ' a mut Vec < TestDescAndFn > ,
1279+ crate_attrs : FxHashSet < String > ,
1280+ edition : Edition ,
1281+ ids : String ,
1282+ output : String ,
1283+ supports_color : bool ,
1284+ rustdoc_test_options : & ' a Arc < IndividualTestOptions > ,
1285+ outdir : & ' a Arc < DirState > ,
1286+ nb_tests : usize ,
1287+ doctests : Vec < DocTest > ,
1288+ opts : & ' a GlobalTestOptions ,
1289+ test_args : & ' a [ String ] ,
1290+ unused_externs : & ' a Arc < Mutex < Vec < UnusedExterns > > > ,
1291+ }
1292+
1293+ impl < ' a > DocTestRunner < ' a > {
1294+ const TEST_BATCH_SIZE : usize = 250 ;
1295+
1296+ fn new (
1297+ nb_errors : & ' a mut usize ,
1298+ ran_edition_tests : & ' a mut usize ,
1299+ standalone : & ' a mut Vec < TestDescAndFn > ,
1300+ edition : Edition ,
1301+ rustdoc_test_options : & ' a Arc < IndividualTestOptions > ,
1302+ outdir : & ' a Arc < DirState > ,
1303+ opts : & ' a GlobalTestOptions ,
1304+ test_args : & ' a [ String ] ,
1305+ unused_externs : & ' a Arc < Mutex < Vec < UnusedExterns > > > ,
1306+ ) -> Self {
1307+ Self {
1308+ nb_errors,
1309+ ran_edition_tests,
1310+ standalone,
1311+ edition,
1312+ crate_attrs : FxHashSet :: default ( ) ,
1313+ ids : String :: new ( ) ,
1314+ output : String :: new ( ) ,
1315+ supports_color : true ,
1316+ rustdoc_test_options,
1317+ outdir,
1318+ nb_tests : 0 ,
1319+ doctests : Vec :: with_capacity ( Self :: TEST_BATCH_SIZE ) ,
1320+ opts,
1321+ test_args,
1322+ unused_externs,
1323+ }
1324+ }
1325+
1326+ fn add_test ( & mut self , doctest : DocTest ) {
1327+ for line in doctest. crate_attrs . split ( '\n' ) {
1328+ self . crate_attrs . insert ( line. to_string ( ) ) ;
1329+ }
1330+ if !self . ids . is_empty ( ) {
1331+ self . ids . push ( ',' ) ;
1332+ }
1333+ self . ids . push_str ( & format ! (
1334+ "{}::TEST" ,
1335+ doctest. generate_test_desc( self . nb_tests, & mut self . output)
1336+ ) ) ;
1337+ self . supports_color &= doctest. supports_color ;
1338+ self . nb_tests += 1 ;
1339+ self . doctests . push ( doctest) ;
1340+
1341+ if self . nb_tests >= Self :: TEST_BATCH_SIZE {
1342+ self . run_tests ( ) ;
1343+ }
1344+ }
1345+
1346+ fn run_tests ( & mut self ) {
1347+ if self . nb_tests == 0 {
1348+ return ;
1349+ }
1350+ let mut code = "\
1351+ #![allow(unused_extern_crates)]
1352+ #![allow(internal_features)]
1353+ #![feature(test)]
1354+ #![feature(rustc_attrs)]
1355+ #![feature(coverage_attribute)]\n "
1356+ . to_string ( ) ;
1357+
1358+ for crate_attr in & self . crate_attrs {
1359+ code. push_str ( crate_attr) ;
1360+ code. push ( '\n' ) ;
1361+ }
1362+
1363+ DocTest :: push_attrs ( & mut code, & self . opts , & mut 0 ) ;
1364+ code. push_str ( "extern crate test;\n " ) ;
1365+
1366+ let test_args =
1367+ self . test_args . iter ( ) . map ( |arg| format ! ( "{arg:?}.to_string()," ) ) . collect :: < String > ( ) ;
1368+ write ! (
1369+ code,
1370+ "\
1371+ {output}
1372+ #[rustc_main]
1373+ #[coverage(off)]
1374+ fn main() {{
1375+ test::test_main(&[{test_args}], vec![{ids}], None);
1376+ }}" ,
1377+ output = self . output,
1378+ ids = self . ids,
1379+ )
1380+ . expect ( "failed to generate test code" ) ;
1381+ let ret = run_test (
1382+ code,
1383+ self . supports_color ,
1384+ None ,
1385+ Arc :: clone ( self . rustdoc_test_options ) ,
1386+ true ,
1387+ Arc :: clone ( self . outdir ) ,
1388+ LangString :: empty_for_test ( ) ,
1389+ self . edition ,
1390+ |_: UnusedExterns | { } ,
1391+ false ,
1392+ ) ;
1393+ if let Err ( TestFailure :: CompileError ) = ret {
1394+ // We failed to compile all compatible tests as one so we push them into the
1395+ // "standalone" doctests.
1396+ debug ! (
1397+ "Failed to compile compatible doctests for edition {} all at once" ,
1398+ self . edition
1399+ ) ;
1400+ for doctest in self . doctests . drain ( ..) {
1401+ self . standalone . push ( doctest. generate_test_desc_and_fn (
1402+ & self . opts ,
1403+ self . edition ,
1404+ Arc :: clone ( self . unused_externs ) ,
1405+ ) ) ;
1406+ }
1407+ } else {
1408+ * self . ran_edition_tests += 1 ;
1409+ if ret. is_err ( ) {
1410+ * self . nb_errors += 1 ;
1411+ }
1412+ }
1413+
1414+ // We reset values.
1415+ self . supports_color = true ;
1416+ self . ids . clear ( ) ;
1417+ self . output . clear ( ) ;
1418+ self . crate_attrs . clear ( ) ;
1419+ self . nb_tests = 0 ;
1420+ self . doctests . clear ( ) ;
1421+ }
1422+ }
1423+
1424+ impl < ' a > Drop for DocTestRunner < ' a > {
1425+ fn drop ( & mut self ) {
1426+ self . run_tests ( ) ;
1427+ }
1428+ }
1429+
12701430#[ derive( Default ) ]
12711431pub ( crate ) struct DocTestKinds {
12721432 /// Tests that cannot be run together with the rest (`compile_fail` and `test_harness`).
@@ -1317,73 +1477,24 @@ impl DocTestKinds {
13171477
13181478 for ( edition, mut doctests) in others {
13191479 doctests. sort_by ( |a, b| a. name . cmp ( & b. name ) ) ;
1320- let mut ids = String :: new ( ) ;
1321- let mut output = "\
1322- #![allow(unused_extern_crates)]
1323- #![allow(internal_features)]
1324- #![feature(test)]
1325- #![feature(rustc_attrs)]
1326- #![feature(coverage_attribute)]\n "
1327- . to_string ( ) ;
1328-
1329- for doctest in & doctests {
1330- output. push_str ( & doctest. crate_attrs ) ;
1331- }
1332-
1333- DocTest :: push_attrs ( & mut output, & opts, & mut 0 ) ;
1334- output. push_str ( "extern crate test;\n " ) ;
1335-
13361480 let rustdoc_test_options = Arc :: clone ( & doctests[ 0 ] . rustdoc_test_options ) ;
13371481 let outdir = Arc :: clone ( & doctests[ 0 ] . outdir ) ;
13381482
1339- let mut supports_color = true ;
1340- for ( pos, doctest) in doctests. iter ( ) . enumerate ( ) {
1341- if !ids. is_empty ( ) {
1342- ids. push ( ',' ) ;
1343- }
1344- ids. push_str ( & format ! ( "{}::TEST" , doctest. generate_test_desc( pos, & mut output) ) ) ;
1345- supports_color &= doctest. supports_color ;
1346- }
1347- let test_args =
1348- test_args. iter ( ) . map ( |arg| format ! ( "{arg:?}.to_string()," ) ) . collect :: < String > ( ) ;
1349- write ! (
1350- output,
1351- "\
1352- #[rustc_main]
1353- #[coverage(off)]
1354- fn main() {{
1355- test::test_main(&[{test_args}], vec![{ids}], None);
1356- }}" ,
1357- )
1358- . unwrap ( ) ;
1359- let ret = run_test (
1360- output,
1361- supports_color,
1362- None ,
1363- rustdoc_test_options,
1364- true ,
1365- outdir,
1366- LangString :: empty_for_test ( ) ,
1483+ // When `DocTestRunner` is dropped, it'll run all pending doctests it didn't already
1484+ // run, so no need to worry about it.
1485+ let mut tests_runner = DocTestRunner :: new (
1486+ & mut nb_errors,
1487+ & mut ran_edition_tests,
1488+ & mut standalone,
13671489 edition,
1368- |_: UnusedExterns | { } ,
1369- false ,
1490+ & rustdoc_test_options,
1491+ & outdir,
1492+ & opts,
1493+ & test_args,
1494+ unused_externs,
13701495 ) ;
1371- if let Err ( TestFailure :: CompileError ) = ret {
1372- // We failed to compile all compatible tests as one so we push them into the
1373- // "standalone" doctests.
1374- debug ! ( "Failed to compile compatible doctests for edition {edition} all at once" ) ;
1375- for doctest in doctests {
1376- standalone. push ( doctest. generate_test_desc_and_fn (
1377- & opts,
1378- edition,
1379- Arc :: clone ( unused_externs) ,
1380- ) ) ;
1381- }
1382- } else {
1383- ran_edition_tests += 1 ;
1384- if ret. is_err ( ) {
1385- nb_errors += 1 ;
1386- }
1496+ for doctest in doctests {
1497+ tests_runner. add_test ( doctest) ;
13871498 }
13881499 }
13891500
0 commit comments