use package_domain::{FileRule, ProjectKind}; use package_infra as infra; use std::{fs, path::Path}; fn rule(id: &str, include: &[&str], required: bool) -> FileRule { FileRule { id: id.into(), source: "appPayload".into(), source_folder: String::new(), target_root: String::new(), include: include.iter().map(|p| (*p).into()).collect(), exclude: Vec::new(), target_relative_path: String::new(), required, } } fn write(root: &Path, relative: &str, bytes: &[u8]) { let path = root.join(relative); fs::create_dir_all(path.parent().unwrap()).unwrap(); fs::write(path, bytes).unwrap(); } /// A Windows program header whose optional header has no known format. fn damaged_program() -> Vec { let mut bytes = vec![0u8; 0x40]; bytes[..2].copy_from_slice(b"MZ"); bytes[0x3c..0x40].copy_from_slice(&0x40u32.to_le_bytes()); bytes.extend(b"PE\0\0"); let mut coff = [0u8; 20]; coff[16..18].copy_from_slice(&2u16.to_le_bytes()); bytes.extend(coff); bytes.extend([0, 0]); bytes } fn project(temp: &Path) -> (infra::ProjectDocument, std::path::PathBuf) { let mut doc = infra::create_project(&temp.join("tool/Tool.isproj"), ProjectKind::Exe).unwrap(); let payload = temp.join("payload"); fs::create_dir_all(&payload).unwrap(); doc.local .sources .insert("appPayload".into(), payload.to_str().unwrap().into()); (doc, payload) } #[test] fn runtime_configs_unreadable_programs_and_broken_rules_are_reported_without_hiding_the_rest() { let temp = tempfile::tempdir().unwrap(); let (doc, payload) = project(temp.path()); write( &payload, "App.runtimeconfig.json", br#"{"runtimeOptions":{"tfm":"net8.0","frameworks":[{"name":"Microsoft.NETCore.App","version":"8.0.0"},{"name":"Microsoft.WindowsDesktop.App","version":"8.0.0"}]}}"#, ); write(&payload, "Self.runtimeconfig.json", br#"{"runtimeOptions":{"includedFrameworks":[{"name":"Microsoft.NETCore.App","version":"8.0.0"}]}}"#); write(&payload, "Broken.dll", &damaged_program()); write(&payload, "Bad.runtimeconfig.json", b"{"); write(&payload, "readme.txt", b"MZ"); let rules = [ rule("app", &["*"], true), rule("gone", &["missing.*"], true), ]; let scan = infra::scan_prerequisites(&rules, &doc.local, doc.path.parent().unwrap(), "x86"); let found: Vec<_> = scan.found.iter().map(|item| item.id.as_str()).collect(); assert_eq!( found, ["dotnet8-desktop-x86"], "the project architecture stands in without App.exe" ); assert_eq!(scan.found[0].reasons.len(), 2); assert!( scan.notes.is_empty() && scan.unsupported.is_empty(), "{scan:?}" ); assert_eq!(scan.problems.len(), 4, "{:?}", scan.problems); assert!(scan.problems[0].starts_with("IncompleteFileList: 파일 규칙 1개를")); assert!( scan.problems[1].contains("gone") && scan.problems[1].contains("MissingRequiredFile"), "the unread rule names itself: {}", scan.problems[1] ); assert!(scan .problems .iter() .any(|p| p.starts_with("Bad.runtimeconfig.json: InvalidRuntimeConfig"))); assert!(scan .problems .iter() .any(|p| p.starts_with("Broken.dll: InvalidPeFile"))); } /// The test program itself is built by MSVC with the shared C runtime, so it imports VCRUNTIME140.dll. #[cfg(all(windows, target_env = "msvc"))] #[test] fn a_real_program_built_with_visual_cpp_asks_for_the_runtime_unless_it_ships_it() { let temp = tempfile::tempdir().unwrap(); let (doc, payload) = project(temp.path()); fs::copy(std::env::current_exe().unwrap(), payload.join("App.exe")).unwrap(); let arch = if cfg!(target_arch = "x86") { "x86" } else { "x64" }; let root = doc.path.parent().unwrap(); let scan = infra::scan_prerequisites(&[rule("app", &["*"], true)], &doc.local, root, "x64"); assert!(scan.problems.is_empty(), "{:?}", scan.problems); let vc = scan .found .iter() .find(|item| item.id == format!("vc14-{arch}")) .expect("Visual C++ runtime"); assert!(vc.reasons.iter().all(|reason| reason.file == "App.exe")); write(&payload, "vcruntime140.dll", b"app-local copy"); let shipped = infra::scan_prerequisites(&[rule("app", &["*"], true)], &doc.local, root, "x64"); let vc = shipped .found .iter() .find(|item| item.id.starts_with("vc14-")); assert!(vc.is_none_or(|item| item .reasons .iter() .all(|r| !r.clue.to_lowercase().starts_with("vcruntime140.dll")))); }