@@ -191,6 +191,12 @@ pub fn blame_line(
191191 } )
192192}
193193
194+ // attributes on expressions are not allowed
195+ // however, in our macro its possible that sometimes the
196+ // assignment to the mutable variable will not be read.
197+ //
198+ // when the last line has no expected blame commit
199+ #[ allow( unused_assignments) ]
194200#[ cfg( test) ]
195201mod test {
196202 use std:: fs:: File ;
@@ -199,75 +205,144 @@ mod test {
199205
200206 use super :: * ;
201207
202- macro_rules! assert_blamed_lines {
203- ( $repo: ident, $file: ident @ $( $commit_msg: literal => $( $line: literal $expected: literal) ,+) ;+ $( ; ) ?) => { {
208+ macro_rules! no_commit_flag {
209+ ( no_commit, $commit_msg: literal) => {
210+ false
211+ } ;
212+ ( , $commit_msg: literal) => {
213+ true
214+ } ;
215+ ( $any: tt, $commit_msg: literal) => {
216+ compile_error!( concat!(
217+ "expected no_commit or nothing for commit " ,
218+ $commit_msg
219+ ) )
220+ } ;
221+ }
222+
223+ macro_rules! add_flag {
224+ ( add, $commit_msg: literal, $line: expr) => {
225+ true
226+ } ;
227+ ( , $commit_msg: literal, $line: expr) => {
228+ false
229+ } ;
230+ ( $any: tt, $commit_msg: literal, $line: expr) => {
231+ compile_error!( concat!(
232+ "expected no_commit or nothing for commit " ,
233+ $commit_msg,
234+ " line " ,
235+ $line
236+ ) )
237+ } ;
238+ }
239+
240+ /// Helper macro to create a history of the same file being modified.
241+ ///
242+ /// Each $commit_msg is a unique identifier for a commit message.
243+ /// Each $line is a string line of the file. These $lines are collected into a single String
244+ /// which then becomes the new contents of the $file
245+ ///
246+ /// Each $line gets blamed using blame_line. The $expected is the commit identifier that we are expecting for that line.
247+ macro_rules! assert_line_blame_progress {
248+ ( $( $commit_msg: literal $( $no_commit: ident) ? => $( $line: literal $( $expected: literal) ? $( $added: ident) ? ) ,+) ;+ $( ; ) ?) => { {
204249 use std:: fs:: OpenOptions ;
205250 use std:: io:: Write ;
206251
252+ let repo = empty_git_repo( ) ;
253+ let file = repo. path( ) . join( "file.txt" ) ;
254+ File :: create( & file) . expect( "could not create file" ) ;
255+
207256 let write_file = |content: & str | {
208257 let mut f = OpenOptions :: new( )
209258 . write( true )
210259 . truncate( true )
211- . open( & $ file)
260+ . open( & file)
212261 . unwrap( ) ;
213262 f. write_all( content. as_bytes( ) ) . unwrap( ) ;
214263 } ;
215264
216- let commit = |msg| create_commit_with_message( $ repo. path( ) , true , msg) ;
265+ let commit = |msg| create_commit_with_message( repo. path( ) , true , msg) ;
217266
218267 $(
219268 let file_content = concat!( $( $line, "\n " ) ,* ) ;
269+ eprintln!( "at commit {}:\n \n {file_content}" , stringify!( $commit_msg) ) ;
220270 write_file( file_content) ;
221- commit( stringify!( $commit_msg) ) ;
271+
272+ let should_commit = no_commit_flag!( $( $no_commit) ?, $commit_msg) ;
273+ if should_commit {
274+ commit( stringify!( $commit_msg) ) ;
275+ }
222276
223277 let mut line_number = 0 ;
278+ let mut added_lines = 0 ;
224279
225280 $(
226281 line_number += 1 ;
227- let blame_result = blame_line( & $file, line_number, 0 , 0 ) . unwrap( ) . commit_message;
228- assert_eq!(
229- blame_result,
230- Some ( concat!( stringify!( $expected) , "\n " ) . to_owned( ) ) ,
231- "Blame mismatch at line {}: expected '{}', got {:?}" ,
232- line_number,
233- stringify!( $expected) ,
234- blame_result
235- ) ;
282+ let has_add_flag = add_flag!( $( $added) ?, $commit_msg, $line) ;
283+ if has_add_flag {
284+ added_lines += 1 ;
285+ }
286+ // if there is no $expected, then we don't care what blame_line returns
287+ // because we won't show it to the user.
288+ $(
289+
290+ let blame_result = blame_line( & file, line_number, added_lines, 0 ) . unwrap( ) . commit_message;
291+ assert_eq!(
292+ blame_result,
293+ Some ( concat!( stringify!( $expected) , "\n " ) . to_owned( ) ) ,
294+ "Blame mismatch\n at commit: {}\n at line: {}\n line contents: {}\n expected commit: {}\n but got commit: {}" ,
295+ $commit_msg,
296+ line_number,
297+ file_content. lines( ) . nth( line_number. try_into( ) . unwrap( ) ) . unwrap( ) ,
298+ stringify!( $expected) ,
299+ blame_result. as_ref( ) . map( |blame| blame. trim_end( ) ) . unwrap_or( "<no commit>" )
300+ ) ;
301+ ) ?
236302 ) *
237303 ) *
238304 } } ;
239305 }
240306
241- fn bob ( ) -> BlameInformation {
242- BlameInformation {
243- commit_hash : Some ( "f14ab1cf" . to_owned ( ) ) ,
244- author_name : Some ( "Bob TheBuilder" . to_owned ( ) ) ,
245- author_email : Some ( "bob@bob.com" . to_owned ( ) ) ,
246- commit_date : Some ( "2028-01-10" . to_owned ( ) ) ,
247- commit_message : Some ( "feat!: extend house" . to_owned ( ) ) ,
248- commit_body : Some ( "BREAKING CHANGE: Removed door" . to_owned ( ) ) ,
249- }
250- }
251-
252307 #[ test]
253- pub fn blame_lin ( ) {
254- let repo = empty_git_repo ( ) ;
255- let file = repo. path ( ) . join ( "file.txt" ) ;
256- File :: create ( & file) . unwrap ( ) ;
257-
258- assert_blamed_lines ! {
259- repo, file @
308+ pub fn blamed_lines ( ) {
309+ assert_line_blame_progress ! {
260310 1 =>
261311 "fn main() {" 1 ,
262312 "" 1 ,
263313 "}" 1 ;
264314 2 =>
265315 "fn main() {" 1 ,
266- " lol" 2 ,
316+ " one" 2 ,
317+ "}" 1 ;
318+ 3 =>
319+ "fn main() {" 1 ,
320+ " one" 2 ,
321+ " two" 3 ,
322+ "}" 1 ;
323+ 4 =>
324+ "fn main() {" 1 ,
325+ " two" 3 ,
326+ "}" 1 ;
327+ 5 no_commit =>
328+ "fn main() {" 1 ,
329+ " hello world" add,
330+ " two" 3 ,
267331 "}" 1 ;
268332 } ;
269333 }
270334
335+ fn bob ( ) -> BlameInformation {
336+ BlameInformation {
337+ commit_hash : Some ( "f14ab1cf" . to_owned ( ) ) ,
338+ author_name : Some ( "Bob TheBuilder" . to_owned ( ) ) ,
339+ author_email : Some ( "bob@bob.com" . to_owned ( ) ) ,
340+ commit_date : Some ( "2028-01-10" . to_owned ( ) ) ,
341+ commit_message : Some ( "feat!: extend house" . to_owned ( ) ) ,
342+ commit_body : Some ( "BREAKING CHANGE: Removed door" . to_owned ( ) ) ,
343+ }
344+ }
345+
271346 #[ test]
272347 pub fn inline_blame_format_parser ( ) {
273348 let default_values = "{author}, {date} • {message} • {commit}" ;
0 commit comments