Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 55 additions & 13 deletions src/bin/uudoc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ fn gen_manpage<T: Args>(
} else {
validation::setup_localization_or_exit(utility);
let mut cmd = util_map.get(utility).unwrap().1();
cmd.set_bin_name(utility.clone());
let mut cmd = cmd.display_name(utility);
if let Some(zip) = tldr {
if let Ok(examples) = write_zip_examples(zip, utility, false) {
cmd = cmd.after_help(examples);
Expand Down Expand Up @@ -274,12 +276,15 @@ fn main() -> io::Result<()> {

println!("Writing to utils");
for (&name, (_, command)) in utils {
if name == "[" {
continue;
}
let p = format!("docs/src/utils/{name}.md");
let (utils_name, usage_name, command) = match name {
"[" => {
continue;
}
n => (n, n, command),
};
let p = format!("docs/src/utils/{usage_name}.md");

let fluent = File::open(format!("src/uu/{name}/locales/en-US.ftl"))
let fluent = File::open(format!("src/uu/{utils_name}/locales/en-US.ftl"))
.and_then(|mut f: File| {
let mut s = String::new();
f.read_to_string(&mut s)?;
Expand All @@ -291,28 +296,51 @@ fn main() -> io::Result<()> {
MDWriter {
w: Box::new(f),
command: command(),
name,
name: usage_name,
tldr_zip: &mut tldr_zip,
utils_per_platform: &utils_per_platform,
fluent,
fluent_key: utils_name.to_string(),
}
.markdown()?;
println!("Wrote to '{p}'");
} else {
println!("Error writing to {p}");
}
writeln!(summary, "* [{name}](utils/{name}.md)")?;
writeln!(summary, "* [{usage_name}](utils/{usage_name}.md)")?;
}
Ok(())
}

fn fix_usage(name: &str, usage: String) -> String {
match name {
"test" => {
// replace to [ but not the first two line
usage
.lines()
.enumerate()
.map(|(i, l)| {
if i > 1 {
l.replace("test", "[")
} else {
l.to_string()
}
})
.collect::<Vec<_>>()
.join("\n")
}
_ => usage,
}
}

struct MDWriter<'a, 'b> {
w: Box<dyn Write>,
command: Command,
name: &'a str,
tldr_zip: &'b mut Option<ZipArchive<File>>,
utils_per_platform: &'b HashMap<&'b str, Vec<String>>,
fluent: Option<String>,
fluent_key: String,
}

impl MDWriter<'_, '_> {
Expand Down Expand Up @@ -343,9 +371,20 @@ impl MDWriter<'_, '_> {
if id.name == key {
// Simple text extraction - just concatenate text elements
let mut result = String::new();
use fluent_syntax::ast::{
Expression, InlineExpression,
PatternElement::{Placeable, TextElement},
};
for element in elements {
if let fluent_syntax::ast::PatternElement::TextElement { value } = element {
result.push_str(&value);
if let TextElement { ref value } = element {
result.push_str(value);
}
if let Placeable {
expression:
Expression::Inline(InlineExpression::StringLiteral { ref value }),
} = element
{
result.push_str(value);
}
}
return Some(result);
Expand Down Expand Up @@ -403,7 +442,8 @@ impl MDWriter<'_, '_> {
/// # Errors
/// Returns an error if the writer fails.
fn usage(&mut self) -> io::Result<()> {
if let Some(usage) = self.extract_fluent_value(&format!("{}-usage", self.name)) {
if let Some(usage) = self.extract_fluent_value(&format!("{}-usage", self.fluent_key)) {
let usage = fix_usage(self.name, usage);
writeln!(self.w, "\n```")?;
writeln!(self.w, "{usage}")?;
writeln!(self.w, "```")
Expand All @@ -415,7 +455,7 @@ impl MDWriter<'_, '_> {
/// # Errors
/// Returns an error if the writer fails.
fn about(&mut self) -> io::Result<()> {
if let Some(about) = self.extract_fluent_value(&format!("{}-about", self.name)) {
if let Some(about) = self.extract_fluent_value(&format!("{}-about", self.fluent_key)) {
writeln!(self.w, "{about}")
} else {
Ok(())
Expand All @@ -425,7 +465,9 @@ impl MDWriter<'_, '_> {
/// # Errors
/// Returns an error if the writer fails.
fn after_help(&mut self) -> io::Result<()> {
if let Some(after_help) = self.extract_fluent_value(&format!("{}-after-help", self.name)) {
if let Some(after_help) =
self.extract_fluent_value(&format!("{}-after-help", self.fluent_key))
{
writeln!(self.w, "\n\n{after_help}")
} else {
Ok(())
Expand Down Expand Up @@ -498,7 +540,7 @@ impl MDWriter<'_, '_> {
writeln!(self.w, "</dt>")?;
let help_text = arg.get_help().unwrap_or_default().to_string();
// Try to resolve Fluent key if it looks like one, otherwise use as-is
let resolved_help = if help_text.starts_with(&format!("{}-help-", self.name)) {
let resolved_help = if help_text.starts_with(&format!("{}-help-", self.fluent_key)) {
self.extract_fluent_value(&help_text).unwrap_or(help_text)
} else {
help_text
Expand Down
2 changes: 1 addition & 1 deletion src/uu/test/locales/en-US.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ test-usage = test EXPRESSION
test
{"[ EXPRESSION ]"}
{"[ ]"}
{"[ OPTION ]"}
{"[ OPTION"}
test-after-help = Exit with the status determined by EXPRESSION.

An omitted EXPRESSION defaults to false.
Expand Down
2 changes: 1 addition & 1 deletion src/uu/test/locales/fr-FR.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ test-usage = test EXPRESSION
test
{"[ EXPRESSION ]"}
{"[ ]"}
{"[ OPTION ]"}
{"[ OPTION"}
test-after-help = Quitter avec le statut déterminé par EXPRESSION.

Une EXPRESSION omise vaut false par défaut.
Expand Down
122 changes: 122 additions & 0 deletions tests/test_uudoc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
//! Tests on the `uudoc` binary.
//!
//! To run the uudoc
//! ```
//! cargo run --bin uudoc --features uudoc
//! ```
//!
//! To run the tests
//! ```
//! cargo test --features uudoc
//! ```
#![cfg(feature = "uudoc")]

use std::env;
pub const TESTS_BINARY: &str = env!("CARGO_BIN_EXE_uudoc");

// Set the environment variable for any tests

// Use the ctor attribute to run this function before any tests
#[ctor::ctor]
fn init() {
// No need for unsafe here
unsafe {
std::env::set_var("UUTESTS_BINARY_PATH", TESTS_BINARY);
}
// Print for debugging
eprintln!("Setting UUTESTS_BINARY_PATH={TESTS_BINARY}");
}

/// Run the `uudoc` command and return the output as a vector of strings.
/// # Errors
/// If the command fails to execute or if the output cannot be converted to UTF-8, an `io::Error` is returned.
fn run_write_doc() -> Vec<String> {
use std::process::Command;
use uutests::util::TestScenario;

let scenario = TestScenario::new("");
let output = Command::new(&scenario.bin_path).output().unwrap();
assert!(
output.status.success(),
"uudoc command failed: {}",
String::from_utf8_lossy(&output.stderr)
);

String::from_utf8(output.stdout)
.unwrap()
.lines()
.map(String::from)
.filter(|line| line.starts_with("Wrote"))
.collect::<Vec<String>>()
}

fn get_doc_file_from_output(output: &str) -> (String, String) {
let correct_path_test = output
.strip_prefix("Wrote to '")
.unwrap()
.strip_suffix("'")
.unwrap()
.to_string();
let content = std::fs::read_to_string(&correct_path_test);
let content = match content {
Ok(content) => content,
Err(e) => {
panic!(
"Failed to read file {}: {} from {:?}",
correct_path_test,
e,
env::current_dir()
);
}
};
(correct_path_test, content)
}

#[test]
fn uudoc_check_test() {
let pages = run_write_doc();
// assert wrote to the correct file
let path_test = pages.iter().find(|line| line.contains("test.md")).unwrap();
let (correct_path, content) = get_doc_file_from_output(path_test);

// open the file
assert!(
content.contains(
"```
test EXPRESSION
test
[ EXPRESSION ]
[ ]
[ OPTION
```
",
),
"{correct_path} does not contains the required text"
);
}

#[test]
fn uudoc_check_sums() {
let pages = run_write_doc();
let sums = [
"md5sum",
"sha1sum",
"sha224sum",
"sha256sum",
"sha384sum",
"sha512sum",
"b2sum",
];
for one_sum in sums {
let output_path = pages
.iter()
.find(|one_line| one_line.contains(one_sum))
.unwrap();
let (correct_path, content) = get_doc_file_from_output(output_path);
let formatted = format!("```\n{one_sum} [OPTIONS] [FILE]...\n```");
assert!(
content.contains(&formatted),
"Content of {correct_path} does not contain the expected format: {formatted}",
);
}
}
Loading