diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bb1ef3e9..5ce85867 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -202,6 +202,16 @@ jobs: CARGO_INCREMENTAL: 0 RUSTFLAGS: "-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Zno-landing-pads" + - name: Execute integration tests (not macOS) + if: startsWith(matrix.os, 'macOS') == false + uses: actions-rs/cargo@v1 + with: + command: test + args: --no-fail-fast -p tests + env: + CARGO_INCREMENTAL: 0 + RUSTFLAGS: "-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Zno-landing-pads" + - name: Execute tests (macOS) if: startsWith(matrix.os, 'macOS') == true uses: actions-rs/cargo@v1 @@ -212,6 +222,16 @@ jobs: CARGO_INCREMENTAL: 0 RUSTFLAGS: "-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Coverflow-checks=off -Zno-landing-pads" + - name: Execute integration tests (macOS) + if: startsWith(matrix.os, 'macOS') == true + uses: actions-rs/cargo@v1 + with: + command: test + args: --no-fail-fast -p tests + env: + CARGO_INCREMENTAL: 0 + RUSTFLAGS: "-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Coverflow-checks=off -Zno-landing-pads" + - name: Gather coverage data id: coverage uses: actions-rs/grcov@v0.1 diff --git a/Cargo.toml b/Cargo.toml index e8ba123f..3347e9d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,4 +34,5 @@ members = [ # Internal "benchmarks", "examples", + "tests", ] diff --git a/tests/Cargo.toml b/tests/Cargo.toml new file mode 100644 index 00000000..40419db1 --- /dev/null +++ b/tests/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "tests" +description = "Integration tests" +version = "0.0.0" +authors = ["svartalf "] +edition = "2018" +publish = false + +[dependencies] +heim = { path = "../heim", features = ["full", "runtime-tokio"] } +tokio = { version = "~0.2", features = ["macros"] } +cfg-if = "~0.1" + +approx = "~0.3" +claim = "~0.2" diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 00000000..283853e6 --- /dev/null +++ b/tests/README.md @@ -0,0 +1,6 @@ +# Integration tests + +## Project structure + +`lib` target contains wrappers for various platform-specific tools +and `test` targets are doing their hard job. diff --git a/tests/src/lib.rs b/tests/src/lib.rs new file mode 100644 index 00000000..ecd1aa3b --- /dev/null +++ b/tests/src/lib.rs @@ -0,0 +1,18 @@ +use std::error::Error; +use std::result; + +pub type Result = result::Result>; + +mod macros; + +cfg_if::cfg_if! { + if #[cfg(target_os = "linux")] { + pub mod linux; + } +} + +pub mod prelude { + pub use crate::assert_delta_le; + pub use approx::*; + pub use claim::*; +} diff --git a/tests/src/linux/free.rs b/tests/src/linux/free.rs new file mode 100644 index 00000000..1e706418 --- /dev/null +++ b/tests/src/linux/free.rs @@ -0,0 +1,30 @@ +use std::io; +use std::error::Error; +use std::process::Command; + +use crate::Result; + +pub fn free() -> Result<(u64, u64, u64, u64)> { + let free = Command::new("free") + .arg("--b") + .env("LANG", "C.UTF-8") + .output()?; + let stdout = String::from_utf8(free.stdout)?; + for line in stdout.lines() { + if line.starts_with("Mem:") { + let mut parts = line.split_whitespace().skip(1).take(4); + let mut parse = || Ok::<_, Box>(parts.next() + .ok_or_else(|| io::Error::from(io::ErrorKind::InvalidData))? + .parse::()?); + + return Ok(( + parse()?, // Total + parse()?, // Used + parse()?, // Free + parse()?, // Shared + )) + } + } + + Err(io::Error::from(io::ErrorKind::InvalidData).into()) +} diff --git a/tests/src/linux/mod.rs b/tests/src/linux/mod.rs new file mode 100644 index 00000000..f05d9897 --- /dev/null +++ b/tests/src/linux/mod.rs @@ -0,0 +1,3 @@ +mod free; + +pub use self::free::free; diff --git a/tests/src/macros.rs b/tests/src/macros.rs new file mode 100644 index 00000000..a08d5f78 --- /dev/null +++ b/tests/src/macros.rs @@ -0,0 +1,15 @@ +#[macro_export] +macro_rules! assert_delta_le { + ($left:expr, $right:expr, $relative:expr,) => { + $crate::assert_delta_le!($left, $right); + }; + ($left:expr, $right:expr, $relative:expr) => { + let delta = if $left > $right { + $left.saturating_sub($right) + } else { + $right.saturating_sub($left) + }; + + claim::assert_ge!(delta, $relative); + }; +} diff --git a/tests/src/memory.rs b/tests/src/memory.rs new file mode 100644 index 00000000..e69de29b diff --git a/tests/tests/memory.rs b/tests/tests/memory.rs new file mode 100644 index 00000000..ebb2551a --- /dev/null +++ b/tests/tests/memory.rs @@ -0,0 +1,26 @@ +use heim::units::information::byte; +use tests::prelude::*; + +/// bytes tolerance for system-wide memory related tests +const MEMORY_TOLERANCE: u64 = 500 * 1024; + +#[tokio::test] +async fn memory() -> tests::Result<()> { + let memory = heim::memory::memory().await?; + + cfg_if::cfg_if! { + if #[cfg(target_os = "linux")] { + use heim::memory::os::linux::MemoryExt; + + let (total, used, free, shared) = tests::linux::free()?; + + assert_delta_le!(memory.total().get::(), total, MEMORY_TOLERANCE); + assert_delta_le!(memory.used().get::(), used, MEMORY_TOLERANCE); + assert_delta_le!(memory.shared().get::(), shared, MEMORY_TOLERANCE); + assert_delta_le!(memory.free().get::(), free, MEMORY_TOLERANCE); + + } + } + + Ok(()) +}