1 // Copyright 2020, The Android Open Source Project 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 //! Implements TempDir which aids in creating an cleaning up temporary directories for testing. 16 17 use std::fs::{create_dir, remove_dir_all}; 18 use std::io::ErrorKind; 19 use std::path::{Path, PathBuf}; 20 use std::{env::temp_dir, ops::Deref}; 21 22 /// Represents the lifecycle of a temporary directory for testing. 23 #[derive(Debug)] 24 pub struct TempDir { 25 path: std::path::PathBuf, 26 do_drop: bool, 27 } 28 29 impl TempDir { 30 /// Creates a temporary directory with a name of the form <prefix>_NNNNN where NNNNN is a zero 31 /// padded random number with 5 figures. The prefix must not contain file system separators. 32 /// The location of the directory cannot be chosen. 33 /// The directory with all of its content is removed from the file system when the resulting 34 /// object gets dropped. new(prefix: &str) -> std::io::Result<Self>35 pub fn new(prefix: &str) -> std::io::Result<Self> { 36 let tmp = loop { 37 let mut tmp = temp_dir(); 38 let number: u16 = rand::random(); 39 tmp.push(format!("{}_{:05}", prefix, number)); 40 match create_dir(&tmp) { 41 Err(e) => match e.kind() { 42 ErrorKind::AlreadyExists => continue, 43 _ => return Err(e), 44 }, 45 Ok(()) => break tmp, 46 } 47 }; 48 Ok(Self { path: tmp, do_drop: true }) 49 } 50 51 /// Returns the absolute path of the temporary directory. path(&self) -> &Path52 pub fn path(&self) -> &Path { 53 &self.path 54 } 55 56 /// Returns a path builder for convenient extension of the path. 57 /// 58 /// ## Example: 59 /// 60 /// ``` 61 /// let tdir = TempDir::new("my_test")?; 62 /// let temp_foo_bar = tdir.build().push("foo").push("bar"); 63 /// ``` 64 /// `temp_foo_bar` derefs to a Path that represents "<tdir.path()>/foo/bar" build(&self) -> PathBuilder65 pub fn build(&self) -> PathBuilder { 66 PathBuilder(self.path.clone()) 67 } 68 69 /// When a test is failing you can set this to false in order to inspect 70 /// the directory structure after the test failed. 71 #[allow(dead_code)] do_not_drop(&mut self)72 pub fn do_not_drop(&mut self) { 73 println!("Disabled automatic cleanup for: {:?}", self.path); 74 log::info!("Disabled automatic cleanup for: {:?}", self.path); 75 self.do_drop = false; 76 } 77 } 78 79 impl Drop for TempDir { drop(&mut self)80 fn drop(&mut self) { 81 if self.do_drop { 82 remove_dir_all(&self.path).expect("Cannot delete temporary dir."); 83 } 84 } 85 } 86 87 /// Allows for convenient building of paths from a TempDir. See TempDir.build() for more details. 88 pub struct PathBuilder(PathBuf); 89 90 impl PathBuilder { 91 /// Adds another segment to the end of the path. Consumes, modifies and returns self. push(mut self, segment: &str) -> Self92 pub fn push(mut self, segment: &str) -> Self { 93 self.0.push(segment); 94 self 95 } 96 } 97 98 impl Deref for PathBuilder { 99 type Target = Path; 100 deref(&self) -> &Self::Target101 fn deref(&self) -> &Self::Target { 102 &self.0 103 } 104 } 105