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

add a --chapter option to mdbook test. #1741

Merged
merged 21 commits into from
Aug 26, 2022
Merged

add a --chapter option to mdbook test. #1741

merged 21 commits into from
Aug 26, 2022

Conversation

clovett
Copy link
Contributor

@clovett clovett commented Feb 9, 2022

Sometimes when working on large books it is handy to be able to run mdbook on a single chapter of a book. For example:

mdbook test --chapter build

mdbook test --chapter build
2022-02-09 13:48:17 [INFO] (mdbook::book): Skipping chapter 'Introduction'...
2022-02-09 13:48:17 [INFO] (mdbook::book): Skipping chapter 'Installation'...
2022-02-09 13:48:17 [INFO] (mdbook::book): Skipping chapter 'Reading Books'...
2022-02-09 13:48:17 [INFO] (mdbook::book): Skipping chapter 'Creating a Book'...
2022-02-09 13:48:17 [INFO] (mdbook::book): Skipping chapter 'Command Line Tool'...
2022-02-09 13:48:17 [INFO] (mdbook::book): Skipping chapter 'init'...
2022-02-09 13:48:17 [INFO] (mdbook::book): Testing chapter 'build': "d:\\git\\clovett\\mdBook\\guide\\src\\cli/build.md"
2022-02-09 13:48:17 [INFO] (mdbook::book): Skipping chapter 'watch'...
2022-02-09 13:48:17 [INFO] (mdbook::book): Skipping chapter 'serve'...
2022-02-09 13:48:17 [INFO] (mdbook::book): Skipping chapter 'test'...
2022-02-09 13:48:18 [INFO] (mdbook::book): Skipping chapter 'clean'...
2022-02-09 13:48:18 [INFO] (mdbook::book): Skipping chapter 'completions'...
2022-02-09 13:48:18 [INFO] (mdbook::book): Skipping chapter 'Format'...
2022-02-09 13:48:18 [INFO] (mdbook::book): Skipping chapter 'SUMMARY.md'...
2022-02-09 13:48:18 [INFO] (mdbook::book): Skipping chapter 'Configuration'...
2022-02-09 13:48:18 [INFO] (mdbook::book): Skipping chapter 'General'...
2022-02-09 13:48:18 [INFO] (mdbook::book): Skipping chapter 'Preprocessors'...
2022-02-09 13:48:18 [INFO] (mdbook::book): Skipping chapter 'Renderers'...
2022-02-09 13:48:18 [INFO] (mdbook::book): Skipping chapter 'Environment Variables'...
2022-02-09 13:48:18 [INFO] (mdbook::book): Skipping chapter 'Theme'...
2022-02-09 13:48:18 [INFO] (mdbook::book): Skipping chapter 'index.hbs'...
2022-02-09 13:48:18 [INFO] (mdbook::book): Skipping chapter 'Syntax highlighting'...
2022-02-09 13:48:18 [INFO] (mdbook::book): Skipping chapter 'Editor'...
2022-02-09 13:48:18 [INFO] (mdbook::book): Skipping chapter 'MathJax Support'...
2022-02-09 13:48:18 [INFO] (mdbook::book): Skipping chapter 'mdBook-specific features'...
2022-02-09 13:48:18 [INFO] (mdbook::book): Skipping chapter 'Markdown'...
2022-02-09 13:48:18 [INFO] (mdbook::book): Skipping chapter 'Continuous Integration'...
2022-02-09 13:48:18 [INFO] (mdbook::book): Skipping chapter 'For Developers'...
2022-02-09 13:48:18 [INFO] (mdbook::book): Skipping chapter 'Preprocessors'...
2022-02-09 13:48:18 [INFO] (mdbook::book): Skipping chapter 'Alternative Backends'...
2022-02-09 13:48:18 [INFO] (mdbook::book): Skipping chapter 'Contributors'...
``` 

Copy link
Contributor

@ehuss ehuss left a comment

Choose a reason for hiding this comment

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

Thanks! This seems like it could be useful.

I'd like to suggest that perhaps the -c flag could take either a chapter name or file path. In most cases, I know the file path but not the chapter name. File paths also support command-line completion, whereas chapter names need to be typed exactly (and deal with shell quoting and such).

Can you also update the documentation at https://github.com/rust-lang/mdBook/blob/master/guide/src/cli/test.md?

src/book/mod.rs Outdated Show resolved Hide resolved
src/book/mod.rs Outdated Show resolved Hide resolved
Comment on lines 70 to 74
if (theme && theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}

if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
if (sidebar && sidebar.startsWith('"') && sidebar.endsWith('"')) {
Copy link
Contributor

Choose a reason for hiding this comment

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

These changes seem unrelated to the PR?

Choose a reason for hiding this comment

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

they were null reference bugs I ran into while testing this PR.

Copy link
Contributor

Choose a reason for hiding this comment

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

I would prefer unrelated changes to not be included.

Choose a reason for hiding this comment

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

this is a bit different as I couldn't get the tests to pass without this.

Copy link
Contributor

Choose a reason for hiding this comment

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

Which tests aren't passing? What kind of errors are you seeing? I don't believe there are any javascript tests, so it is not clear to me how this could have any impact on anything.

Choose a reason for hiding this comment

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

Hmmm, trying to repro it I can't find it any more and I see there's a try{ } catch (e) { } around this whole thing, so it's possible I caught the exception in the debugger and mistook it for a bug when it was expecting the try/catch block to catch it and continue. So I reverted these changes.

@ehuss ehuss added the S-waiting-on-author Status: The marked PR is awaiting some action (such as code changes) from the PR author. label Apr 15, 2022
@lovettchris
Copy link

I'd like to suggest that perhaps the -c flag could take either a chapter name or file path. In most cases, I know the file path but not the chapter name. File paths also support command-line completion, whereas chapter names need to be typed exactly (and deal with shell quoting and such).

Done.

@ehuss
Copy link
Contributor

ehuss commented Jul 16, 2022

@clovett Just checking in if you are still interested in this. It looks like there are some test failures.

@lovettchris
Copy link

I am still interested in this PR, but I'm kinda stumped by the test failures. Clearly the test output changes, and I tried to fix that, but I'm not sure why it didn't work.

@ehuss
Copy link
Contributor

ehuss commented Aug 1, 2022

The PR changes the output to use chapter_path instead of path. path is absolute whereas chapter_path is relative. That means that the pattern Testing chapter [^:]*: "([^"]+)[\\/]README.md needs to be updated, as there is no leading slash in front of the README.md file.

@lovettchris
Copy link

The PR changes the output to use chapter_path instead of path. path is absolute whereas chapter_path is relative. That means that the pattern Testing chapter [^:]*: "([^"]+)[\\/]README.md needs to be updated, as there is no leading slash in front of the README.md file.

Ah, thanks for the hint - I think I got it now.

@clovett clovett requested a review from ehuss August 4, 2022 05:55
@clovett
Copy link
Contributor Author

clovett commented Aug 4, 2022

@ehuss - yay the tests are passing.

Copy link
Contributor

@ehuss ehuss left a comment

Choose a reason for hiding this comment

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

I think this will need some tests.

Also, can you add some kind of error message if the --chapter value doesn't match anything?

src/book/mod.rs Outdated
/// Run `rustdoc` tests on the book, linking against the provided libraries.
pub fn test(&mut self, library_paths: Vec<&str>) -> Result<()> {
/// Run `rustdoc` tests on the book, linking against the provided libraries
/// and optionally testing only a signal named chapter while skipping the others.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/// and optionally testing only a signal named chapter while skipping the others.
/// and optionally testing only a single named chapter while skipping the others.

Choose a reason for hiding this comment

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

fixed

src/book/mod.rs Outdated
pub fn test(&mut self, library_paths: Vec<&str>) -> Result<()> {
/// Run `rustdoc` tests on the book, linking against the provided libraries
/// and optionally testing only a signal named chapter while skipping the others.
pub fn test(&mut self, library_paths: Vec<&str>, chapter: Option<&str>) -> Result<()> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Unfortunately we can't change the API as that would be a semver breaking change.

I think you will need to add a new method, and have the old test call it.

Copy link

@lovettchris lovettchris Aug 11, 2022

Choose a reason for hiding this comment

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

done, I added "test_chapter", but instead of duplicating the code I just make test call test_chapter and I added a couple new tests for "test_chapter".

src/book/mod.rs Outdated
Comment on lines 274 to 276
if chapter.is_some()
&& ch.name != chapter.unwrap()
&& chapter_path.to_str().unwrap() != chapter.unwrap()
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd prefer to avoid using unwrap here. Perhaps more like this:

Suggested change
if chapter.is_some()
&& ch.name != chapter.unwrap()
&& chapter_path.to_str().unwrap() != chapter.unwrap()
if let Some(chapter) = chapter {
if ch.name != chapter && chapter_path.to_str() != Some(chapter) {

However, this logic seems wrong. It seems to require that the --chapter value passed in equals both the chapter name and its filename. I would expect it to match one or the other. Perhaps something more like?

!(ch.name == chapter || chapter_path.to_str() == Some(chapter))

Choose a reason for hiding this comment

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

it works, notice this is the inverse of the test !(ch.name == chapter || chapter_path == chapter)

Comment on lines 70 to 74
if (theme && theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}

if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
if (sidebar && sidebar.startsWith('"') && sidebar.endsWith('"')) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I would prefer unrelated changes to not be included.

@lovettchris
Copy link

lovettchris commented Aug 11, 2022

Can you also update the documentation at https://github.com/rust-lang/mdBook/blob/master/guide/src/cli/test.md?

I added:

--chapter

The --chapter (-c) option allows you to test a specific chapter of the
book using the chapter name or the relative path to the chapter.

src/book/mod.rs Outdated
Comment on lines 290 to 291
if chapter_name != "" && ch.name != chapter_name && cp != chapter_name {
info!("Skipping chapter '{}'...", ch.name);
Copy link
Contributor

Choose a reason for hiding this comment

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

This all seemed to have reverted back to using an empty string as a sentinel instead of an Option. Is there any particular reason for that?

Choose a reason for hiding this comment

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

sorry this might be my Rust ignorance, I couldn't figure out how to compare chapter : Option<&str> and ch.name: String. Rust complains: "can't compare std::string::String with std::option::Option<&str>". So, moving them both to &str solved the problem, but I guess I could instead convert ch.name to Option<&str> using:

let cn: Option<&str> = Some(&ch.name);

I pushed this change to see if this is what you prefer...

self.test_chapter(library_paths, None)
}

/// Run `rustdoc` tests on a specific chapter of the book, linking against the provided libraries.
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a little unclear to me on what happens if chapter is None. Can it include a little more explanation?

Choose a reason for hiding this comment

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

Sure thing, I added clarifying comments // test_chapter with chapter:None will run all tests. and on the test_chapter function itself the doc comment: /// If chapterisNone, all tests will be run.

@lovettchris
Copy link

@ehuss I think I resolved all the excellent issues you raised.

Copy link
Contributor

@ehuss ehuss left a comment

Choose a reason for hiding this comment

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

Thanks! Just some code style suggestions.

src/book/mod.rs Outdated
Comment on lines 282 to 288
let cp = chapter_path.to_str();
let cn: Option<&str> = Some(&ch.name);

if chapter.is_some() && cn != chapter && cp != chapter {
info!("Skipping chapter '{}'...", ch.name);
continue;
};
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of using local variables like this, I still think it would be clearer to use something like the suggestion I posted before. The local variables create an indirection where you then have to refer to other parts of the code to figure out what they mean. Using the values directly I think makes it a little clearer as to what is being checked.

Also, after testing this out a bit, I feel like the output is overwhelmed with the "skipping chapter" text. I'm thinking it might be better to reduce that to a debug message to reduce the noise.

Perhaps something like this:

Suggested change
let cp = chapter_path.to_str();
let cn: Option<&str> = Some(&ch.name);
if chapter.is_some() && cn != chapter && cp != chapter {
info!("Skipping chapter '{}'...", ch.name);
continue;
};
if let Some(chapter) = chapter {
if ch.name != chapter && chapter_path.to_str() != Some(chapter) {
debug!("Skipping chapter '{}'...", ch.name);
continue;
}
}

Choose a reason for hiding this comment

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

The skipping chapter prompts are how you find the chapter names so you can write mdbook test -c ? for example then you find the chapter name you want to test and run the specific command. But I could make it so that it looks for the specific string "?" so it only prints in that case.

Choose a reason for hiding this comment

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

I pushed the if-check looking for command line arg "?" instead and I added that to the help text to make it discoverable.

Choose a reason for hiding this comment

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

Oh, and I see now how you are using if let Some to remove the need for local variables, so I pushed that fix too.

src/book/mod.rs Outdated
Comment on lines 332 to 334
if chapter.is_some() && !chapter_found {
bail!(format!("Chapter not found: {:?}", chapter));
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This produces some strange output. Generally it is recommended to not use debug displays for user output. Also, the bail! macro can take formatted strings directly, there is no need to use format!. I think it might be better to extract the name through a pattern and use a display impl. Something like this:

Suggested change
if chapter.is_some() && !chapter_found {
bail!(format!("Chapter not found: {:?}", chapter));
}
if let Some(chapter) = chapter {
if !chapter_found {
bail!("Chapter not found: {}", chapter);
}
}

Choose a reason for hiding this comment

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

Much better thanks, I merged your fix. I was just copying the use of bail from a few lines earlier: bail!("One or more tests failed");, so unless there is a better error formatter available, I'll leave the bail as you have written it.

Copy link
Contributor

@ehuss ehuss left a comment

Choose a reason for hiding this comment

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

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-author Status: The marked PR is awaiting some action (such as code changes) from the PR author.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants