Write documentation for new iced_test APIs

This commit is contained in:
Héctor Ramón Jiménez 2025-09-12 22:53:28 +02:00
parent 5796ba272e
commit 59e2687146
No known key found for this signature in database
GPG key ID: 7CC46565708259A7
6 changed files with 240 additions and 21 deletions

View file

@ -1,17 +1,59 @@
//! A shareable, simple format of end-to-end tests.
use crate::Instruction;
use crate::core::Size;
use crate::emulator;
use crate::instruction;
/// An end-to-end test for iced applications.
///
/// Ice tests encode a certain configuration together with a sequence of instructions.
/// An ice test passes if all the instructions can be executed successfully.
///
/// Normally, ice tests are run by an [`Emulator`](crate::Emulator) in continuous
/// integration pipelines.
///
/// Ice tests can be easily run by saving them as `.ice` files in a folder and simply
/// calling [`run`](crate::run). These test files can be recorded by enabling the `tester`
/// feature flag in the root crate.
#[derive(Debug, Clone, PartialEq)]
pub struct Ice {
/// The viewport [`Size`] that must be used for the test.
pub viewport: Size,
/// The [`emulator::Mode`] that must be used for the test.
pub mode: emulator::Mode,
/// The name of the [`Preset`](crate::program::Preset) that must be used for the test.
pub preset: Option<String>,
/// The sequence of instructions of the test.
pub instructions: Vec<Instruction>,
}
impl Ice {
/// Parses an [`Ice`] test from its textual representation.
///
/// Here is an example of the [`Ice`] test syntax:
///
/// ```text
/// viewport: 500x800
/// mode: Impatient
/// preset: Empty
/// -----
/// click at "What needs to be done?"
/// type "Create the universe"
/// type enter
/// type "Make an apple pie"
/// type enter
/// expect "2 tasks left"
/// click at "Create the universe"
/// expect "1 task left"
/// click at "Make an apple pie"
/// expect "0 tasks left"
/// ```
///
/// This syntax is _very_ experimental and extremely likely to change often.
/// For this reason, it is reserved for advanced users that want to early test it.
///
/// Currently, in order to use it, you will need to earn the right and prove you understand
/// its experimental nature by reading the code!
pub fn parse(content: &str) -> Result<Self, ParseError> {
let Some((metadata, rest)) = content.split_once("-") else {
return Err(ParseError::NoMetadata);
@ -140,32 +182,64 @@ impl std::fmt::Display for Ice {
}
}
/// An error produced during [`Ice::parse`].
#[derive(Debug, Clone, thiserror::Error)]
pub enum ParseError {
/// No metadata is present.
#[error("the ice test has no metadata")]
NoMetadata,
/// The metadata is invalid.
#[error("invalid metadata in line {line}: \"{content}\"")]
InvalidMetadata { line: usize, content: String },
InvalidMetadata {
/// The number of the invalid line.
line: usize,
/// The content of the invalid line.
content: String,
},
/// The viewport is invalid.
#[error("invalid viewport in line {line}: \"{value}\"")]
InvalidViewport { line: usize, value: String },
InvalidViewport {
/// The number of the invalid line.
line: usize,
/// The invalid value.
value: String,
},
/// The [`emulator::Mode`] is invalid.
#[error("invalid mode in line {line}: \"{value}\"")]
InvalidMode { line: usize, value: String },
InvalidMode {
/// The number of the invalid line.
line: usize,
/// The invalid value.
value: String,
},
/// A metadata field is unknown.
#[error("unknown metadata field in line {line}: \"{field}\"")]
UnknownField { line: usize, field: String },
UnknownField {
/// The number of the invalid line.
line: usize,
/// The name of the unknown field.
field: String,
},
/// Viewport metadata is missing.
#[error("metadata is missing the viewport field")]
MissingViewport,
/// [`emulator::Mode`] metadata is missing.
#[error("metadata is missing the mode field")]
MissingMode,
/// An [`Instruction`] failed to parse.
#[error("invalid instruction in line {line}: {error}")]
InvalidInstruction {
/// The number of the invalid line.
line: usize,
/// The parse error.
error: instruction::ParseError,
},
}