feat: Add Docker support to integration tests

This allows the tests to be run inside of a Docker container with linux
with X11 inside.

Signed-off-by: John Nunley <dev@notgull.net>
This commit is contained in:
John Nunley
2024-03-10 21:04:54 -07:00
parent facef799d3
commit 68acedcdda
16 changed files with 549 additions and 17 deletions

View File

@@ -14,4 +14,4 @@ async-process = "2.1.0"
inventory = "0.3.15"
owo-colors = "4.0.0"
serde = { workspace = true, features = ["derive"] }
serde_json = "1.0.114"
serde_json.workspace = true

View File

@@ -10,10 +10,11 @@ use std::env;
use std::ffi::{OsStr, OsString};
use std::mem;
use std::num::NonZeroUsize;
use std::panic;
const GUI_TEST_CURRENT_TEST_NAME: &str = "GUI_TEST_CURRENT_TEST_NAME";
const GUI_TEST_SUBPROCESS_LIMIT: &str = "GUI_TEST_SUBPROCESS_LIMIT";
const DEFAULT_LIMIT: usize = 4;
const DEFAULT_LIMIT: usize = 1;
#[doc(hidden)]
pub use inventory as __inventory;
@@ -23,7 +24,7 @@ pub use inventory as __inventory;
macro_rules! main {
($handler:expr) => {
fn main() {
$crate::__entry($handler)
$crate::__entry(|| $handler)
}
};
}
@@ -136,8 +137,22 @@ impl Harness {
/// Run a closure as a test.
pub fn with_test<T>(&mut self, name: impl Into<String>, f: impl FnOnce() -> T) -> T {
let _test = self.test(name.into());
f()
let test = self.test(name.into());
match panic::catch_unwind(panic::AssertUnwindSafe(f)) {
Ok(x) => x,
Err(err) => {
if let Some(panic) = err.downcast_ref::<&'static str>() {
test.fail(panic.to_string());
} else if let Some(panic) = err.downcast_ref::<String>() {
test.fail(panic.clone());
} else {
test.fail("unintelligible error".to_string());
}
panic::resume_unwind(err)
}
}
}
/// Begin a test group.
@@ -238,6 +253,14 @@ impl Testing<'_> {
// Send the "skipped" event.
self.harness.take().unwrap().end_test(TestResult::Skipped);
}
/// Fail this test.
fn fail(mut self, panic: String) {
self.harness
.take()
.unwrap()
.end_test(TestResult::Failed(panic));
}
}
impl Drop for Testing<'_> {
@@ -351,7 +374,7 @@ pub enum TestResult {
/// Entry point of the test.
#[doc(hidden)]
pub fn __entry<H: TestHandler + Send + 'static>(handler: H) {
pub fn __entry<H: TestHandler + Send + 'static>(handler: impl FnOnce() -> H) {
// Look for the test name environment variable.
if let Some(test_name) = env::var(GUI_TEST_CURRENT_TEST_NAME)
.ok()
@@ -364,10 +387,13 @@ pub fn __entry<H: TestHandler + Send + 'static>(handler: H) {
.unwrap_or_else(|| panic!("unable to find test '{test_name}'"));
// Create a harness.
let mut harness = Harness::new(test_to_run.name, handler);
let mut harness = Harness::new(test_to_run.name, handler());
// Run the test.
(test_to_run.func)(&mut harness);
panic::catch_unwind(panic::AssertUnwindSafe(move || {
(test_to_run.func)(&mut harness)
}))
.ok();
} else {
// Run a subprocess for every test.
let limit = env::var(GUI_TEST_SUBPROCESS_LIMIT)

View File

@@ -8,7 +8,8 @@ use crate::{TestEvent, TestHandler};
use std::io::Write;
/// A wrapper around a writer that sends data down a stream.
pub struct WriteHandler<W> {
#[derive(Debug)]
pub struct WriteHandler<W: Write> {
/// The inner writer.
writer: W,
}
@@ -30,3 +31,9 @@ impl<W: Write> TestHandler for WriteHandler<W> {
self.writer.write_all(&payload).unwrap();
}
}
impl<W: Write> Drop for WriteHandler<W> {
fn drop(&mut self) {
self.writer.flush().ok();
}
}

View File

@@ -70,6 +70,7 @@ impl UserHandler {
// If this is the end, dump other events.
while ender {
ender = false;
assert!(self.current_start.take().is_some());
// Pick one set.
@@ -78,11 +79,12 @@ impl UserHandler {
self.current_start = Some(test_name);
// Dump events and look for a conclusion.
ender = false;
self.dump_events(entries.into_iter().inspect(|ty| {
ender |= matches!(ty, TestEventType::Complete { .. });
}));
}
println!();
}
}
}
@@ -160,5 +162,19 @@ impl TestHandler for UserHandler {
impl Drop for UserHandler {
fn drop(&mut self) {
assert!(self.cache.is_empty());
// Write the final bit to the stdout.
let mut stdout = io::stdout().lock();
if !self.failures.is_empty() {
writeln!(stdout, "Test Failures:").ok();
for (test_name, panic) in &self.failures {
writeln!(stdout, " {}", test_name).ok();
writeln!(stdout, "-------------").ok();
writeln!(stdout, "{}", panic).ok();
writeln!(stdout, "-------------").ok();
}
}
}
}