11//! Utility for building and running tests against mdbook.
22
3+ use anyhow:: Context ;
34use mdbook_driver:: MDBook ;
45use mdbook_driver:: init:: BookBuilder ;
56use snapbox:: IntoData ;
@@ -91,6 +92,7 @@ impl BookTest {
9192 ///
9293 /// Normally the contents outside of the `<main>` tag aren't interesting,
9394 /// and they add a significant amount of noise.
95+ #[ track_caller]
9496 pub fn check_main_file ( & mut self , path : & str , expected : impl IntoData ) -> & mut Self {
9597 if !self . built {
9698 self . build ( ) ;
@@ -107,6 +109,7 @@ impl BookTest {
107109 }
108110
109111 /// Checks the summary contents of `toc.js` against the expected value.
112+ #[ track_caller]
110113 pub fn check_toc_js ( & mut self , expected : impl IntoData ) -> & mut Self {
111114 if !self . built {
112115 self . build ( ) ;
@@ -119,6 +122,7 @@ impl BookTest {
119122 }
120123
121124 /// Returns the summary contents from `toc.js`.
125+ #[ track_caller]
122126 pub fn toc_js_html ( & self ) -> String {
123127 let full_path = self . dir . join ( "book/toc.js" ) ;
124128 let actual = read_to_string ( & full_path) ;
@@ -135,27 +139,31 @@ impl BookTest {
135139 }
136140
137141 /// Checks that the contents of the given file matches the expected value.
138- pub fn check_file ( & mut self , path : & str , expected : impl IntoData ) -> & mut Self {
142+ ///
143+ /// The path can use glob-style wildcards, but it must match only a single file.
144+ #[ track_caller]
145+ pub fn check_file ( & mut self , path_pattern : & str , expected : impl IntoData ) -> & mut Self {
139146 if !self . built {
140147 self . build ( ) ;
141148 }
142- let path = self . dir . join ( path ) ;
149+ let path = glob_one ( & self . dir , path_pattern ) ;
143150 let actual = read_to_string ( & path) ;
144151 self . assert . eq ( actual, expected) ;
145152 self
146153 }
147154
148- /// Checks that the given file contains the given string somewhere.
149- pub fn check_file_contains ( & mut self , path : & str , expected : & str ) -> & mut Self {
155+ /// Checks that the given file contains the given [`snapbox::Assert`] pattern somewhere.
156+ ///
157+ /// The path can use glob-style wildcards, but it must match only a single file.
158+ #[ track_caller]
159+ pub fn check_file_contains ( & mut self , path_pattern : & str , expected : & str ) -> & mut Self {
150160 if !self . built {
151161 self . build ( ) ;
152162 }
153- let path = self . dir . join ( path ) ;
163+ let path = glob_one ( & self . dir , path_pattern ) ;
154164 let actual = read_to_string ( & path) ;
155- assert ! (
156- actual. contains( expected) ,
157- "Did not find {expected:?} in {path:?}\n \n {actual}" ,
158- ) ;
165+ let expected = format ! ( "...\n [..]{expected}[..]\n ...\n " ) ;
166+ self . assert . eq ( actual, expected) ;
159167 self
160168 }
161169
@@ -164,11 +172,14 @@ impl BookTest {
164172 /// Beware that using this is fragile, as it may be unable to catch
165173 /// regressions (it can't tell the difference between success, or the
166174 /// string being looked for changed).
167- pub fn check_file_doesnt_contain ( & mut self , path : & str , string : & str ) -> & mut Self {
175+ ///
176+ /// The path can use glob-style wildcards, but it must match only a single file.
177+ #[ track_caller]
178+ pub fn check_file_doesnt_contain ( & mut self , path_pattern : & str , string : & str ) -> & mut Self {
168179 if !self . built {
169180 self . build ( ) ;
170181 }
171- let path = self . dir . join ( path ) ;
182+ let path = glob_one ( & self . dir , path_pattern ) ;
172183 let actual = read_to_string ( & path) ;
173184 assert ! (
174185 !actual. contains( string) ,
@@ -178,6 +189,7 @@ impl BookTest {
178189 }
179190
180191 /// Checks that the list of files at the given path matches the given value.
192+ #[ track_caller]
181193 pub fn check_file_list ( & mut self , path : & str , expected : impl IntoData ) -> & mut Self {
182194 let mut all_paths: Vec < _ > = walkdir:: WalkDir :: new ( & self . dir . join ( path) )
183195 . into_iter ( )
@@ -499,5 +511,25 @@ fn assert(root: &Path) -> snapbox::Assert {
499511#[ track_caller]
500512pub fn read_to_string < P : AsRef < Path > > ( path : P ) -> String {
501513 let path = path. as_ref ( ) ;
502- std:: fs:: read_to_string ( path) . unwrap_or_else ( |e| panic ! ( "could not read file {path:?}: {e:?}" ) )
514+ std:: fs:: read_to_string ( path)
515+ . with_context ( || format ! ( "could not read file {path:?}" ) )
516+ . unwrap ( )
517+ }
518+
519+ /// Returns the first path from the given glob pattern.
520+ pub fn glob_one < P : AsRef < Path > > ( path : P , pattern : & str ) -> PathBuf {
521+ let path = path. as_ref ( ) ;
522+ let mut matches = glob:: glob ( path. join ( pattern) . to_str ( ) . unwrap ( ) ) . unwrap ( ) ;
523+ let Some ( first) = matches. next ( ) else {
524+ panic ! ( "expected at least one file at `{path:?}` with pattern `{pattern}`, found none" ) ;
525+ } ;
526+ let first = first. unwrap ( ) ;
527+ if let Some ( next) = matches. next ( ) {
528+ panic ! (
529+ "expected only one file for pattern `{pattern}` in `{path:?}`, \
530+ found `{first:?}` and `{:?}`",
531+ next. unwrap( )
532+ ) ;
533+ }
534+ first
503535}
0 commit comments