@@ -6,6 +6,7 @@ use std::path::{
66 PathBuf ,
77} ;
88
9+ use super :: path:: canonicalize_path_sys;
910use super :: providers:: {
1011 CwdProvider ,
1112 EnvProvider ,
@@ -37,10 +38,11 @@ impl TestDir {
3738 /// Writes the given file under the test directory. Creates parent directories if needed.
3839 ///
3940 /// The path given by `file` is *not* canonicalized.
41+ #[ deprecated]
4042 pub async fn with_file ( self , file : impl TestFile ) -> Self {
4143 let file_path = file. path ( ) ;
42- if file_path. is_absolute ( ) {
43- panic ! ( "absolute paths are currently not supported " ) ;
44+ if file_path. is_absolute ( ) && !file_path . starts_with ( self . temp_dir . path ( ) ) {
45+ panic ! ( "path falls outside of the temp dir " ) ;
4446 }
4547
4648 let path = self . temp_dir . path ( ) . join ( file_path) ;
@@ -52,6 +54,28 @@ impl TestDir {
5254 tokio:: fs:: write ( path, file. content ( ) ) . await . unwrap ( ) ;
5355 self
5456 }
57+
58+ /// Writes the given file under the test directory. Creates parent directories if needed.
59+ ///
60+ /// This function panics if the file path is outside of the test directory.
61+ pub async fn with_file_sys < P : SystemProvider > ( self , file : impl TestFile , provider : & P ) -> Self {
62+ let file_path = canonicalize_path_sys ( file. path ( ) . to_string_lossy ( ) , provider) . unwrap ( ) ;
63+
64+ // Check to ensure that the file path resolves under the test directory.
65+ if !file_path. starts_with ( & self . temp_dir . path ( ) . to_string_lossy ( ) . to_string ( ) ) {
66+ panic ! ( "outside of temp dir" ) ;
67+ }
68+
69+ let file_path = PathBuf :: from ( file_path) ;
70+ if let Some ( parent) = file_path. parent ( ) {
71+ if !parent. exists ( ) {
72+ tokio:: fs:: create_dir_all ( parent) . await . unwrap ( ) ;
73+ }
74+ }
75+
76+ tokio:: fs:: write ( file_path, file. content ( ) ) . await . unwrap ( ) ;
77+ self
78+ }
5579}
5680
5781impl Default for TestDir {
@@ -79,6 +103,16 @@ where
79103 }
80104}
81105
106+ impl TestFile for Box < dyn TestFile > {
107+ fn path ( & self ) -> PathBuf {
108+ ( * * self ) . path ( )
109+ }
110+
111+ fn content ( & self ) -> Vec < u8 > {
112+ ( * * self ) . content ( )
113+ }
114+ }
115+
82116/// Test helper that implements [EnvProvider], [HomeProvider], and [CwdProvider].
83117#[ derive( Debug , Clone ) ]
84118pub struct TestProvider {
@@ -161,3 +195,34 @@ impl CwdProvider for TestProvider {
161195}
162196
163197impl SystemProvider for TestProvider { }
198+
199+ #[ cfg( test) ]
200+ mod tests {
201+ use tokio:: fs;
202+
203+ use super :: * ;
204+
205+ #[ tokio:: test]
206+ async fn test_tempdir_files ( ) {
207+ let mut test_dir = TestDir :: new ( ) ;
208+ let test_provider = TestProvider :: new_with_base ( test_dir. path ( ) ) ;
209+
210+ let files = [ ( "base" , "base" ) , ( "~/tilde" , "tilde" ) , ( "$HOME/home" , "home" ) ] ;
211+ for file in files {
212+ test_dir = test_dir. with_file_sys ( file, & test_provider) . await ;
213+ }
214+
215+ assert_eq ! ( fs:: read_to_string( test_dir. join( "base" ) ) . await . unwrap( ) , "base" ) ;
216+ assert_eq ! ( fs:: read_to_string( test_dir. join( "tilde" ) ) . await . unwrap( ) , "tilde" ) ;
217+ assert_eq ! ( fs:: read_to_string( test_dir. join( "home" ) ) . await . unwrap( ) , "home" ) ;
218+ }
219+
220+ #[ tokio:: test]
221+ #[ should_panic]
222+ async fn test_tempdir_write_file_outside ( ) {
223+ let test_dir = TestDir :: new ( ) ;
224+ let test_provider = TestProvider :: new_with_base ( test_dir. path ( ) ) ;
225+
226+ let _ = test_dir. with_file_sys ( ( ".." , "hello" ) , & test_provider) . await ;
227+ }
228+ }
0 commit comments