mirror of
https://github.com/rust-windowing/winit.git
synced 2026-08-30 13:20:06 -04:00
@@ -275,5 +275,7 @@ license = "Apache-2.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
serde = { version = "1", features = ["serde_derive"] }
|
async-io = "2.3.1"
|
||||||
|
gui-test = { path = "it/gui-test" }
|
||||||
mint = "0.5.6"
|
mint = "0.5.6"
|
||||||
|
serde = { version = "1", features = ["serde_derive"] }
|
||||||
|
|||||||
@@ -8,8 +8,10 @@ edition.workspace = true
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
async-executor = "1.8.0"
|
async-executor = "1.8.0"
|
||||||
async-io = "2.3.1"
|
async-io.workspace = true
|
||||||
async-lock = "3.3.0"
|
async-lock = "3.3.0"
|
||||||
async-process = "2.1.0"
|
async-process = "2.1.0"
|
||||||
inventory = "0.3.15"
|
inventory = "0.3.15"
|
||||||
|
owo-colors = "4.0.0"
|
||||||
serde = { workspace = true, features = ["derive"] }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
|
serde_json = "1.0.114"
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
//! A testing framework that can be run remotely.
|
//! A testing framework that can be run remotely.
|
||||||
|
|
||||||
|
pub mod stream;
|
||||||
|
pub mod remote;
|
||||||
|
pub mod user;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
@@ -17,7 +21,7 @@ pub use inventory as __inventory;
|
|||||||
/// Replacement for the `main` function.
|
/// Replacement for the `main` function.
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! main {
|
macro_rules! main {
|
||||||
($harness:expr) => {
|
($handler:expr) => {
|
||||||
fn main() {
|
fn main() {
|
||||||
$crate::__entry($harness)
|
$crate::__entry($harness)
|
||||||
}
|
}
|
||||||
@@ -122,10 +126,7 @@ impl Harness {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Send the "test started" event to the handler.
|
// Send the "test started" event to the handler.
|
||||||
self.handler.handle_test(TestEvent {
|
self.send_event(TestEventType::TestStarted { name: name.into() });
|
||||||
runner: self.name.clone(),
|
|
||||||
ty: TestEventType::TestStarted { name: name.into() },
|
|
||||||
});
|
|
||||||
|
|
||||||
// Return the handle.
|
// Return the handle.
|
||||||
Testing {
|
Testing {
|
||||||
@@ -156,10 +157,7 @@ impl Harness {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Send the "group started" event to the handler.
|
// Send the "group started" event to the handler.
|
||||||
self.handler.handle_test(TestEvent {
|
self.send_event(TestEventType::GroupStarted { name: name.into() });
|
||||||
ty: TestEventType::GroupStarted { name: name.into() },
|
|
||||||
runner: self.name.clone(),
|
|
||||||
});
|
|
||||||
|
|
||||||
// Return the handle.
|
// Return the handle.
|
||||||
Grouping { harness: self }
|
Grouping { harness: self }
|
||||||
@@ -180,10 +178,7 @@ impl Harness {
|
|||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.handler.handle_test(TestEvent {
|
self.send_event(TestEventType::TestEnded { result: reason });
|
||||||
runner: self.name.clone(),
|
|
||||||
ty: TestEventType::TestEnded { result: reason },
|
|
||||||
});
|
|
||||||
|
|
||||||
let count = match mem::replace(&mut self.state, State::Default) {
|
let count = match mem::replace(&mut self.state, State::Default) {
|
||||||
State::InTest { past_groups } => past_groups,
|
State::InTest { past_groups } => past_groups,
|
||||||
@@ -198,10 +193,7 @@ impl Harness {
|
|||||||
|
|
||||||
/// End the current group.
|
/// End the current group.
|
||||||
fn end_group(&mut self) {
|
fn end_group(&mut self) {
|
||||||
self.handler.handle_test(TestEvent {
|
self.send_event(TestEventType::GroupEnded);
|
||||||
runner: self.name.clone(),
|
|
||||||
ty: TestEventType::GroupEnded,
|
|
||||||
});
|
|
||||||
|
|
||||||
let count = match mem::replace(&mut self.state, State::Default) {
|
let count = match mem::replace(&mut self.state, State::Default) {
|
||||||
State::InGroups(groups) => groups,
|
State::InGroups(groups) => groups,
|
||||||
@@ -213,6 +205,26 @@ impl Harness {
|
|||||||
Some(groups) => State::InGroups(groups),
|
Some(groups) => State::InGroups(groups),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Send a test event of the provided type.
|
||||||
|
fn send_event(&mut self, ty: TestEventType) {
|
||||||
|
let event = TestEvent {
|
||||||
|
runner: self.name.clone(),
|
||||||
|
ty,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.handler.handle_test(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Harness {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.send_event(TestEventType::Complete {
|
||||||
|
total: self.test_count,
|
||||||
|
fail: self.test_fails,
|
||||||
|
pass: self.test_passed,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An in-progress test.
|
/// An in-progress test.
|
||||||
@@ -279,14 +291,13 @@ pub trait TestHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// An event produced by the test harness.
|
/// An event produced by the test harness.
|
||||||
#[non_exhaustive]
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct TestEvent {
|
pub struct TestEvent {
|
||||||
/// The name of the runner associated with the event.
|
/// The name of the runner associated with the event.
|
||||||
runner: String,
|
pub runner: String,
|
||||||
|
|
||||||
/// The type of the event.
|
/// The type of the event.
|
||||||
ty: TestEventType,
|
pub ty: TestEventType,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The type of the event.
|
/// The type of the event.
|
||||||
@@ -294,7 +305,16 @@ pub struct TestEvent {
|
|||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub enum TestEventType {
|
pub enum TestEventType {
|
||||||
/// The tests are complete and the harness can be disconnected.
|
/// The tests are complete and the harness can be disconnected.
|
||||||
Complete,
|
Complete {
|
||||||
|
/// Total number of tests.
|
||||||
|
total: usize,
|
||||||
|
|
||||||
|
/// Total number of passing tests.
|
||||||
|
pass: usize,
|
||||||
|
|
||||||
|
/// Total number of failed tests.
|
||||||
|
fail: usize,
|
||||||
|
},
|
||||||
|
|
||||||
/// A test has started.
|
/// A test has started.
|
||||||
TestStarted { name: String },
|
TestStarted { name: String },
|
||||||
|
|||||||
30
it/gui-test/src/remote.rs
Normal file
30
it/gui-test/src/remote.rs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
//! Create a test handler that can be run remotely.
|
||||||
|
|
||||||
|
use crate::TestHandler;
|
||||||
|
use crate::stream::WriteHandler;
|
||||||
|
use crate::user::UserHandler;
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
use std::net::TcpStream;
|
||||||
|
|
||||||
|
/// Create a test handler adjusted for the current environment.
|
||||||
|
pub fn handler() -> Box<dyn TestHandler + Send + 'static> {
|
||||||
|
// If GUI_TEST_UNIX_STREAM is enabled, use that as a Unix stream.
|
||||||
|
#[cfg(unix)]
|
||||||
|
if let Some(stream_path) = env::var_os("GUI_TEST_UNIX_STREAM")
|
||||||
|
.filter(|s| !s.is_empty()) {
|
||||||
|
let stream = std::os::unix::net::UnixStream::connect(stream_path).expect("unable to connect to gui-test handler");
|
||||||
|
return Box::new(WriteHandler::new(stream));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If GUI_TEST_TCP_STREAM is enabled, use that as a TCP stream.
|
||||||
|
if let Some(tcp_ip) = env::var("GUI_TEST_TCP_STREAM")
|
||||||
|
.ok()
|
||||||
|
.filter(|s| !s.is_empty()) {
|
||||||
|
let stream = TcpStream::connect(tcp_ip).unwrap();
|
||||||
|
return Box::new(WriteHandler::new(stream));
|
||||||
|
}
|
||||||
|
|
||||||
|
// By default, use the user handler.
|
||||||
|
Box::new(UserHandler::new())
|
||||||
|
}
|
||||||
34
it/gui-test/src/stream.rs
Normal file
34
it/gui-test/src/stream.rs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
//! Write events to an output stream.
|
||||||
|
//!
|
||||||
|
//! The format is as follows:
|
||||||
|
//! - First 8 bytes: big-endian length of payload.
|
||||||
|
//! - Next {len} bytes: JSON payload to deserialize from.
|
||||||
|
|
||||||
|
use crate::{TestEvent, TestHandler};
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
/// A wrapper around a writer that sends data down a stream.
|
||||||
|
pub struct WriteHandler<W> {
|
||||||
|
/// The inner writer.
|
||||||
|
writer: W,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W: Write> WriteHandler<W> {
|
||||||
|
/// Create a new write handler.
|
||||||
|
pub fn new(writer: W) -> Self {
|
||||||
|
Self {
|
||||||
|
writer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W: Write> TestHandler for WriteHandler<W> {
|
||||||
|
fn handle_test(&mut self, event: TestEvent) {
|
||||||
|
let payload = serde_json::to_vec(&event).unwrap();
|
||||||
|
let length = u64::to_be_bytes(payload.len() as u64);
|
||||||
|
|
||||||
|
// Write the payload to the stream.
|
||||||
|
self.writer.write_all(&length).unwrap();
|
||||||
|
self.writer.write_all(&payload).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
164
it/gui-test/src/user.rs
Normal file
164
it/gui-test/src/user.rs
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
//! User-facing reporter.
|
||||||
|
|
||||||
|
use crate::{TestEvent, TestEventType, TestHandler, TestResult};
|
||||||
|
use owo_colors::OwoColorize;
|
||||||
|
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::io::{self, prelude::*};
|
||||||
|
|
||||||
|
const TABSIZE: usize = 2;
|
||||||
|
|
||||||
|
/// User-facing reporter.
|
||||||
|
///
|
||||||
|
/// This reporter dumps events to the console in a user-readable format.
|
||||||
|
pub struct UserHandler {
|
||||||
|
/// Current indent.
|
||||||
|
indent: usize,
|
||||||
|
|
||||||
|
/// The test set we're currently displaying.
|
||||||
|
current_start: Option<String>,
|
||||||
|
|
||||||
|
/// Test name we are running, if any.
|
||||||
|
test_name: Option<String>,
|
||||||
|
|
||||||
|
/// Cached events.
|
||||||
|
cache: BTreeMap<String, Vec<TestEventType>>,
|
||||||
|
|
||||||
|
/// Failures we had.
|
||||||
|
failures: Vec<(String, String)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UserHandler {
|
||||||
|
/// Create a new handler.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
indent: 0,
|
||||||
|
current_start: None,
|
||||||
|
test_name: None,
|
||||||
|
cache: BTreeMap::new(),
|
||||||
|
failures: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Process the provided events.
|
||||||
|
fn process_events(&mut self, events: impl IntoIterator<Item = TestEvent>) {
|
||||||
|
for event in events {
|
||||||
|
// Tell if this is an end event.
|
||||||
|
let mut ender = matches!(event.ty, TestEventType::Complete { .. });
|
||||||
|
|
||||||
|
// If there is no test name set, run the current one.
|
||||||
|
match self.current_start.as_ref() {
|
||||||
|
None => {
|
||||||
|
let TestEvent { runner, ty } = event;
|
||||||
|
self.current_start = Some(runner);
|
||||||
|
self.dump_events(Some(ty));
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(test_name) => {
|
||||||
|
// If there is a test name set and it's ours, post it immediately.
|
||||||
|
if test_name == &event.runner {
|
||||||
|
self.dump_events(Some(event.ty));
|
||||||
|
} else {
|
||||||
|
// Add it to the back of another one of the events.
|
||||||
|
self.cache
|
||||||
|
.entry(test_name.clone())
|
||||||
|
.or_default()
|
||||||
|
.push(event.ty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is the end, dump other events.
|
||||||
|
while ender {
|
||||||
|
assert!(self.current_start.take().is_some());
|
||||||
|
|
||||||
|
// Pick one set.
|
||||||
|
if let Some(entry) = self.cache.first_entry() {
|
||||||
|
let (test_name, entries) = entry.remove_entry();
|
||||||
|
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 { .. });
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Dump the provided events to the console.
|
||||||
|
fn dump_events(&mut self, events: impl IntoIterator<Item = TestEventType>) {
|
||||||
|
let mut stdout = io::stdout().lock();
|
||||||
|
|
||||||
|
for event in events {
|
||||||
|
// Write the indent.
|
||||||
|
for _ in 0..(self.indent * TABSIZE) {
|
||||||
|
stdout.write_all(b" ").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
match event {
|
||||||
|
TestEventType::GroupStarted { name } => {
|
||||||
|
assert!(self.test_name.is_none());
|
||||||
|
|
||||||
|
// Write the group name and bump the indent.
|
||||||
|
writeln!(stdout, "{}", name.yellow().italic()).unwrap();
|
||||||
|
|
||||||
|
// Add to the indent.
|
||||||
|
self.indent += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestEventType::GroupEnded => {
|
||||||
|
assert!(self.test_name.is_none());
|
||||||
|
|
||||||
|
// Drop the indent.
|
||||||
|
self.indent = self.indent.checked_sub(1).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
TestEventType::TestStarted { name } => {
|
||||||
|
assert!(self.test_name.is_none());
|
||||||
|
|
||||||
|
// Write the line.
|
||||||
|
write!(stdout, "{} ", name.white().italic()).unwrap();
|
||||||
|
self.test_name = Some(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
TestEventType::TestEnded { result } => {
|
||||||
|
let test_name = self.test_name.take().unwrap();
|
||||||
|
|
||||||
|
// Write the result.
|
||||||
|
match result {
|
||||||
|
TestResult::Passed => {
|
||||||
|
writeln!(stdout, "{}", "ok".green().bold()).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
TestResult::Failed(failure) => {
|
||||||
|
self.failures.push((test_name, failure));
|
||||||
|
writeln!(stdout, "{}", "FAIL".red().bold()).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
TestResult::Skipped => {
|
||||||
|
writeln!(stdout, "{}", "skipped".yellow().bold()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
// Completion.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestHandler for UserHandler {
|
||||||
|
fn handle_test(&mut self, event: TestEvent) {
|
||||||
|
self.process_events(Some(event));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for UserHandler {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
assert!(self.cache.is_empty());
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user