Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Validate forge command outputs in docs #2687

Open
wants to merge 103 commits into
base: master
Choose a base branch
from

Conversation

franciszekjob
Copy link
Collaborator

@franciszekjob franciszekjob commented Nov 19, 2024

Relates #2479
Closes #2684

Introduced changes

  • Automatically run snforge commands which are present in the docc; if the output of respective output is present in the docs too, it's compared with the actual output from executed command
  • Allow ignoring commands (so they will not be executed)

Checklist

  • Linked relevant issue
  • Updated relevant documentation
  • Added relevant tests
  • Performed self-review of the code
  • Added changes to CHANGELOG.md

franciszekjob and others added 30 commits November 8, 2024 17:38
@franciszekjob franciszekjob marked this pull request as ready for review November 21, 2024 11:09
crates/docs/src/validation.rs Outdated Show resolved Hide resolved
crates/docs/src/validation.rs Outdated Show resolved Hide resolved
Comment on lines 70 to 91
let temp = resolve_temp_dir(&package_name, &listings_to_packages_mapping);
let output = test_runner(&temp).args(args).assert();

assert_stdout_contains(output, snippet_output);
}
}

print_success_message(snippets.len(), "snforge");
print_success_message(snippets.len(), snippet_type.as_str());
}

fn resolve_temp_dir(
package_name: &str,
listings_to_packages_mapping: &HashMap<String, Vec<String>>,
) -> TempDir {
if is_package_from_docs_listings(package_name, listings_to_packages_mapping) {
setup_package_from_docs_listings(package_name, listings_to_packages_mapping)
} else if ["addition", "fibonacci"].contains(&package_name) {
setup_hello_workspace()
} else {
setup_package(package_name)
}
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: Currently we use hello_workpaces package (from crates/forge/tests/data) in one example in docs. In future we can simplify this logic and remove is_package_from_docs_listings, once we use only packages from docs listings. Ref: #2698

@@ -63,8 +46,8 @@ fn test_docs_snippets() {
let exit_code = output.status.code().unwrap_or_default();
let stderr = String::from_utf8_lossy(&output.stderr);

assert_valid_snippet(exit_code != 2, snippet, "sncast", &stderr);
assert_valid_snippet(exit_code != 2, snippet, &stderr);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not check if it's 0?

@@ -1,4 +1,4 @@
#!/bin/bash
set -e

for d in ./docs/listings/*; do (cd "$d" && scarb test); done
for d in ./docs/listings/*; do (cd "$d" && scarb check); done
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How did it work before?

Comment on lines 69 to 73
if package_path.contains("docs/listings") {
scarb_toml["dev-dependencies"]["snforge_std"]
.as_table_mut()
.and_then(|snforge_std| snforge_std.remove("workspace"));
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think we should be hardcoding some docs specific behaviors here. This could be a separate function that shares the logic with this one.

+ I think you wouldn't have to change the interface of current setup_package_with_file_patterns as well.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that you created one below, but I'd try to change this so the interface of tests we already had remains unchanged.

Comment on lines 176 to 201
pub fn create_listings_to_packages_mapping() -> HashMap<String, Vec<String>> {
let docs_listings_path = "../../docs/listings";
let mut mapping = HashMap::new();

for listing in WalkDir::new(docs_listings_path)
.min_depth(1)
.max_depth(1)
.into_iter()
.filter_map(Result::ok)
.filter(|e| e.file_type().is_dir())
{
let listing_path = listing.path();
let crates_dir = listing_path.join("crates");

if crates_dir.is_dir() {
let packages = list_packages_in_directory(&crates_dir);
if let Some(listing_name) = listing_path.file_name().and_then(|x| x.to_str()) {
if !packages.is_empty() {
mapping.insert(listing_name.to_string(), packages);
}
}
}
}

mapping
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this logic a bit too complicated just to get a mapping between a directory name and it's related crates?

Comment on lines 94 to 97
let command_match = caps.get(2)?;
let match_start = caps.get(0)?.start();
let output = caps.get(3).map(|m| m.as_str().to_string());
let ignored = caps.get(1).map_or(false, |m| m.as_str().contains("ignore"));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Could you reorder this to have .get(0), .get(1) etc?

crates/docs/src/validation.rs Outdated Show resolved Hide resolved
);
}

pub fn create_listings_to_packages_mapping() -> HashMap<String, Vec<String>> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
pub fn create_listings_to_packages_mapping() -> HashMap<String, Vec<String>> {
pub fn create_listing_to_packages_map() -> HashMap<String, Vec<String>> {

Applicable in other places to rename listings_to_packages_mapping -> listing_to_packages_map

mapping
}

fn list_packages_in_directory(dir_path: &Path) -> Vec<String> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we could leverage scarb metadata command to get packages names?

.map(|(listing_name, _)| listing_name.clone())
}

pub(crate) fn setup_package_from_docs_listings(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
pub(crate) fn setup_package_from_docs_listings(
pub(crate) fn setup_package_from_docs_listing(


pub(crate) fn setup_package_from_docs_listings(
package_name: &str,
packages_mapping: &HashMap<String, Vec<String>>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
packages_mapping: &HashMap<String, Vec<String>>,
listing_to_packages_map: &HashMap<String, Vec<String>>,

Also in other places

pub fn get_re(&self) -> Regex {
let escaped_command = regex::escape(self.as_str());
let pattern = format!(
r"(?ms)^(?:<!--\s*(.*?)\s*-->\n)?```shell\n\$ ({escaped_command} .+?)\n```(?:\s*<details>\n<summary>Output:<\/summary>\n\n```shell\n([\s\S]+?)\n```[\s]*<\/details>)?"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a short comment explanation of this regex?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Improve skipping docs snippets
4 participants