1
+ //! Utilities for testing `gitoxide` crates, many of which might be useful for testing programs that use `git` in general.
2
+ #![ deny( missing_docs) ]
3
+ use std:: ffi:: OsString ;
1
4
use std:: {
2
5
collections:: BTreeMap ,
3
6
convert:: Infallible ,
@@ -15,6 +18,20 @@ use once_cell::sync::Lazy;
15
18
use parking_lot:: Mutex ;
16
19
pub use tempfile;
17
20
21
+ /// A result type to allow using the try operator `?` in unit tests.
22
+ ///
23
+ /// Use it like so:
24
+ ///
25
+ /// ```norun
26
+ /// use git_testtools::Result;
27
+ ///
28
+ /// #[test]
29
+ /// fn this() -> Result {
30
+ /// let x: usize = "42".parse()?;
31
+ /// Ok(())
32
+ ///
33
+ /// }
34
+ /// ```
18
35
pub type Result < T = ( ) > = std:: result:: Result < T , Box < dyn std:: error:: Error > > ;
19
36
20
37
static SCRIPT_IDENTITY : Lazy < Mutex < BTreeMap < PathBuf , u32 > > > = Lazy :: new ( || Mutex :: new ( BTreeMap :: new ( ) ) ) ;
@@ -46,49 +63,58 @@ static EXCLUDE_LUT: Lazy<Mutex<Option<git_worktree::fs::Cache<'static>>>> = Lazy
46
63
Mutex :: new ( cache)
47
64
} ) ;
48
65
66
+ /// Define how [scripted_fixture_repo_writable_with_args()] uses produces the writable copy.
49
67
pub enum Creation {
68
+ /// Run the script once and copy the data from its output to the writable location.
69
+ /// This is fast but won't work if absolute paths are produced by the script.
50
70
CopyFromReadOnly ,
71
+ /// Run the script in the writable location. That way, absolute paths match the location.
51
72
ExecuteScript ,
52
73
}
53
74
75
+ /// Run `git` in `working_dir` with all provided `args`.
54
76
pub fn run_git ( working_dir : & Path , args : & [ & str ] ) -> std:: io:: Result < std:: process:: ExitStatus > {
55
77
std:: process:: Command :: new ( "git" )
56
78
. current_dir ( working_dir)
57
79
. args ( args)
58
80
. status ( )
59
81
}
60
82
83
+ /// Convert a hexadecimal hash into its corresponding `ObjectId` or _panic_.
61
84
pub fn hex_to_id ( hex : & str ) -> git_hash:: ObjectId {
62
85
git_hash:: ObjectId :: from_hex ( hex. as_bytes ( ) ) . expect ( "40 bytes hex" )
63
86
}
64
87
88
+ /// Return the path to the `<crate-root>/tests/fixtures/<path>` directory.
65
89
pub fn fixture_path ( path : impl AsRef < Path > ) -> PathBuf {
66
90
PathBuf :: from ( "tests" ) . join ( "fixtures" ) . join ( path. as_ref ( ) )
67
91
}
68
92
69
- pub fn crate_under_test ( ) -> String {
70
- std:: env:: current_dir ( )
71
- . expect ( "CWD is valid" )
72
- . file_name ( )
73
- . expect ( "typical cargo invocation" )
74
- . to_string_lossy ( )
75
- . into_owned ( )
76
- }
77
-
93
+ /// Load the fixture from `<crate-root>/tests/fixtures/<path>` and return its data, or _panic_.
78
94
pub fn fixture_bytes ( path : impl AsRef < Path > ) -> Vec < u8 > {
79
95
match std:: fs:: read ( fixture_path ( path. as_ref ( ) ) ) {
80
96
Ok ( res) => res,
81
97
Err ( _) => panic ! ( "File at '{}' not found" , path. as_ref( ) . display( ) ) ,
82
98
}
83
99
}
100
+
101
+ /// Run the executable at `script_name`, like `make_repo.sh` to produce a read-only directory to which
102
+ /// the path is returned.
103
+ /// Note that it persists and the script at `script_name` will only be executed once if it ran without error.
84
104
pub fn scripted_fixture_repo_read_only ( script_name : impl AsRef < Path > ) -> Result < PathBuf > {
85
105
scripted_fixture_repo_read_only_with_args ( script_name, None )
86
106
}
87
107
108
+ /// Run the executable at `script_name`, like `make_repo.sh` to produce a writable directory to which
109
+ /// the tempdir is returned. It will be removed automatically, courtesy of [`tempfile::TempDir`].
110
+ ///
111
+ /// Note that `script_name` is only executed once, so the data can be copied from its read-only location.
88
112
pub fn scripted_fixture_repo_writable ( script_name : & str ) -> Result < tempfile:: TempDir > {
89
113
scripted_fixture_repo_writable_with_args ( script_name, None , Creation :: CopyFromReadOnly )
90
114
}
91
115
116
+ /// Like [`scripted_fixture_repo_writable()`], but passes `args` to `script_name` while providing control over
117
+ /// the way files are created with `mode`.
92
118
pub fn scripted_fixture_repo_writable_with_args (
93
119
script_name : & str ,
94
120
args : impl IntoIterator < Item = & ' static str > ,
@@ -108,6 +134,7 @@ pub fn scripted_fixture_repo_writable_with_args(
108
134
} )
109
135
}
110
136
137
+ /// A utility to copy the entire contents of `src_dir` into `dst_dir`.
111
138
pub fn copy_recursively_into_existing_dir ( src_dir : impl AsRef < Path > , dst_dir : impl AsRef < Path > ) -> std:: io:: Result < ( ) > {
112
139
fs_extra:: copy_items (
113
140
& std:: fs:: read_dir ( src_dir) ?
@@ -125,7 +152,8 @@ pub fn copy_recursively_into_existing_dir(src_dir: impl AsRef<Path>, dst_dir: im
125
152
. map_err ( |err| std:: io:: Error :: new ( std:: io:: ErrorKind :: Other , err) ) ?;
126
153
Ok ( ( ) )
127
154
}
128
- /// Returns the directory at which the data is present
155
+
156
+ /// Like `scripted_fixture_repo_read_only()`], but passes `args` to `script_name`.
129
157
pub fn scripted_fixture_repo_read_only_with_args (
130
158
script_name : impl AsRef < Path > ,
131
159
args : impl IntoIterator < Item = & ' static str > ,
@@ -387,6 +415,7 @@ fn extract_archive(
387
415
Ok ( ( archive_identity, platform) )
388
416
}
389
417
418
+ /// Transform a verbose bom errors from raw bytes into a `BStr` to make printing/debugging human-readable.
390
419
pub fn to_bstr_err ( err : nom:: Err < VerboseError < & [ u8 ] > > ) -> VerboseError < & BStr > {
391
420
let err = match err {
392
421
nom:: Err :: Error ( err) | nom:: Err :: Failure ( err) => err,
@@ -405,35 +434,36 @@ fn family_name() -> &'static str {
405
434
}
406
435
}
407
436
408
- pub fn sleep_forever ( ) -> ! {
409
- loop {
410
- std:: thread:: sleep ( std:: time:: Duration :: from_secs ( u64:: MAX ) )
411
- }
412
- }
413
-
437
+ /// A utility to set environment variables, while unsetting them (or resetting them to their previous value) on drop.
414
438
#[ derive( Default ) ]
415
439
pub struct Env < ' a > {
416
- altered_vars : Vec < & ' a str > ,
440
+ altered_vars : Vec < ( & ' a str , Option < OsString > ) > ,
417
441
}
418
442
419
443
impl < ' a > Env < ' a > {
444
+ /// Create a new instance.
420
445
pub fn new ( ) -> Self {
421
446
Env {
422
447
altered_vars : Vec :: new ( ) ,
423
448
}
424
449
}
425
450
451
+ /// Set `var` to `value`.
426
452
pub fn set ( mut self , var : & ' a str , value : impl Into < String > ) -> Self {
453
+ let prev = std:: env:: var_os ( var) ;
427
454
std:: env:: set_var ( var, value. into ( ) ) ;
428
- self . altered_vars . push ( var) ;
455
+ self . altered_vars . push ( ( var, prev ) ) ;
429
456
self
430
457
}
431
458
}
432
459
433
460
impl < ' a > Drop for Env < ' a > {
434
461
fn drop ( & mut self ) {
435
- for var in & self . altered_vars {
436
- std:: env:: remove_var ( var) ;
462
+ for ( var, prev_value) in & self . altered_vars {
463
+ match prev_value {
464
+ Some ( value) => std:: env:: set_var ( var, value) ,
465
+ None => std:: env:: remove_var ( var) ,
466
+ }
437
467
}
438
468
}
439
469
}
0 commit comments