Skip to main content

canic_testkit/artifacts/
wasm.rs

1use std::{
2    fs,
3    path::{Path, PathBuf},
4    process::Command,
5};
6
7pub const INTERNAL_TEST_ENDPOINTS_ENV: (&str, &str) = ("CANIC_INTERNAL_TEST_ENDPOINTS", "1");
8
9///
10/// WasmBuildProfile
11///
12
13#[derive(Clone, Copy, Debug, Eq, PartialEq)]
14pub enum WasmBuildProfile {
15    Debug,
16    Fast,
17    Release,
18}
19
20impl WasmBuildProfile {
21    /// Return the Cargo profile arguments for this build profile.
22    #[must_use]
23    pub const fn cargo_args(self) -> &'static [&'static str] {
24        match self {
25            Self::Debug => &[],
26            Self::Fast => &["--profile", "fast"],
27            Self::Release => &["--release"],
28        }
29    }
30
31    /// Return the target-directory component for this build profile.
32    #[must_use]
33    pub const fn target_dir_name(self) -> &'static str {
34        match self {
35            Self::Debug => "debug",
36            Self::Fast => "fast",
37            Self::Release => "release",
38        }
39    }
40
41    /// Return the explicit Canic wasm-profile selector for local builders.
42    #[must_use]
43    pub const fn canic_wasm_profile_value(self) -> &'static str {
44        match self {
45            Self::Debug => "debug",
46            Self::Fast => "fast",
47            Self::Release => "release",
48        }
49    }
50}
51
52/// Resolve the wasm artifact path for one crate under a target directory.
53#[must_use]
54pub fn wasm_path(target_dir: &Path, crate_name: &str, profile: WasmBuildProfile) -> PathBuf {
55    target_dir
56        .join("wasm32-unknown-unknown")
57        .join(profile.target_dir_name())
58        .join(format!("{crate_name}.wasm"))
59}
60
61/// Check whether all requested wasm artifacts already exist.
62#[must_use]
63pub fn wasm_artifacts_ready(
64    target_dir: &Path,
65    canisters: &[&str],
66    profile: WasmBuildProfile,
67) -> bool {
68    canisters
69        .iter()
70        .all(|name| wasm_path(target_dir, name, profile).is_file())
71}
72
73/// Read a compiled wasm artifact for one crate.
74#[must_use]
75pub fn read_wasm(target_dir: &Path, crate_name: &str, profile: WasmBuildProfile) -> Vec<u8> {
76    let path = wasm_path(target_dir, crate_name, profile);
77    fs::read(&path).unwrap_or_else(|err| panic!("failed to read {crate_name} wasm: {err}"))
78}
79
80/// Build one or more wasm canisters into the provided target directory.
81pub fn build_wasm_canisters(
82    workspace_root: &Path,
83    target_dir: &Path,
84    packages: &[&str],
85    profile: WasmBuildProfile,
86    extra_env: &[(&str, &str)],
87) {
88    let mut cmd = Command::new("cargo");
89    cmd.current_dir(workspace_root);
90    cmd.env("CARGO_TARGET_DIR", target_dir);
91    cmd.env("DFX_NETWORK", "local");
92    cmd.args(["build", "--target", "wasm32-unknown-unknown"]);
93    cmd.args(profile.cargo_args());
94
95    for (key, value) in extra_env {
96        cmd.env(key, value);
97    }
98
99    for name in packages {
100        cmd.args(["-p", name]);
101    }
102
103    let output = cmd.output().expect("failed to run cargo build");
104    assert!(
105        output.status.success(),
106        "cargo build failed: {}",
107        String::from_utf8_lossy(&output.stderr)
108    );
109}
110
111/// Build one or more wasm canisters with the internal test endpoint surface enabled.
112pub fn build_internal_test_wasm_canisters(
113    workspace_root: &Path,
114    target_dir: &Path,
115    packages: &[&str],
116    profile: WasmBuildProfile,
117) {
118    build_wasm_canisters(
119        workspace_root,
120        target_dir,
121        packages,
122        profile,
123        &[INTERNAL_TEST_ENDPOINTS_ENV],
124    );
125}
126
127/// Build one or more wasm canisters with the internal test endpoint surface
128/// enabled plus any additional caller-provided environment overrides.
129pub fn build_internal_test_wasm_canisters_with_env<'a>(
130    workspace_root: &Path,
131    target_dir: &Path,
132    packages: &[&str],
133    profile: WasmBuildProfile,
134    extra_env: &[(&'a str, &'a str)],
135) {
136    let mut build_env = Vec::with_capacity(extra_env.len() + 1);
137    build_env.push(INTERNAL_TEST_ENDPOINTS_ENV);
138    build_env.extend_from_slice(extra_env);
139    build_wasm_canisters(workspace_root, target_dir, packages, profile, &build_env);
140}