#![allow(
clippy::needless_raw_string_hashes,
clippy::uninlined_format_args,
clippy::too_many_lines,
clippy::missing_errors_doc,
clippy::missing_panics_doc,
clippy::doc_markdown,
clippy::items_after_statements
)]
use std::path::{Path, PathBuf};
use std::process::Command as StdCommand;
use assert_fs::TempDir;
use assert_fs::prelude::*;
mod common;
use common::*;
fn host_offline() -> bool {
std::env::var_os("CABIN_NET_OFFLINE").is_some()
}
fn network_reachable() -> bool {
use std::net::{TcpStream, ToSocketAddrs};
use std::time::Duration;
let Ok(mut addrs) = "github.com:443".to_socket_addrs() else {
return false;
};
let Some(addr) = addrs.next() else {
return false;
};
TcpStream::connect_timeout(&addr, Duration::from_secs(3)).is_ok()
}
fn examples_root() -> PathBuf {
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
manifest_dir
.parent()
.and_then(Path::parent)
.expect("workspace root should be two levels above crates/cabin")
.join("examples")
}
fn copy_example(name: &str) -> TempDir {
let dir = TempDir::new().expect("temp dir");
dir.copy_from(examples_root().join(name), &["**"])
.unwrap_or_else(|err| panic!("failed to copy example `{name}`: {err}"));
dir
}
fn run_artifact(path: &Path, label: &str) -> String {
let output = StdCommand::new(path)
.output()
.unwrap_or_else(|err| panic!("{label}: failed to spawn `{}`: {err}", path.display()));
assert!(
output.status.success(),
"{label}: `{}` exited with {:?}; stderr = {}",
path.display(),
output.status,
String::from_utf8_lossy(&output.stderr)
);
String::from_utf8(output.stdout)
.unwrap_or_else(|err| panic!("{label}: artifact stdout is not utf-8: {err}"))
}
#[test]
fn hello_c_builds_and_runs() {
if !c_and_cxx_build_tools_available() {
eprintln!("test skipped: requires ninja + C/C++ compilers");
return;
}
let dir = copy_example("hello-c");
cabin()
.args(["build", "--manifest-path"])
.arg(dir.path().join("cabin.toml"))
.arg("--build-dir")
.arg(dir.path().join("build"))
.assert()
.success();
let artifact = dir.path().join(format!(
"build/dev/packages/hello-c/hello-c{}",
std::env::consts::EXE_SUFFIX
));
let stdout = run_artifact(&artifact, "hello-c");
assert!(
stdout.contains("Hello from Cabin (C)"),
"hello-c artifact: stdout = {stdout}"
);
}
#[test]
fn hello_cpp_builds_and_runs() {
if !build_tools_available() {
eprintln!("test skipped: requires ninja + a C++ compiler");
return;
}
let dir = copy_example("hello-cpp");
cabin()
.args(["build", "--manifest-path"])
.arg(dir.path().join("cabin.toml"))
.arg("--build-dir")
.arg(dir.path().join("build"))
.assert()
.success();
let output = cabin()
.args(["run", "--manifest-path"])
.arg(dir.path().join("cabin.toml"))
.arg("--build-dir")
.arg(dir.path().join("build"))
.assert()
.success()
.get_output()
.clone();
let stdout = String::from_utf8(output.stdout).expect("stdout is utf-8");
assert!(
stdout.contains("Hello from Cabin (C++)"),
"hello-cpp run: stdout = {stdout}"
);
}
#[test]
fn library_and_app_builds_and_runs() {
if !build_tools_available() {
eprintln!("test skipped: requires ninja + a C++ compiler");
return;
}
let dir = copy_example("library-and-app");
cabin()
.args(["build", "--manifest-path"])
.arg(dir.path().join("cabin.toml"))
.arg("--build-dir")
.arg(dir.path().join("build"))
.assert()
.success();
let output = cabin()
.args(["run", "--manifest-path"])
.arg(dir.path().join("cabin.toml"))
.arg("--build-dir")
.arg(dir.path().join("build"))
.assert()
.success()
.get_output()
.clone();
let stdout = String::from_utf8(output.stdout).expect("stdout is utf-8");
assert!(
stdout.contains("Hello, Cabin!"),
"library-and-app run: stdout = {stdout}"
);
}
#[test]
fn workspace_basic_builds_workspace() {
if !build_tools_available() {
eprintln!("test skipped: requires ninja + a C++ compiler");
return;
}
let dir = copy_example("workspace-basic");
cabin()
.args(["build", "--workspace", "--manifest-path"])
.arg(dir.path().join("cabin.toml"))
.arg("--build-dir")
.arg(dir.path().join("build"))
.assert()
.success();
}
#[test]
fn workspace_basic_builds_single_package() {
if !build_tools_available() {
eprintln!("test skipped: requires ninja + a C++ compiler");
return;
}
let dir = copy_example("workspace-basic");
cabin()
.args(["build", "-p", "cli", "--manifest-path"])
.arg(dir.path().join("cabin.toml"))
.arg("--build-dir")
.arg(dir.path().join("build"))
.assert()
.success();
}
#[test]
fn workspace_basic_runs_selected_package() {
if !build_tools_available() {
eprintln!("test skipped: requires ninja + a C++ compiler");
return;
}
let dir = copy_example("workspace-basic");
let output = cabin()
.args(["run", "-p", "cli", "--manifest-path"])
.arg(dir.path().join("cabin.toml"))
.arg("--build-dir")
.arg(dir.path().join("build"))
.assert()
.success()
.get_output()
.clone();
let stdout = String::from_utf8(output.stdout).expect("stdout is utf-8");
assert!(
stdout.contains("doubled(21) = 42"),
"workspace-basic run -p cli: stdout = {stdout}"
);
}
#[test]
fn zlib_usage_builds_and_runs() {
if !c_and_cxx_build_tools_available() {
eprintln!("test skipped: requires ninja + C/C++ compilers");
return;
}
if host_offline() {
eprintln!(
"test skipped: CABIN_NET_OFFLINE is set; zlib-usage needs to fetch the port archive"
);
return;
}
if !network_reachable() {
eprintln!(
"test skipped: cannot reach github.com:443 to fetch the zlib port archive (set CABIN_NET_OFFLINE=1 to silence the probe)"
);
return;
}
let dir = copy_example("zlib-usage");
cabin()
.args(["build", "--manifest-path"])
.arg(dir.path().join("cabin.toml"))
.arg("--build-dir")
.arg(dir.path().join("build"))
.assert()
.success();
let output = cabin()
.args(["run", "--manifest-path"])
.arg(dir.path().join("cabin.toml"))
.arg("--build-dir")
.arg(dir.path().join("build"))
.assert()
.success()
.get_output()
.clone();
let stdout = String::from_utf8(output.stdout).expect("stdout is utf-8");
assert!(
stdout.contains("zlib version: 1.3"),
"zlib-usage run: stdout = {stdout}"
);
}