#![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 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() {
require_c_and_cxx_build_tools();
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() {
require_cxx_build_tools();
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 platform_cfg_builds_and_runs() {
require_cxx_build_tools();
let dir = copy_example("platform-cfg");
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");
let expected = if cfg!(windows) {
"Hello from Cabin on Windows"
} else {
"Hello from Cabin on Unix"
};
assert!(
stdout.contains(expected),
"platform-cfg run: stdout = {stdout}, expected to contain {expected:?}"
);
}
#[test]
fn library_and_app_builds_and_runs() {
require_cxx_build_tools();
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() {
require_cxx_build_tools();
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() {
require_cxx_build_tools();
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() {
require_cxx_build_tools();
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]
#[ignore = "requires external network"]
fn zlib_usage_builds_and_runs() {
require_c_and_cxx_build_tools();
let dir = copy_example("zlib-usage");
run_port_build_then_run(&PortBuildRun {
label: "zlib-usage",
manifest: dir.path().join("cabin.toml"),
build_dir: dir.path().join("build"),
cache_dir: dir.path().join("cache"),
expected_stdout: &["zlib version: 1.3"],
});
}
#[test]
#[ignore = "requires external network"]
fn cjson_usage_builds_and_runs() {
require_c_and_cxx_build_tools();
let dir = copy_example("cjson-usage");
run_port_build_then_run(&PortBuildRun {
label: "cjson-usage",
manifest: dir.path().join("cabin.toml"),
build_dir: dir.path().join("build"),
cache_dir: dir.path().join("cache"),
expected_stdout: &["cJSON parsed name: Cabin", "cJSON version: 1.7"],
});
}
#[test]
#[ignore = "requires external network"]
fn xxhash_usage_builds_and_runs() {
require_c_and_cxx_build_tools();
let dir = copy_example("xxhash-usage");
run_port_build_then_run(&PortBuildRun {
label: "xxhash-usage",
manifest: dir.path().join("cabin.toml"),
build_dir: dir.path().join("build"),
cache_dir: dir.path().join("cache"),
expected_stdout: &["xxHash version: 803", "XXH64(\"Cabin\") = 002d85a6f376e171"],
});
}
#[test]
#[ignore = "requires external network"]
fn tinyxml2_usage_builds_and_runs() {
require_cxx_build_tools();
let dir = copy_example("tinyxml2-usage");
run_port_build_then_run(&PortBuildRun {
label: "tinyxml2-usage",
manifest: dir.path().join("cabin.toml"),
build_dir: dir.path().join("build"),
cache_dir: dir.path().join("cache"),
expected_stdout: &["tinyxml2 parsed to: Cabin", "tinyxml2 version: 11.0.0"],
});
}
#[test]
#[ignore = "requires external network"]
fn sqlite3_usage_builds_and_runs() {
require_c_and_cxx_build_tools();
let dir = copy_example("sqlite3-usage");
run_port_build_then_run(&PortBuildRun {
label: "sqlite3-usage",
manifest: dir.path().join("cabin.toml"),
build_dir: dir.path().join("build"),
cache_dir: dir.path().join("cache"),
expected_stdout: &[
"sqlite version: 3.53",
"sqlite threadsafe: 1",
"sqlite query result: 42",
],
});
}
#[test]
#[ignore = "requires external network"]
fn sqlite3_single_threaded_feature_disables_threadsafety() {
require_c_and_cxx_build_tools();
let dir = copy_example("sqlite3-usage");
dir.child("cabin.toml")
.write_str(
r#"[package]
name = "sqlite3-usage"
version = "0.1.0"
[dependencies]
sqlite3 = { port = true, version = "^3", features = ["single-threaded"] }
[target.sqlite3-usage]
type = "executable"
sources = ["src/main.c"]
deps = ["sqlite3"]
"#,
)
.unwrap();
run_port_build_then_run(&PortBuildRun {
label: "sqlite3-usage single-threaded",
manifest: dir.path().join("cabin.toml"),
build_dir: dir.path().join("build"),
cache_dir: dir.path().join("cache"),
expected_stdout: &["sqlite threadsafe: 0"],
});
}
#[test]
#[ignore = "requires external network"]
fn libpng_usage_cache_lifecycle_builds_and_runs() {
require_c_and_cxx_build_tools();
let dir = copy_example("libpng-usage");
let manifest = dir.path().join("cabin.toml");
let warm_cache = dir.path().join("cache");
let frozen_cache = dir.path().join("cache-frozen");
run_port_cache_lifecycle(&PortCacheLifecycle {
label: "libpng-usage",
manifest,
build_root: dir.path().join("build"),
warm_cache,
pristine_cache: frozen_cache,
expected_stdout: &[
"libpng version: 1.6.50",
"zlib version (via libpng port edge): 1.3",
],
expected_downloads: &["libpng", "zlib"],
frozen_port: "libpng",
});
}
#[test]
fn library_with_tests_runs_tests() {
require_cxx_build_tools();
let dir = copy_example("library-with-tests");
let output = cabin()
.args(["test", "--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");
for expected in [
"test library-with-tests:calc_test ... ok",
"test library-with-tests:parity_test ... ok",
"test result: ok. 2 passed; 0 failed (of 2)",
] {
assert!(
stdout.contains(expected),
"library-with-tests test: missing `{expected}`; stdout = {stdout}"
);
}
}