11use std:: collections:: BTreeSet ;
2- use std:: env;
32use std:: fs:: { File , remove_file} ;
43use std:: io:: Write ;
5- use std:: path:: Path ;
4+ use std:: path:: { Path , PathBuf } ;
5+ use std:: { env, fs} ;
66
77use build_helper:: ci:: CiEnv ;
88use clap:: CommandFactory ;
@@ -23,6 +23,27 @@ 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 prepare_test_specific_dir ( ) -> PathBuf {
34+ let current = std:: thread:: current ( ) ;
35+ // Replace "::" with "_" to make it safe for directory names on Windows systems
36+ let test_path = current. name ( ) . unwrap ( ) . replace ( "::" , "_" ) ;
37+
38+ let testdir = parse ( "" ) . tempdir ( ) . join ( test_path) ;
39+
40+ // clean up any old test files
41+ let _ = fs:: remove_dir_all ( & testdir) ;
42+ let _ = fs:: create_dir_all ( & testdir) ;
43+
44+ testdir
45+ }
46+
2647#[ test]
2748fn download_ci_llvm ( ) {
2849 let config = parse ( "llvm.download-ci-llvm = false" ) ;
@@ -539,3 +560,189 @@ fn test_ci_flag() {
539560 let config = Config :: parse_inner ( Flags :: parse ( & [ "check" . into ( ) ] ) , |& _| toml:: from_str ( "" ) ) ;
540561 assert_eq ! ( config. is_running_on_ci, CiEnv :: is_ci( ) ) ;
541562}
563+
564+ #[ test]
565+ fn test_precedence_of_includes ( ) {
566+ let testdir = prepare_test_specific_dir ( ) ;
567+
568+ let root_config = testdir. join ( "config.toml" ) ;
569+ let root_config_content = br#"
570+ include = ["./extension.toml"]
571+
572+ [llvm]
573+ link-jobs = 2
574+ "# ;
575+ File :: create ( & root_config) . unwrap ( ) . write_all ( root_config_content) . unwrap ( ) ;
576+
577+ let extension = testdir. join ( "extension.toml" ) ;
578+ let extension_content = br#"
579+ change-id=543
580+ include = ["./extension2.toml"]
581+ "# ;
582+ File :: create ( extension) . unwrap ( ) . write_all ( extension_content) . unwrap ( ) ;
583+
584+ let extension = testdir. join ( "extension2.toml" ) ;
585+ let extension_content = br#"
586+ change-id=742
587+
588+ [llvm]
589+ link-jobs = 10
590+
591+ [build]
592+ description = "Some creative description"
593+ "# ;
594+ File :: create ( extension) . unwrap ( ) . write_all ( extension_content) . unwrap ( ) ;
595+
596+ let config = Config :: parse_inner (
597+ Flags :: parse ( & [ "check" . to_owned ( ) , format ! ( "--config={}" , root_config. to_str( ) . unwrap( ) ) ] ) ,
598+ get_toml,
599+ ) ;
600+
601+ assert_eq ! ( config. change_id. unwrap( ) , ChangeId :: Id ( 543 ) ) ;
602+ assert_eq ! ( config. llvm_link_jobs. unwrap( ) , 2 ) ;
603+ assert_eq ! ( config. description. unwrap( ) , "Some creative description" ) ;
604+ }
605+
606+ #[ test]
607+ #[ should_panic( expected = "Cyclic inclusion detected" ) ]
608+ fn test_cyclic_include_direct ( ) {
609+ let testdir = prepare_test_specific_dir ( ) ;
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 = prepare_test_specific_dir ( ) ;
633+
634+ let root_config = testdir. join ( "config.toml" ) ;
635+ let root_config_content = br#"
636+ include = ["./extension.toml"]
637+ "# ;
638+ File :: create ( & root_config) . unwrap ( ) . write_all ( root_config_content) . unwrap ( ) ;
639+
640+ let extension = testdir. join ( "extension.toml" ) ;
641+ let extension_content = br#"
642+ include = ["./extension2.toml"]
643+ "# ;
644+ File :: create ( extension) . unwrap ( ) . write_all ( extension_content) . unwrap ( ) ;
645+
646+ let extension = testdir. join ( "extension2.toml" ) ;
647+ let extension_content = br#"
648+ include = ["./extension3.toml"]
649+ "# ;
650+ File :: create ( extension) . unwrap ( ) . write_all ( extension_content) . unwrap ( ) ;
651+
652+ let extension = testdir. join ( "extension3.toml" ) ;
653+ let extension_content = br#"
654+ include = ["./extension.toml"]
655+ "# ;
656+ File :: create ( extension) . unwrap ( ) . write_all ( extension_content) . unwrap ( ) ;
657+
658+ let config = Config :: parse_inner (
659+ Flags :: parse ( & [ "check" . to_owned ( ) , format ! ( "--config={}" , root_config. to_str( ) . unwrap( ) ) ] ) ,
660+ get_toml,
661+ ) ;
662+ }
663+
664+ #[ test]
665+ fn test_include_absolute_paths ( ) {
666+ let testdir = prepare_test_specific_dir ( ) ;
667+
668+ let extension = testdir. join ( "extension.toml" ) ;
669+ File :: create ( & extension) . unwrap ( ) . write_all ( & [ ] ) . unwrap ( ) ;
670+
671+ let root_config = testdir. join ( "config.toml" ) ;
672+ let extension_absolute_path =
673+ extension. canonicalize ( ) . unwrap ( ) . to_str ( ) . unwrap ( ) . replace ( '\\' , r"\\" ) ;
674+ let root_config_content = format ! ( r#"include = ["{}"]"# , extension_absolute_path) ;
675+ File :: create ( & root_config) . unwrap ( ) . write_all ( root_config_content. as_bytes ( ) ) . unwrap ( ) ;
676+
677+ let config = Config :: parse_inner (
678+ Flags :: parse ( & [ "check" . to_owned ( ) , format ! ( "--config={}" , root_config. to_str( ) . unwrap( ) ) ] ) ,
679+ get_toml,
680+ ) ;
681+ }
682+
683+ #[ test]
684+ fn test_include_relative_paths ( ) {
685+ let testdir = prepare_test_specific_dir ( ) ;
686+
687+ let _ = fs:: create_dir_all ( & testdir. join ( "subdir/another_subdir" ) ) ;
688+
689+ let root_config = testdir. join ( "config.toml" ) ;
690+ let root_config_content = br#"
691+ include = ["./subdir/extension.toml"]
692+ "# ;
693+ File :: create ( & root_config) . unwrap ( ) . write_all ( root_config_content) . unwrap ( ) ;
694+
695+ let extension = testdir. join ( "subdir/extension.toml" ) ;
696+ let extension_content = br#"
697+ include = ["../extension2.toml"]
698+ "# ;
699+ File :: create ( extension) . unwrap ( ) . write_all ( extension_content) . unwrap ( ) ;
700+
701+ let extension = testdir. join ( "extension2.toml" ) ;
702+ let extension_content = br#"
703+ include = ["./subdir/another_subdir/extension3.toml"]
704+ "# ;
705+ File :: create ( extension) . unwrap ( ) . write_all ( extension_content) . unwrap ( ) ;
706+
707+ let extension = testdir. join ( "subdir/another_subdir/extension3.toml" ) ;
708+ let extension_content = br#"
709+ include = ["../../extension4.toml"]
710+ "# ;
711+ File :: create ( extension) . unwrap ( ) . write_all ( extension_content) . unwrap ( ) ;
712+
713+ let extension = testdir. join ( "extension4.toml" ) ;
714+ File :: create ( extension) . unwrap ( ) . write_all ( & [ ] ) . unwrap ( ) ;
715+
716+ let config = Config :: parse_inner (
717+ Flags :: parse ( & [ "check" . to_owned ( ) , format ! ( "--config={}" , root_config. to_str( ) . unwrap( ) ) ] ) ,
718+ get_toml,
719+ ) ;
720+ }
721+
722+ #[ test]
723+ fn test_include_precedence_over_profile ( ) {
724+ let testdir = prepare_test_specific_dir ( ) ;
725+
726+ let root_config = testdir. join ( "config.toml" ) ;
727+ let root_config_content = br#"
728+ profile = "dist"
729+ include = ["./extension.toml"]
730+ "# ;
731+ File :: create ( & root_config) . unwrap ( ) . write_all ( root_config_content) . unwrap ( ) ;
732+
733+ let extension = testdir. join ( "extension.toml" ) ;
734+ let extension_content = br#"
735+ [rust]
736+ channel = "dev"
737+ "# ;
738+ File :: create ( extension) . unwrap ( ) . write_all ( extension_content) . unwrap ( ) ;
739+
740+ let config = Config :: parse_inner (
741+ Flags :: parse ( & [ "check" . to_owned ( ) , format ! ( "--config={}" , root_config. to_str( ) . unwrap( ) ) ] ) ,
742+ get_toml,
743+ ) ;
744+
745+ // "dist" profile would normally set the channel to "auto-detect", but includes should
746+ // override profile settings, so we expect this to be "dev" here.
747+ assert_eq ! ( config. channel, "dev" ) ;
748+ }
0 commit comments