• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std::env::{current_exe, temp_dir};
6 use std::fs::{create_dir_all, remove_dir_all};
7 use std::io::Result;
8 use std::ops::Deref;
9 use std::path::{Path, PathBuf};
10 use std::thread::panicking;
11 
12 use crate::{getpid, gettid};
13 
14 /// Returns a stable path based on the label, pid, and tid. If the label isn't provided the
15 /// current_exe is used instead.
get_temp_path(label: Option<&str>) -> PathBuf16 pub fn get_temp_path(label: Option<&str>) -> PathBuf {
17     if let Some(label) = label {
18         temp_dir().join(format!("{}-{}-{}", label, getpid(), gettid()))
19     } else {
20         get_temp_path(Some(
21             current_exe()
22                 .unwrap()
23                 .file_name()
24                 .unwrap()
25                 .to_str()
26                 .unwrap(),
27         ))
28     }
29 }
30 
31 /// Automatically deletes the path it contains when it goes out of scope unless it is a test and
32 /// drop is called after a panic!.
33 ///
34 /// This is particularly useful for creating temporary directories for use with tests.
35 pub struct ScopedPath<P: AsRef<Path>>(P);
36 
37 impl<P: AsRef<Path>> ScopedPath<P> {
create(p: P) -> Result<Self>38     pub fn create(p: P) -> Result<Self> {
39         create_dir_all(p.as_ref())?;
40         Ok(ScopedPath(p))
41     }
42 }
43 
44 impl<P: AsRef<Path>> AsRef<Path> for ScopedPath<P> {
as_ref(&self) -> &Path45     fn as_ref(&self) -> &Path {
46         self.0.as_ref()
47     }
48 }
49 
50 impl<P: AsRef<Path>> Deref for ScopedPath<P> {
51     type Target = Path;
52 
deref(&self) -> &Self::Target53     fn deref(&self) -> &Self::Target {
54         self.0.as_ref()
55     }
56 }
57 
58 impl<P: AsRef<Path>> Drop for ScopedPath<P> {
drop(&mut self)59     fn drop(&mut self) {
60         // Leave the files on a failed test run for debugging.
61         if panicking() && cfg!(test) {
62             eprintln!("NOTE: Not removing {}", self.display());
63             return;
64         }
65         if let Err(e) = remove_dir_all(&**self) {
66             eprintln!("Failed to remove {}: {}", self.display(), e);
67         }
68     }
69 }
70 
71 #[cfg(test)]
72 pub(crate) mod tests {
73     use super::*;
74 
75     use std::panic::catch_unwind;
76 
77     #[test]
gettemppath()78     fn gettemppath() {
79         assert_ne!("", get_temp_path(None).to_string_lossy());
80         assert!(get_temp_path(None).starts_with(temp_dir()));
81         assert_eq!(
82             get_temp_path(None),
83             get_temp_path(Some(
84                 current_exe()
85                     .unwrap()
86                     .file_name()
87                     .unwrap()
88                     .to_str()
89                     .unwrap()
90             ))
91         );
92         assert_ne!(
93             get_temp_path(Some("label")),
94             get_temp_path(Some(
95                 current_exe()
96                     .unwrap()
97                     .file_name()
98                     .unwrap()
99                     .to_str()
100                     .unwrap()
101             ))
102         );
103     }
104 
105     #[test]
scopedpath_exists()106     fn scopedpath_exists() {
107         let tmp_path = get_temp_path(None);
108         {
109             let scoped_path = ScopedPath::create(&tmp_path).unwrap();
110             assert!(scoped_path.exists());
111         }
112         assert!(!tmp_path.exists());
113     }
114 
115     #[test]
scopedpath_notexists()116     fn scopedpath_notexists() {
117         let tmp_path = get_temp_path(None);
118         {
119             let _scoped_path = ScopedPath(&tmp_path);
120         }
121         assert!(!tmp_path.exists());
122     }
123 
124     #[test]
scopedpath_panic()125     fn scopedpath_panic() {
126         let tmp_path = get_temp_path(None);
127         assert!(catch_unwind(|| {
128             {
129                 let scoped_path = ScopedPath::create(&tmp_path).unwrap();
130                 assert!(scoped_path.exists());
131                 panic!()
132             }
133         })
134         .is_err());
135         assert!(tmp_path.exists());
136         remove_dir_all(&tmp_path).unwrap();
137     }
138 }
139