material: Add doc tests

Taken from the slint repo, these verify that the code snippets included in the docs compile.


Imported from d247c2b72c
This commit is contained in:
Simon Hausmann 2025-07-01 10:56:23 +02:00 committed by Olivier Goffart
parent 256dfa7aaa
commit cf08b23b56
5 changed files with 195 additions and 2 deletions

View file

@ -27,6 +27,20 @@ jobs:
secrets:
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
tests:
env:
CARGO_PROFILE_RELEASE_OPT_LEVEL: s
CARGO_INCREMENTAL: false
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
key: tests
- name: Build & run tests
run: cargo test
deploy:
runs-on: ubuntu-latest

View file

@ -2,8 +2,8 @@
# SPDX-License-Identifier: MIT
[workspace]
members = ["crate", "examples/gallery"]
default-members = ["crate", "examples/gallery"]
members = ["crate", "examples/gallery", "tests/doctests"]
default-members = ["crate", "examples/gallery", "tests/doctests"]
resolver = "3"
[workspace.package]

View file

@ -0,0 +1,25 @@
# Copyright © SixtyFPS GmbH <info@slint.dev>
# SPDX-License-Identifier: MIT
[package]
name = "doctests"
authors.workspace = true
edition.workspace = true
homepage.workspace = true
license.workspace = true
repository.workspace = true
rust-version.workspace = true
version.workspace = true
publish = false
build = "build.rs"
[[bin]]
path = "main.rs"
name = "doctests"
[build-dependencies]
walkdir = "2"
[dev-dependencies]
slint-interpreter = { version = "1.12.0", features = ["display-diagnostics"] }
spin_on = { version = "0.1" }

View file

@ -0,0 +1,101 @@
// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: MIT
use std::io::{BufWriter, Write};
use std::path::Path;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let tests_file_path =
std::path::Path::new(&std::env::var_os("OUT_DIR").unwrap()).join("test_functions.rs");
let mut tests_file = BufWriter::new(std::fs::File::create(&tests_file_path)?);
let prefix = Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../..")
.canonicalize()?;
for entry in walkdir::WalkDir::new(&prefix)
.follow_links(false)
.into_iter()
.filter_entry(|entry| entry.file_name() != "target")
{
let entry = entry?;
let path = entry.path();
if path.extension().is_none_or(|e| e != "md" && e != "mdx") {
continue;
}
let file = std::fs::read_to_string(path)?;
let file = file.replace('\r', ""); // Remove \r, because Windows.
const BEGIN_MARKER: &str = "\n```slint";
if !file.contains(BEGIN_MARKER) {
continue;
}
let stem = path
.strip_prefix(&prefix)?
.to_string_lossy()
.replace('-', "ˍ")
.replace(['/', '\\'], "")
.replace(['.'], "")
.to_lowercase();
writeln!(tests_file, "\nmod {stem} {{")?;
let mut rest = file.as_str();
let mut line = 1;
while let Some(begin) = rest.find(BEGIN_MARKER) {
line += rest[..begin].bytes().filter(|&c| c == b'\n').count() + 1;
rest = rest[begin..].strip_prefix(BEGIN_MARKER).unwrap();
// Permit `slint,no-preview` and `slint,no-auto-preview` but skip `slint,ignore` and others.
rest = match rest.split_once('\n') {
Some((",ignore", _)) => continue,
Some((x, _)) if x.contains("no-test") => continue,
Some((_, rest)) => rest,
_ => continue,
};
let end = rest.find("\n```\n").ok_or_else(|| {
format!(
"Could not find the end of a code snippet in {}",
path.display()
)
})?;
let snippet = &rest[..end];
if snippet.starts_with("{{#include") {
// Skip non literal slint text
continue;
}
rest = &rest[end..];
write!(
tests_file,
r##"
#[test]
fn line_{}() {{
crate::do_test("{}", "{}").unwrap();
}}
"##,
line,
snippet.escape_default(),
path.to_string_lossy().escape_default()
)?;
line += snippet.bytes().filter(|&c| c == b'\n').count() + 1;
}
writeln!(tests_file, "}}")?;
println!("cargo:rerun-if-changed={}", path.display());
}
println!(
"cargo:rustc-env=TEST_FUNCTIONS={}",
tests_file_path.to_string_lossy()
);
println!("cargo:rustc-env=SLINT_ENABLE_EXPERIMENTAL_FEATURES=1");
Ok(())
}

View file

@ -0,0 +1,53 @@
// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: MIT
#![allow(uncommon_codepoints)]
#[cfg(test)]
fn do_test(snippet: &str, path: &str) -> Result<(), Box<dyn std::error::Error>> {
let must_wrap = !snippet.contains("component ") && !snippet.contains("global ");
let code = if must_wrap {
format!(
"import {{
CheckBox, CheckState, Switch, TimePicker, NavigationBar}} from\"material.slint\";
component Example {{\n{snippet}\n}}"
)
} else {
snippet.into()
};
let mut compiler = slint_interpreter::Compiler::default();
compiler.set_include_paths(vec![std::path::PathBuf::from(concat!(
env!("CARGO_MANIFEST_DIR"),
"/../../"
))]);
let result = spin_on::spin_on(compiler.build_from_source(code, path.into()));
let diagnostics = result
.diagnostics()
.filter(|d| {
let msg = d.message();
// It is ok if there is no components
msg != "No component found"
// Ignore warning about examples that don't inherit from Window or not exported
&& !msg.contains(" doesn't inherit Window.")
&& msg != "Component is implicitly marked for export. This is deprecated and it should be explicitly exported"
})
.collect::<Vec<_>>();
slint_interpreter::print_diagnostics(&diagnostics);
if result.has_errors() && !diagnostics.is_empty() {
return Err(format!("Error when loading {snippet:?} in {path:?}: {diagnostics:?}").into());
}
Ok(())
}
include!(env!("TEST_FUNCTIONS"));
fn main() {
println!("Nothing to see here, please run me through cargo test :)");
}