1 // Copyright 2020 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::ops::Deref;
8 use std::path::{Path, PathBuf};
9 use std::thread::panicking;
10
11 use super::linux::{getpid, gettid};
12
13 /// Returns a stable path based on the label, pid, and tid. If the label isn't provided the
14 /// current_exe is used instead.
get_temp_path(label: Option<&str>) -> PathBuf15 pub fn get_temp_path(label: Option<&str>) -> PathBuf {
16 if let Some(label) = label {
17 temp_dir().join(format!("{}-{}-{}", label, getpid(), gettid()))
18 } else {
19 get_temp_path(Some(current_exe().unwrap().to_str().unwrap()))
20 }
21 }
22
23 /// Automatically deletes the path it contains when it goes out of scope unless it is a test and
24 /// drop is called after a panic!.
25 ///
26 /// This is particularly useful for creating temporary directories for use with tests.
27 pub struct ScopedPath<P: AsRef<Path>>(P);
28
29 impl<P: AsRef<Path>> ScopedPath<P> {
create(p: P) -> Result<Self, std::io::Error>30 pub fn create(p: P) -> Result<Self, std::io::Error> {
31 create_dir_all(p.as_ref())?;
32 Ok(ScopedPath(p))
33 }
34 }
35
36 impl<P: AsRef<Path>> AsRef<Path> for ScopedPath<P> {
as_ref(&self) -> &Path37 fn as_ref(&self) -> &Path {
38 self.0.as_ref()
39 }
40 }
41
42 impl<P: AsRef<Path>> Deref for ScopedPath<P> {
43 type Target = Path;
44
deref(&self) -> &Self::Target45 fn deref(&self) -> &Self::Target {
46 self.0.as_ref()
47 }
48 }
49
50 impl<P: AsRef<Path>> Drop for ScopedPath<P> {
drop(&mut self)51 fn drop(&mut self) {
52 // Leave the files on a failed test run for debugging.
53 if panicking() && cfg!(test) {
54 eprintln!("NOTE: Not removing {}", self.display());
55 return;
56 }
57 if let Err(e) = remove_dir_all(&**self) {
58 eprintln!("Failed to remove {}: {}", self.display(), e);
59 }
60 }
61 }
62
63 #[cfg(test)]
64 pub(crate) mod tests {
65 use super::*;
66
67 use std::panic::catch_unwind;
68
69 #[test]
gettemppath()70 fn gettemppath() {
71 assert_ne!("", get_temp_path(None).to_string_lossy());
72 assert_eq!(
73 get_temp_path(None),
74 get_temp_path(Some(current_exe().unwrap().to_str().unwrap()))
75 );
76 assert_ne!(
77 get_temp_path(Some("label")),
78 get_temp_path(Some(current_exe().unwrap().to_str().unwrap()))
79 );
80 }
81
82 #[test]
scopedpath_exists()83 fn scopedpath_exists() {
84 let tmp_path = get_temp_path(None);
85 {
86 let scoped_path = ScopedPath::create(&tmp_path).unwrap();
87 assert!(scoped_path.exists());
88 }
89 assert!(!tmp_path.exists());
90 }
91
92 #[test]
scopedpath_notexists()93 fn scopedpath_notexists() {
94 let tmp_path = get_temp_path(None);
95 {
96 let _scoped_path = ScopedPath(&tmp_path);
97 }
98 assert!(!tmp_path.exists());
99 }
100
101 #[test]
scopedpath_panic()102 fn scopedpath_panic() {
103 let tmp_path = get_temp_path(None);
104 assert!(catch_unwind(|| {
105 {
106 let scoped_path = ScopedPath::create(&tmp_path).unwrap();
107 assert!(scoped_path.exists());
108 panic!()
109 }
110 })
111 .is_err());
112 assert!(tmp_path.exists());
113 remove_dir_all(&tmp_path).unwrap();
114 }
115 }
116