|
| 1 | +//! One `binary()` and one `git_sha256` for every CLI test target (#824). |
| 2 | +//! |
| 3 | +//! `common/mod.rs` owns both helpers, and its own tests pin `git_sha256` |
| 4 | +//! to the canonical Git-blob hash and to production's |
| 5 | +//! `compute_git_sha256_from_bytes` (the two shapes the per-file copies |
| 6 | +//! took). This ratchet keeps new private copies out: a file may define its |
| 7 | +//! own only while it is listed below. |
| 8 | +
|
| 9 | +use std::path::Path; |
| 10 | + |
| 11 | +/// Files that still define a private `binary()` or `git_sha256`. Each is |
| 12 | +/// changed by an open PR, or is a shared module whose includers don't all |
| 13 | +/// declare `mod common`. Migrate a file onto `common` and delete its entry; |
| 14 | +/// a stale entry is not an error, so a PR that migrates one never turns |
| 15 | +/// another red. |
| 16 | +const PENDING_PRIVATE_HELPERS: &[&str] = &[ |
| 17 | + "apply/apply_network.rs", |
| 18 | + "apply/in_process_npm_multicopy.rs", |
| 19 | + "cli/api_client_errors_e2e.rs", |
| 20 | + "cli/covgap_api_client.rs", |
| 21 | + "cli/output_modes_e2e.rs", |
| 22 | + "cli/telemetry_e2e.rs", |
| 23 | + "cli_scan_silent.rs", |
| 24 | + "covgap_commands_scan_mod.rs", |
| 25 | + "diff_created_file_e2e.rs", |
| 26 | + "docker_e2e_npm.rs", |
| 27 | + "docker_e2e_nuget.rs", |
| 28 | + "docker_e2e_pypi.rs", |
| 29 | + "e2e_embedded_vex.rs", |
| 30 | + "e2e_gem.rs", |
| 31 | + "e2e_hosted_production.rs", |
| 32 | + "e2e_npm.rs", |
| 33 | + "e2e_pypi.rs", |
| 34 | + "e2e_redirect_gem_build.rs", |
| 35 | + "e2e_redirect_gradle_build.rs", |
| 36 | + "e2e_redirect_npm_build.rs", |
| 37 | + "e2e_redirect_pnpm_build.rs", |
| 38 | + "e2e_scan.rs", |
| 39 | + "e2e_vendor_composer_build.rs", |
| 40 | + "e2e_vendor_npm_build.rs", |
| 41 | + "e2e_vendor_pnpm_build.rs", |
| 42 | + "e2e_vendor_pypi_build.rs", |
| 43 | + "e2e_vex_lockfile/cargo.rs", |
| 44 | + "e2e_vex_lockfile/maven.rs", |
| 45 | + "e2e_vex_lockfile/uv.rs", |
| 46 | + "e2e_vex_lockfile/yarn.rs", |
| 47 | + "e2e_vex_redirect.rs", |
| 48 | + "e2e_vex_vendor.rs", |
| 49 | + "get/get_batch_paths_e2e.rs", |
| 50 | + "get/global_packages_e2e.rs", |
| 51 | + "in_process_agent_reapply.rs", |
| 52 | + "in_process_alternate_installers.rs", |
| 53 | + "in_process_cargo_apply.rs", |
| 54 | + "in_process_edge_cases.rs", |
| 55 | + "in_process_gem_apply.rs", |
| 56 | + "in_process_gem_multi_platform.rs", |
| 57 | + "in_process_get_manifest_path.rs", |
| 58 | + "in_process_pypi_apply.rs", |
| 59 | + "in_process_pypi_multi_release.rs", |
| 60 | + "in_process_remote_ecosystems_apply.rs", |
| 61 | + "in_process_remove_repair_lifecycle.rs", |
| 62 | + "mode_migration_cargo.rs", |
| 63 | + "mode_migration_npm.rs", |
| 64 | + "remove/covgap_commands_remove.rs", |
| 65 | + "remove_rollback_api_overrides.rs", |
| 66 | + "repair/coverage_fix_repair_vendor_predelete.rs", |
| 67 | + "repair/covgap_commands_repair.rs", |
| 68 | + "repair/covgap_commands_repair_vendor.rs", |
| 69 | + "repair/repair_invariants.rs", |
| 70 | + "repair/repair_vendor_e2e.rs", |
| 71 | + "repair/repair_vendor_flavors_e2e.rs", |
| 72 | + "rollback/rollback_invariants.rs", |
| 73 | + "rollback/rollback_multicopy_blob_gate.rs", |
| 74 | + "scan/covgap_commands_fetch_stage.rs", |
| 75 | + "scan/covgap_commands_scan_vendor_flow.rs", |
| 76 | + "scan/scan_invariants.rs", |
| 77 | + "scan/scan_paths_e2e.rs", |
| 78 | + "scan/scan_sync_e2e.rs", |
| 79 | + "scan/scan_vendor_step_error_e2e.rs", |
| 80 | + "scan_rollout_e2e.rs", |
| 81 | + "scan_vendor_e2e.rs", |
| 82 | + "vendor_ecosystem_fixtures/mod.rs", |
| 83 | + "vendor_jvm_cli.rs", |
| 84 | + "vex_e2e_common/mod.rs", |
| 85 | + "vlt_e2e_common/mod.rs", |
| 86 | + "vlt_hosted_common/mod.rs", |
| 87 | + "vlt_vendor_common/mod.rs", |
| 88 | +]; |
| 89 | + |
| 90 | +/// Every `.rs` file under `tests/`, as (`/`-joined relative path, text). |
| 91 | +fn test_sources() -> Vec<(String, String)> { |
| 92 | + fn walk(dir: &Path, root: &Path, out: &mut Vec<(String, String)>) { |
| 93 | + let mut entries: Vec<_> = std::fs::read_dir(dir) |
| 94 | + .unwrap_or_else(|e| panic!("read {}: {e}", dir.display())) |
| 95 | + .map(|e| e.unwrap().path()) |
| 96 | + .collect(); |
| 97 | + entries.sort(); |
| 98 | + for path in entries { |
| 99 | + if path.is_dir() { |
| 100 | + walk(&path, root, out); |
| 101 | + } else if path.extension().is_some_and(|e| e == "rs") { |
| 102 | + let rel = path |
| 103 | + .strip_prefix(root) |
| 104 | + .unwrap() |
| 105 | + .components() |
| 106 | + .map(|c| c.as_os_str().to_string_lossy().into_owned()) |
| 107 | + .collect::<Vec<_>>() |
| 108 | + .join("/"); |
| 109 | + let text = std::fs::read_to_string(&path) |
| 110 | + .unwrap_or_else(|e| panic!("read {}: {e}", path.display())); |
| 111 | + out.push((rel, text)); |
| 112 | + } |
| 113 | + } |
| 114 | + } |
| 115 | + let root = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests"); |
| 116 | + let mut out = Vec::new(); |
| 117 | + walk(&root, &root, &mut out); |
| 118 | + out |
| 119 | +} |
| 120 | + |
| 121 | +/// Whether `text` defines a `binary()` or `git_sha256(..)` of its own. |
| 122 | +fn defines_private_helper(text: &str) -> bool { |
| 123 | + text.lines().any(|line| { |
| 124 | + let line = line.trim_start(); |
| 125 | + let line = line |
| 126 | + .strip_prefix("pub(crate) ") |
| 127 | + .or_else(|| line.strip_prefix("pub ")) |
| 128 | + .unwrap_or(line); |
| 129 | + line.starts_with("fn binary()") || line.starts_with("fn git_sha256(") |
| 130 | + }) |
| 131 | +} |
| 132 | + |
| 133 | +#[test] |
| 134 | +fn no_new_private_binary_or_git_sha256() { |
| 135 | + let unexpected: Vec<String> = test_sources() |
| 136 | + .into_iter() |
| 137 | + .filter(|(rel, text)| { |
| 138 | + !rel.starts_with("common/") |
| 139 | + && !PENDING_PRIVATE_HELPERS.contains(&rel.as_str()) |
| 140 | + && defines_private_helper(text) |
| 141 | + }) |
| 142 | + .map(|(rel, _)| rel) |
| 143 | + .collect(); |
| 144 | + assert!( |
| 145 | + unexpected.is_empty(), |
| 146 | + "these test files define their own binary() or git_sha256: {unexpected:?}. \ |
| 147 | + Use `common::binary` / `common::git_sha256` instead (declare \ |
| 148 | + `#[path = \"common/mod.rs\"] mod common;`, or `use crate::common::..` in a \ |
| 149 | + directory test binary). Do not add them to PENDING_PRIVATE_HELPERS." |
| 150 | + ); |
| 151 | +} |
| 152 | + |
| 153 | +#[test] |
| 154 | +fn the_detector_sees_every_former_copy_shape() { |
| 155 | + for copy in [ |
| 156 | + "fn binary() -> PathBuf {\n env!(\"CARGO_BIN_EXE_socket-patch\").into()\n}", |
| 157 | + "fn binary() -> &'static str {\n env!(\"CARGO_BIN_EXE_socket-patch\")\n}", |
| 158 | + " fn binary() -> PathBuf {", |
| 159 | + "pub fn git_sha256(bytes: &[u8]) -> String {", |
| 160 | + "fn git_sha256(content: &[u8]) -> String {", |
| 161 | + ] { |
| 162 | + assert!(defines_private_helper(copy), "missed: {copy}"); |
| 163 | + } |
| 164 | + for not_a_copy in [ |
| 165 | + "use common::{binary, git_sha256};", |
| 166 | + "let out = Command::new(binary()).output();", |
| 167 | + "pub fn git_sha256_file(path: &Path) -> String {", |
| 168 | + ] { |
| 169 | + assert!( |
| 170 | + !defines_private_helper(not_a_copy), |
| 171 | + "false positive: {not_a_copy}" |
| 172 | + ); |
| 173 | + } |
| 174 | +} |
0 commit comments