11use std:: collections:: BTreeSet ;
2- use std:: env;
32use std:: fs:: { File , remove_file} ;
43use std:: io:: Write ;
54use std:: path:: Path ;
5+ use std:: { env, fs} ;
66
77use build_helper:: ci:: CiEnv ;
88use clap:: CommandFactory ;
@@ -23,6 +23,19 @@ pub(crate) fn parse(config: &str) -> Config {
2323 )
2424}
2525
26+ fn get_toml ( file : & Path ) -> Result < TomlConfig , toml:: de:: Error > {
27+ let contents = std:: fs:: read_to_string ( file) . unwrap ( ) ;
28+ toml:: from_str ( & contents) . and_then ( |table : toml:: Value | TomlConfig :: deserialize ( table) )
29+ }
30+
31+ /// Helps with debugging by using consistent test-specific directories instead of
32+ /// random temporary directories.
33+ fn test_specific_dirname ( ) -> String {
34+ let current = std:: thread:: current ( ) ;
35+ // Replace "::" with "_" to make it safe for directory names on Windows systems
36+ current. name ( ) . unwrap ( ) . replace ( "::" , "_" )
37+ }
38+
2639#[ test]
2740fn download_ci_llvm ( ) {
2841 let config = parse ( "llvm.download-ci-llvm = false" ) ;
@@ -539,3 +552,210 @@ fn test_ci_flag() {
539552 let config = Config :: parse_inner ( Flags :: parse ( & [ "check" . into ( ) ] ) , |& _| toml:: from_str ( "" ) ) ;
540553 assert_eq ! ( config. is_running_on_ci, CiEnv :: is_ci( ) ) ;
541554}
555+
556+ #[ test]
557+ fn test_precedence_of_includes ( ) {
558+ let testdir = parse ( "" ) . tempdir ( ) . join ( test_specific_dirname ( ) ) ;
559+
560+ // clean up any old test files
561+ let _ = fs:: remove_dir_all ( & testdir) ;
562+ let _ = fs:: create_dir_all ( & testdir) ;
563+
564+ let root_config = testdir. join ( "config.toml" ) ;
565+ let root_config_content = br#"
566+ include = ["./extension.toml"]
567+
568+ [llvm]
569+ link-jobs = 2
570+ "# ;
571+ File :: create ( & root_config) . unwrap ( ) . write_all ( root_config_content) . unwrap ( ) ;
572+
573+ let extension = testdir. join ( "extension.toml" ) ;
574+ let extension_content = br#"
575+ change-id=543
576+ include = ["./extension2.toml"]
577+ "# ;
578+ File :: create ( extension) . unwrap ( ) . write_all ( extension_content) . unwrap ( ) ;
579+
580+ let extension = testdir. join ( "extension2.toml" ) ;
581+ let extension_content = br#"
582+ change-id=742
583+
584+ [llvm]
585+ link-jobs = 10
586+
587+ [build]
588+ description = "Some creative description"
589+ "# ;
590+ File :: create ( extension) . unwrap ( ) . write_all ( extension_content) . unwrap ( ) ;
591+
592+ let config = Config :: parse_inner (
593+ Flags :: parse ( & [ "check" . to_owned ( ) , format ! ( "--config={}" , root_config. to_str( ) . unwrap( ) ) ] ) ,
594+ get_toml,
595+ ) ;
596+
597+ assert_eq ! ( config. change_id. unwrap( ) , ChangeId :: Id ( 543 ) ) ;
598+ assert_eq ! ( config. llvm_link_jobs. unwrap( ) , 2 ) ;
599+ assert_eq ! ( config. description. unwrap( ) , "Some creative description" ) ;
600+ }
601+
602+ #[ test]
603+ #[ should_panic( expected = "Cyclic inclusion detected" ) ]
604+ fn test_cyclic_include_direct ( ) {
605+ let testdir = parse ( "" ) . tempdir ( ) . join ( test_specific_dirname ( ) ) ;
606+
607+ // clean up any old test files
608+ let _ = fs:: remove_dir_all ( & testdir) ;
609+ let _ = fs:: create_dir_all ( & testdir) ;
610+
611+ let root_config = testdir. join ( "config.toml" ) ;
612+ let root_config_content = br#"
613+ include = ["./extension.toml"]
614+ "# ;
615+ File :: create ( & root_config) . unwrap ( ) . write_all ( root_config_content) . unwrap ( ) ;
616+
617+ let extension = testdir. join ( "extension.toml" ) ;
618+ let extension_content = br#"
619+ include = ["./config.toml"]
620+ "# ;
621+ File :: create ( extension) . unwrap ( ) . write_all ( extension_content) . unwrap ( ) ;
622+
623+ let config = Config :: parse_inner (
624+ Flags :: parse ( & [ "check" . to_owned ( ) , format ! ( "--config={}" , root_config. to_str( ) . unwrap( ) ) ] ) ,
625+ get_toml,
626+ ) ;
627+ }
628+
629+ #[ test]
630+ #[ should_panic( expected = "Cyclic inclusion detected" ) ]
631+ fn test_cyclic_include_indirect ( ) {
632+ let testdir = parse ( "" ) . tempdir ( ) . join ( test_specific_dirname ( ) ) ;
633+
634+ // clean up any old test files
635+ let _ = fs:: remove_dir_all ( & testdir) ;
636+ let _ = fs:: create_dir_all ( & testdir) ;
637+
638+ let root_config = testdir. join ( "config.toml" ) ;
639+ let root_config_content = br#"
640+ include = ["./extension.toml"]
641+ "# ;
642+ File :: create ( & root_config) . unwrap ( ) . write_all ( root_config_content) . unwrap ( ) ;
643+
644+ let extension = testdir. join ( "extension.toml" ) ;
645+ let extension_content = br#"
646+ include = ["./extension2.toml"]
647+ "# ;
648+ File :: create ( extension) . unwrap ( ) . write_all ( extension_content) . unwrap ( ) ;
649+
650+ let extension = testdir. join ( "extension2.toml" ) ;
651+ let extension_content = br#"
652+ include = ["./extension3.toml"]
653+ "# ;
654+ File :: create ( extension) . unwrap ( ) . write_all ( extension_content) . unwrap ( ) ;
655+
656+ let extension = testdir. join ( "extension3.toml" ) ;
657+ let extension_content = br#"
658+ include = ["./extension.toml"]
659+ "# ;
660+ File :: create ( extension) . unwrap ( ) . write_all ( extension_content) . unwrap ( ) ;
661+
662+ let config = Config :: parse_inner (
663+ Flags :: parse ( & [ "check" . to_owned ( ) , format ! ( "--config={}" , root_config. to_str( ) . unwrap( ) ) ] ) ,
664+ get_toml,
665+ ) ;
666+ }
667+
668+ #[ test]
669+ fn test_include_absolute_paths ( ) {
670+ let testdir = parse ( "" ) . tempdir ( ) . join ( test_specific_dirname ( ) ) ;
671+
672+ // clean up any old test files
673+ let _ = fs:: remove_dir_all ( & testdir) ;
674+ let _ = fs:: create_dir_all ( & testdir) ;
675+
676+ let extension = testdir. join ( "extension.toml" ) ;
677+ File :: create ( & extension) . unwrap ( ) . write_all ( & [ ] ) . unwrap ( ) ;
678+
679+ let root_config = testdir. join ( "config.toml" ) ;
680+ let root_config_content =
681+ format ! ( "include = [\" {}\" ]" , extension. canonicalize( ) . unwrap( ) . to_str( ) . unwrap( ) ) ;
682+ File :: create ( & root_config) . unwrap ( ) . write_all ( root_config_content. as_bytes ( ) ) . unwrap ( ) ;
683+
684+ let config = Config :: parse_inner (
685+ Flags :: parse ( & [ "check" . to_owned ( ) , format ! ( "--config={}" , root_config. to_str( ) . unwrap( ) ) ] ) ,
686+ get_toml,
687+ ) ;
688+ }
689+
690+ #[ test]
691+ fn test_include_relative_paths ( ) {
692+ let testdir = parse ( "" ) . tempdir ( ) . join ( test_specific_dirname ( ) ) ;
693+
694+ // clean up any old test files
695+ let _ = fs:: remove_dir_all ( & testdir) ;
696+ let _ = fs:: create_dir_all ( & testdir. join ( "subdir/another_subdir" ) ) ;
697+
698+ let root_config = testdir. join ( "config.toml" ) ;
699+ let root_config_content = br#"
700+ include = ["./subdir/extension.toml"]
701+ "# ;
702+ File :: create ( & root_config) . unwrap ( ) . write_all ( root_config_content) . unwrap ( ) ;
703+
704+ let extension = testdir. join ( "subdir/extension.toml" ) ;
705+ let extension_content = br#"
706+ include = ["../extension2.toml"]
707+ "# ;
708+ File :: create ( extension) . unwrap ( ) . write_all ( extension_content) . unwrap ( ) ;
709+
710+ let extension = testdir. join ( "extension2.toml" ) ;
711+ let extension_content = br#"
712+ include = ["./subdir/another_subdir/extension3.toml"]
713+ "# ;
714+ File :: create ( extension) . unwrap ( ) . write_all ( extension_content) . unwrap ( ) ;
715+
716+ let extension = testdir. join ( "subdir/another_subdir/extension3.toml" ) ;
717+ let extension_content = br#"
718+ include = ["../../extension4.toml"]
719+ "# ;
720+ File :: create ( extension) . unwrap ( ) . write_all ( extension_content) . unwrap ( ) ;
721+
722+ let extension = testdir. join ( "extension4.toml" ) ;
723+ File :: create ( extension) . unwrap ( ) . write_all ( & [ ] ) . unwrap ( ) ;
724+
725+ let config = Config :: parse_inner (
726+ Flags :: parse ( & [ "check" . to_owned ( ) , format ! ( "--config={}" , root_config. to_str( ) . unwrap( ) ) ] ) ,
727+ get_toml,
728+ ) ;
729+ }
730+
731+ #[ test]
732+ fn test_include_precedence_over_profile ( ) {
733+ let testdir = parse ( "" ) . tempdir ( ) . join ( test_specific_dirname ( ) ) ;
734+
735+ // clean up any old test files
736+ let _ = fs:: remove_dir_all ( & testdir) ;
737+ let _ = fs:: create_dir_all ( & testdir) ;
738+
739+ let root_config = testdir. join ( "config.toml" ) ;
740+ let root_config_content = br#"
741+ profile = "dist"
742+ include = ["./extension.toml"]
743+ "# ;
744+ File :: create ( & root_config) . unwrap ( ) . write_all ( root_config_content) . unwrap ( ) ;
745+
746+ let extension = testdir. join ( "extension.toml" ) ;
747+ let extension_content = br#"
748+ [rust]
749+ channel = "dev"
750+ "# ;
751+ File :: create ( extension) . unwrap ( ) . write_all ( extension_content) . unwrap ( ) ;
752+
753+ let config = Config :: parse_inner (
754+ Flags :: parse ( & [ "check" . to_owned ( ) , format ! ( "--config={}" , root_config. to_str( ) . unwrap( ) ) ] ) ,
755+ get_toml,
756+ ) ;
757+
758+ // "dist" profile would normally set the channel to "auto-detect", but includes should
759+ // override profile settings, so we expect this to be "dev" here.
760+ assert_eq ! ( config. channel, "dev" ) ;
761+ }
0 commit comments