|  | 
|  | 1 | +//! Smoke test for the rustc diagnostics translation infrastructure. | 
|  | 2 | +//! | 
|  | 3 | +//! # References | 
|  | 4 | +//! | 
|  | 5 | +//! - Current tracking issue: <https://github.com/rust-lang/rust/issues/132181>. | 
|  | 6 | +//! - Old tracking issue: <https://github.com/rust-lang/rust/issues/100717> | 
|  | 7 | +//! - Initial translation infra implementation: <https://github.com/rust-lang/rust/pull/95512>. | 
|  | 8 | +
 | 
|  | 9 | +// This test uses symbolic links to stub out a fake sysroot to save testing time. | 
|  | 10 | +//@ needs-symlink | 
|  | 11 | +//@ needs-subprocess | 
|  | 12 | + | 
|  | 13 | +#![deny(warnings)] | 
|  | 14 | + | 
|  | 15 | +use std::path::{Path, PathBuf}; | 
|  | 16 | + | 
|  | 17 | +use run_make_support::rustc::sysroot; | 
|  | 18 | +use run_make_support::{cwd, rfs, run_in_tmpdir, rustc}; | 
|  | 19 | + | 
|  | 20 | +fn main() { | 
|  | 21 | +    builtin_fallback_bundle(); | 
|  | 22 | +    additional_primary_bundle(); | 
|  | 23 | +    missing_slug_prefers_fallback_bundle(); | 
|  | 24 | +    broken_primary_bundle_prefers_fallback_bundle(); | 
|  | 25 | +    locale_sysroot(); | 
|  | 26 | +    missing_sysroot(); | 
|  | 27 | +    file_sysroot(); | 
|  | 28 | +} | 
|  | 29 | + | 
|  | 30 | +/// Check that the test works normally, using the built-in fallback bundle. | 
|  | 31 | +fn builtin_fallback_bundle() { | 
|  | 32 | +    rustc().input("test.rs").run_fail().assert_stderr_contains("struct literal body without path"); | 
|  | 33 | +} | 
|  | 34 | + | 
|  | 35 | +/// Check that a primary bundle can be loaded and will be preferentially used where possible. | 
|  | 36 | +fn additional_primary_bundle() { | 
|  | 37 | +    rustc() | 
|  | 38 | +        .input("test.rs") | 
|  | 39 | +        .arg("-Ztranslate-additional-ftl=working.ftl") | 
|  | 40 | +        .run_fail() | 
|  | 41 | +        .assert_stderr_contains("this is a test message"); | 
|  | 42 | +} | 
|  | 43 | + | 
|  | 44 | +/// Check that a primary bundle without the desired message will use the fallback bundle. | 
|  | 45 | +fn missing_slug_prefers_fallback_bundle() { | 
|  | 46 | +    rustc() | 
|  | 47 | +        .input("test.rs") | 
|  | 48 | +        .arg("-Ztranslate-additional-ftl=missing.ftl") | 
|  | 49 | +        .run_fail() | 
|  | 50 | +        .assert_stderr_contains("struct literal body without path"); | 
|  | 51 | +} | 
|  | 52 | + | 
|  | 53 | +/// Check that a primary bundle with a broken message (e.g. an interpolated variable is not | 
|  | 54 | +/// provided) will use the fallback bundle. | 
|  | 55 | +fn broken_primary_bundle_prefers_fallback_bundle() { | 
|  | 56 | +    // FIXME(#135817): as of the rmake.rs port, the compiler actually ICEs on the additional | 
|  | 57 | +    // `broken.ftl`, even though the original intention seems to be that it should gracefully | 
|  | 58 | +    // failover to the fallback bundle. On `aarch64-apple-darwin`, somehow it *doesn't* ICE. | 
|  | 59 | + | 
|  | 60 | +    rustc() | 
|  | 61 | +        .env("RUSTC_ICE", "0") // disable ICE dump file, not needed | 
|  | 62 | +        .input("test.rs") | 
|  | 63 | +        .arg("-Ztranslate-additional-ftl=broken.ftl") | 
|  | 64 | +        .run_fail(); | 
|  | 65 | +} | 
|  | 66 | + | 
|  | 67 | +#[track_caller] | 
|  | 68 | +fn shallow_symlink_dir_entries(src_dir: &Path, dst_dir: &Path) { | 
|  | 69 | +    for entry in rfs::read_dir(src_dir) { | 
|  | 70 | +        let entry = entry.unwrap(); | 
|  | 71 | +        let src_entry_path = entry.path(); | 
|  | 72 | +        let src_filename = src_entry_path.file_name().unwrap(); | 
|  | 73 | +        let meta = rfs::symlink_metadata(&src_entry_path); | 
|  | 74 | +        if meta.is_symlink() || meta.is_file() { | 
|  | 75 | +            rfs::symlink_file(&src_entry_path, dst_dir.join(src_filename)); | 
|  | 76 | +        } else if meta.is_dir() { | 
|  | 77 | +            rfs::symlink_dir(&src_entry_path, dst_dir.join(src_filename)); | 
|  | 78 | +        } else { | 
|  | 79 | +            unreachable!() | 
|  | 80 | +        } | 
|  | 81 | +    } | 
|  | 82 | +} | 
|  | 83 | + | 
|  | 84 | +#[track_caller] | 
|  | 85 | +fn shallow_symlink_dir_entries_materialize_single_dir( | 
|  | 86 | +    src_dir: &Path, | 
|  | 87 | +    dst_dir: &Path, | 
|  | 88 | +    dir_filename: &str, | 
|  | 89 | +) { | 
|  | 90 | +    shallow_symlink_dir_entries(src_dir, dst_dir); | 
|  | 91 | + | 
|  | 92 | +    let dst_symlink_meta = rfs::symlink_metadata(dst_dir.join(dir_filename)); | 
|  | 93 | + | 
|  | 94 | +    if dst_symlink_meta.is_file() || dst_symlink_meta.is_dir() { | 
|  | 95 | +        unreachable!(); | 
|  | 96 | +    } | 
|  | 97 | + | 
|  | 98 | +    #[cfg(windows)] | 
|  | 99 | +    { | 
|  | 100 | +        use std::os::windows::fs::FileTypeExt as _; | 
|  | 101 | +        if dst_symlink_meta.file_type().is_symlink_file() { | 
|  | 102 | +            rfs::remove_file(dst_dir.join(dir_filename)); | 
|  | 103 | +        } else if dst_symlink_meta.file_type().is_symlink_dir() { | 
|  | 104 | +            rfs::remove_dir(dst_dir.join(dir_filename)); | 
|  | 105 | +        } else { | 
|  | 106 | +            unreachable!(); | 
|  | 107 | +        } | 
|  | 108 | +    } | 
|  | 109 | +    #[cfg(not(windows))] | 
|  | 110 | +    { | 
|  | 111 | +        rfs::remove_file(dst_dir.join(dir_filename)); | 
|  | 112 | +    } | 
|  | 113 | + | 
|  | 114 | +    rfs::create_dir_all(dst_dir.join(dir_filename)); | 
|  | 115 | +} | 
|  | 116 | + | 
|  | 117 | +#[track_caller] | 
|  | 118 | +fn setup_fakeroot_parents() -> PathBuf { | 
|  | 119 | +    let sysroot = sysroot(); | 
|  | 120 | +    let fakeroot = cwd().join("fakeroot"); | 
|  | 121 | +    rfs::create_dir_all(&fakeroot); | 
|  | 122 | +    shallow_symlink_dir_entries_materialize_single_dir(&sysroot, &fakeroot, "lib"); | 
|  | 123 | +    shallow_symlink_dir_entries_materialize_single_dir( | 
|  | 124 | +        &sysroot.join("lib"), | 
|  | 125 | +        &fakeroot.join("lib"), | 
|  | 126 | +        "rustlib", | 
|  | 127 | +    ); | 
|  | 128 | +    shallow_symlink_dir_entries_materialize_single_dir( | 
|  | 129 | +        &sysroot.join("lib").join("rustlib"), | 
|  | 130 | +        &fakeroot.join("lib").join("rustlib"), | 
|  | 131 | +        "src", | 
|  | 132 | +    ); | 
|  | 133 | +    shallow_symlink_dir_entries( | 
|  | 134 | +        &sysroot.join("lib").join("rustlib").join("src"), | 
|  | 135 | +        &fakeroot.join("lib").join("rustlib").join("src"), | 
|  | 136 | +    ); | 
|  | 137 | +    fakeroot | 
|  | 138 | +} | 
|  | 139 | + | 
|  | 140 | +/// Check that a locale can be loaded from the sysroot given a language identifier by making a local | 
|  | 141 | +/// copy of the sysroot and adding the custom locale to it. | 
|  | 142 | +fn locale_sysroot() { | 
|  | 143 | +    run_in_tmpdir(|| { | 
|  | 144 | +        let fakeroot = setup_fakeroot_parents(); | 
|  | 145 | + | 
|  | 146 | +        // When download-rustc is enabled, real sysroot will have a share directory. Delete the link | 
|  | 147 | +        // to it. | 
|  | 148 | +        let _ = std::fs::remove_file(fakeroot.join("share")); | 
|  | 149 | + | 
|  | 150 | +        let fake_locale_path = fakeroot.join("share").join("locale").join("zh-CN"); | 
|  | 151 | +        rfs::create_dir_all(&fake_locale_path); | 
|  | 152 | +        rfs::symlink_file( | 
|  | 153 | +            cwd().join("working.ftl"), | 
|  | 154 | +            fake_locale_path.join("basic-translation.ftl"), | 
|  | 155 | +        ); | 
|  | 156 | + | 
|  | 157 | +        rustc() | 
|  | 158 | +            .env("RUSTC_ICE", "0") | 
|  | 159 | +            .input("test.rs") | 
|  | 160 | +            .sysroot(&fakeroot) | 
|  | 161 | +            .arg("-Ztranslate-lang=zh-CN") | 
|  | 162 | +            .run_fail() | 
|  | 163 | +            .assert_stderr_contains("this is a test message"); | 
|  | 164 | +    }); | 
|  | 165 | +} | 
|  | 166 | + | 
|  | 167 | +/// Check that the compiler errors out when the sysroot requested cannot be found. This test might | 
|  | 168 | +/// start failing if there actually exists a Klingon translation of rustc's error messages. | 
|  | 169 | +fn missing_sysroot() { | 
|  | 170 | +    run_in_tmpdir(|| { | 
|  | 171 | +        rustc() | 
|  | 172 | +            .input("test.rs") | 
|  | 173 | +            .arg("-Ztranslate-lang=tlh") | 
|  | 174 | +            .run_fail() | 
|  | 175 | +            .assert_stderr_contains("missing locale directory"); | 
|  | 176 | +    }); | 
|  | 177 | +} | 
|  | 178 | + | 
|  | 179 | +/// Check that the compiler errors out when the directory for the locale in the sysroot is actually | 
|  | 180 | +/// a file. | 
|  | 181 | +fn file_sysroot() { | 
|  | 182 | +    run_in_tmpdir(|| { | 
|  | 183 | +        let fakeroot = setup_fakeroot_parents(); | 
|  | 184 | +        rfs::create_dir_all(fakeroot.join("share").join("locale")); | 
|  | 185 | +        rfs::write(fakeroot.join("share").join("locale").join("zh-CN"), b"not a dir"); | 
|  | 186 | + | 
|  | 187 | +        rustc() | 
|  | 188 | +            .input("test.rs") | 
|  | 189 | +            .sysroot(&fakeroot) | 
|  | 190 | +            .arg("-Ztranslate-lang=zh-CN") | 
|  | 191 | +            .run_fail() | 
|  | 192 | +            .assert_stderr_contains("is not a directory"); | 
|  | 193 | +    }); | 
|  | 194 | +} | 
0 commit comments