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