You've already forked comprehensive-rust
mirror of
https://github.com/google/comprehensive-rust.git
synced 2025-11-25 07:34:56 +02:00
tests: Migrate create_slide.list.sh into cargo xtask function. (#2957)
The new xtask function makes the helper code - more readable - more reliable due to better error checking - be in the same place as other helper functions - and more aligned to the skillset relevant for contributing in this repository. The shell script grew and was not readable for everyone anymore without deeper knowledge. mitigates #2941 in a more reliable way but does still not fully fix the root cause
This commit is contained in:
7
.github/workflows/build.yml
vendored
7
.github/workflows/build.yml
vendored
@@ -162,12 +162,7 @@ jobs:
|
||||
working-directory: ./tests
|
||||
- name: Test Javascript
|
||||
if: matrix.language == 'en'
|
||||
run: |
|
||||
./src/slides/create-slide.list.sh
|
||||
npm test
|
||||
env:
|
||||
TEST_BOOK_DIR: ../book/comprehensive-rust-${{ matrix.language }}/html
|
||||
working-directory: ./tests
|
||||
run: cargo xtask web-tests --dir book/comprehensive-rust-${{ matrix.language }}/html
|
||||
|
||||
po-diff:
|
||||
name: Translation diff
|
||||
|
||||
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -3554,6 +3554,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This script (re)creates the slides.list.ts file based on the given book html directory.
|
||||
# It is used to regenerate the list of slides that are tested in the slide-size.test.ts file.
|
||||
# Takes either TEST_BOOK_DIR environment variable or first parameter as override.
|
||||
|
||||
set -e
|
||||
BASEDIR="$(dirname "$0")"
|
||||
|
||||
if [[ -n "$1" ]]; then
|
||||
# take directory from command line
|
||||
TEST_BOOK_DIR="$1"
|
||||
fi
|
||||
|
||||
# check if TEST_BOOK_DIR is empty (not set by environment nor parameter)
|
||||
if [[ -z "${TEST_BOOK_DIR}" ]]; then
|
||||
echo "Usage: $0 <book_html_dir>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# check if this is the correct root directory by checking if it contains the index.html
|
||||
if [[ ! -f "${TEST_BOOK_DIR}/index.html" ]]; then
|
||||
echo "Could not find index.html in ${TEST_BOOK_DIR}. Please check if the correct directory is used (e.g. book/html). You might need to (re)create the directory with mdbook build."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# These special pages should never be tested for slide size.
|
||||
EXCLUDE_FILES=(
|
||||
"exercise.html"
|
||||
"solution.html"
|
||||
"toc.html"
|
||||
"print.html"
|
||||
"404.html"
|
||||
"glossary.html"
|
||||
"index.html"
|
||||
"course-structure.html"
|
||||
)
|
||||
|
||||
CANDIDATE_SLIDES=""
|
||||
if [[ -n "${CI}" ]]; then
|
||||
echo "CI environment detected, checking only changed slides."
|
||||
# Find changed markdown files in src/ and map them to their html output.
|
||||
# GITHUB_BASE_REF is available in PRs. Default to 'main' for other CI contexts.
|
||||
CANDIDATE_SLIDES=$(git diff --name-only "origin/${GITHUB_BASE_REF:-main}"... \
|
||||
| grep '^src/.*\.md$' \
|
||||
| sed 's|^src/||; s|\.md$|.html|' \
|
||||
|| true)
|
||||
else
|
||||
# TODO: Limit the amount of files to check: Figure out what a good local diff base is.
|
||||
echo "Local environment, checking all slides."
|
||||
# Find all .html files recursively.
|
||||
CANDIDATE_SLIDES=$(find "${TEST_BOOK_DIR}" -name "*.html" -printf "%P\n")
|
||||
fi
|
||||
|
||||
SLIDES=""
|
||||
if [[ -n "${CANDIDATE_SLIDES}" ]]; then
|
||||
# From the candidate slides, filter out:
|
||||
# - Files that are just redirects.
|
||||
# - Files that are in the EXCLUDE_FILES list.
|
||||
# - Files that do not exist in the TEST_BOOK_DIR (by using ls)
|
||||
pushd "${TEST_BOOK_DIR}" > /dev/null
|
||||
EXCLUDE_PATTERN=$(IFS="|" ; echo "${EXCLUDE_FILES[*]}")
|
||||
SLIDES=$(echo "${CANDIDATE_SLIDES}" | grep -v -E "${EXCLUDE_PATTERN}" \
|
||||
| xargs ls 2>/dev/null \
|
||||
| xargs -r grep -L "Redirecting to...") || true
|
||||
popd > /dev/null
|
||||
fi
|
||||
|
||||
if [[ -n "${CI}" ]]; then
|
||||
echo "The following slides will be checked:"
|
||||
echo "${SLIDES}"
|
||||
fi
|
||||
OUTPUT="${BASEDIR}/slides.list.ts"
|
||||
|
||||
# create a ts module that can be imported in the tests
|
||||
echo "export const slides = [" > ${OUTPUT};
|
||||
for SLIDE in ${SLIDES}; do
|
||||
echo " \"${SLIDE}\"," >> ${OUTPUT};
|
||||
done;
|
||||
echo "];" >> ${OUTPUT};
|
||||
@@ -1,5 +1,5 @@
|
||||
// to enable local testing for slide size checks please (re)generate this file by executing:
|
||||
// $ ./tests/src/slides/create-slide.list.sh book/html
|
||||
// $ cargo xtask create-slide-list --dir book/html/
|
||||
//
|
||||
// This file is on purpose not pre-filled in the repository to avoid
|
||||
// a) manual maintenance of slide list
|
||||
|
||||
@@ -7,3 +7,4 @@ publish = false
|
||||
[dependencies]
|
||||
anyhow = "1.0.100"
|
||||
clap = { version = "4.5.48", features = ["derive"] }
|
||||
walkdir = "2.5.0"
|
||||
|
||||
@@ -21,9 +21,10 @@
|
||||
|
||||
use anyhow::{Context, Result, anyhow};
|
||||
use clap::{Parser, Subcommand};
|
||||
use std::env;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
use std::{env, fs};
|
||||
use walkdir::WalkDir;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
execute_task()
|
||||
@@ -54,6 +55,13 @@ enum Task {
|
||||
#[arg(short, long)]
|
||||
dir: Option<PathBuf>,
|
||||
},
|
||||
/// (Re)creates the slides.list.ts file based on the given book html
|
||||
/// directory.
|
||||
CreateSlideList {
|
||||
/// The book html directory
|
||||
#[arg(short, long)]
|
||||
dir: PathBuf,
|
||||
},
|
||||
/// Tests all included Rust snippets.
|
||||
RustTests,
|
||||
/// Starts a web server with the course.
|
||||
@@ -85,6 +93,7 @@ fn execute_task() -> Result<()> {
|
||||
match cli.task {
|
||||
Task::InstallTools { binstall } => install_tools(binstall),
|
||||
Task::WebTests { dir } => run_web_tests(dir),
|
||||
Task::CreateSlideList { dir } => create_slide_list(dir),
|
||||
Task::RustTests => run_rust_tests(),
|
||||
Task::Serve { language, output } => start_web_server(language, output),
|
||||
Task::Build { language, output } => build(language, output),
|
||||
@@ -191,13 +200,7 @@ fn run_web_tests(dir: Option<PathBuf>) -> Result<()> {
|
||||
|
||||
if let Some(d) = &absolute_dir {
|
||||
println!("Refreshing slide lists...");
|
||||
let refresh_slides_script = Path::new("tests")
|
||||
.join("src")
|
||||
.join("slides")
|
||||
.join("create-slide.list.sh");
|
||||
let mut cmd = Command::new(&refresh_slides_script);
|
||||
cmd.current_dir(workspace_dir).arg(d);
|
||||
run_command(&mut cmd)?;
|
||||
create_slide_list(d.clone())?;
|
||||
}
|
||||
|
||||
let tests_dir = workspace_dir.join("tests");
|
||||
@@ -210,6 +213,125 @@ fn run_web_tests(dir: Option<PathBuf>) -> Result<()> {
|
||||
run_command(&mut cmd)
|
||||
}
|
||||
|
||||
// Creates a list of .html slides from the html directory containing the
|
||||
// index.html to check the slides.
|
||||
// - CI environment: Only modified files are listed
|
||||
// - Otherwise: All existing html files
|
||||
fn create_slide_list(html_directory: PathBuf) -> Result<()> {
|
||||
let workspace_dir = Path::new(env!("CARGO_WORKSPACE_DIR"));
|
||||
let tests_dir = workspace_dir.join("tests");
|
||||
|
||||
// Check if the provided directory is correct
|
||||
if !html_directory.join("index.html").exists() {
|
||||
return Err(anyhow!(
|
||||
"Could not find index.html in {}. Please check if the correct directory is used (e.g. book/html).",
|
||||
html_directory.display()
|
||||
));
|
||||
}
|
||||
|
||||
// These special slides are not checked against the style guide
|
||||
let exclude_paths = [
|
||||
"exercise.html",
|
||||
"solution.html",
|
||||
"toc.html",
|
||||
"print.html",
|
||||
"404.html",
|
||||
"glossary.html",
|
||||
"index.html",
|
||||
"course-structure.html",
|
||||
]
|
||||
.map(PathBuf::from);
|
||||
|
||||
// Collect the files relevant for evaluation.
|
||||
// - CI environment variable is set: all modified markdown files in the src/
|
||||
// directory
|
||||
// - all html files in the provided directory otherwise
|
||||
let candidate_slides: Vec<PathBuf> = if env::var("CI").is_ok() {
|
||||
println!("CI environment detected, checking only modified slides.");
|
||||
// GITHUB_BASE_REF is available in PRs. Default to 'main' for other CI
|
||||
// contexts.
|
||||
let base_ref = env::var("GITHUB_BASE_REF").unwrap_or("main".to_string());
|
||||
let mut cmd = Command::new("git");
|
||||
cmd.arg("diff")
|
||||
.arg("--name-only")
|
||||
.arg(format!("{}...", base_ref))
|
||||
.arg("--")
|
||||
// Retrieve all modified files in the src directory.
|
||||
// Pathspec syntax: https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-pathspec
|
||||
// `*` can match path separators, thus matches also files in
|
||||
// subdirectories
|
||||
.arg("src/*.md");
|
||||
println!("> {cmd:?}");
|
||||
let output = cmd.output().context("Failed to run git diff")?;
|
||||
String::from_utf8(output.stdout)?
|
||||
.lines()
|
||||
.map(|line| {
|
||||
let path = Path::new(line);
|
||||
// We know the path starts with "src/" because of the pathspec in the
|
||||
// `git diff` command, and we need it relative to the html base
|
||||
// directory
|
||||
let stripped_path = path.strip_prefix("src").unwrap();
|
||||
let mut html_path = stripped_path.to_path_buf();
|
||||
// replace the .md extension with .html
|
||||
html_path.set_extension("html");
|
||||
html_path
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
println!("Local environment, checking all slides.");
|
||||
WalkDir::new(&html_directory)
|
||||
.into_iter()
|
||||
.filter_map(|e| e.ok())
|
||||
// only files with .html extension
|
||||
.filter(|e| e.path().extension().is_some_and(|ext| ext == "html"))
|
||||
// relative path inside the html directory
|
||||
.map(|e| e.path().strip_prefix(&html_directory).unwrap().to_path_buf())
|
||||
.collect()
|
||||
};
|
||||
// Filter the candidate slides
|
||||
let mut slides = Vec::new();
|
||||
for slide in candidate_slides {
|
||||
// Skip excluded files
|
||||
if exclude_paths.iter().any(|exclude_path| slide.ends_with(exclude_path)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Test if the html files actually exist
|
||||
let full_path = html_directory.join(&slide);
|
||||
if !full_path.exists() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Optimization: check if these are redirection html files and skip these
|
||||
let content = fs::read_to_string(&full_path)
|
||||
.with_context(|| format!("Failed to read slide: {}", slide.display()))?;
|
||||
if content.contains("Redirecting to...") {
|
||||
continue;
|
||||
}
|
||||
slides.push(slide);
|
||||
}
|
||||
|
||||
if env::var("CI").is_ok() {
|
||||
println!("The following slides have been modified and will be checked:");
|
||||
for slide in &slides {
|
||||
println!("{}", slide.display());
|
||||
}
|
||||
}
|
||||
|
||||
// Write the file list into a .ts file that can be read by the JS based webtest
|
||||
let output_path = tests_dir.join("src").join("slides").join("slides.list.ts");
|
||||
let mut output_content = "export const slides = [\n".to_string();
|
||||
for slide in slides {
|
||||
output_content.push_str(&format!(" \"{}\",\n", slide.display()));
|
||||
}
|
||||
output_content.push_str("];\n");
|
||||
|
||||
fs::write(&output_path, output_content)
|
||||
.with_context(|| format!("Failed to write to {}", output_path.display()))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_rust_tests() -> Result<()> {
|
||||
println!("Running rust tests...");
|
||||
let workspace_root = Path::new(env!("CARGO_WORKSPACE_DIR"));
|
||||
|
||||
Reference in New Issue
Block a user