| 1 | #![deny (rust_2018_idioms)] |
| 2 | |
| 3 | use std::env; |
| 4 | use std::ffi::{OsStr, OsString}; |
| 5 | use std::fs::File; |
| 6 | use std::io::{Read, Seek, SeekFrom, Write}; |
| 7 | use std::path::{Path, PathBuf}; |
| 8 | use tempfile::{tempdir, Builder, NamedTempFile, TempPath}; |
| 9 | |
| 10 | fn exists<P: AsRef<Path>>(path: P) -> bool { |
| 11 | std::fs::metadata(path.as_ref()).is_ok() |
| 12 | } |
| 13 | |
| 14 | #[test] |
| 15 | fn test_prefix() { |
| 16 | let tmpfile = NamedTempFile::with_prefix("prefix" ).unwrap(); |
| 17 | let name = tmpfile.path().file_name().unwrap().to_str().unwrap(); |
| 18 | assert!(name.starts_with("prefix" )); |
| 19 | } |
| 20 | |
| 21 | #[test] |
| 22 | fn test_basic() { |
| 23 | let mut tmpfile = NamedTempFile::new().unwrap(); |
| 24 | write!(tmpfile, "abcde" ).unwrap(); |
| 25 | tmpfile.seek(SeekFrom::Start(0)).unwrap(); |
| 26 | let mut buf = String::new(); |
| 27 | tmpfile.read_to_string(&mut buf).unwrap(); |
| 28 | assert_eq!("abcde" , buf); |
| 29 | } |
| 30 | |
| 31 | #[test] |
| 32 | fn test_deleted() { |
| 33 | let tmpfile = NamedTempFile::new().unwrap(); |
| 34 | let path = tmpfile.path().to_path_buf(); |
| 35 | assert!(exists(&path)); |
| 36 | drop(tmpfile); |
| 37 | assert!(!exists(&path)); |
| 38 | } |
| 39 | |
| 40 | #[test] |
| 41 | fn test_persist() { |
| 42 | let mut tmpfile = NamedTempFile::new().unwrap(); |
| 43 | let old_path = tmpfile.path().to_path_buf(); |
| 44 | let persist_path = env::temp_dir().join("persisted_temporary_file" ); |
| 45 | write!(tmpfile, "abcde" ).unwrap(); |
| 46 | { |
| 47 | assert!(exists(&old_path)); |
| 48 | let mut f = tmpfile.persist(&persist_path).unwrap(); |
| 49 | assert!(!exists(&old_path)); |
| 50 | |
| 51 | // Check original file |
| 52 | f.seek(SeekFrom::Start(0)).unwrap(); |
| 53 | let mut buf = String::new(); |
| 54 | f.read_to_string(&mut buf).unwrap(); |
| 55 | assert_eq!("abcde" , buf); |
| 56 | } |
| 57 | |
| 58 | { |
| 59 | // Try opening it at the new path. |
| 60 | let mut f = File::open(&persist_path).unwrap(); |
| 61 | f.seek(SeekFrom::Start(0)).unwrap(); |
| 62 | let mut buf = String::new(); |
| 63 | f.read_to_string(&mut buf).unwrap(); |
| 64 | assert_eq!("abcde" , buf); |
| 65 | } |
| 66 | std::fs::remove_file(&persist_path).unwrap(); |
| 67 | } |
| 68 | |
| 69 | #[test] |
| 70 | fn test_persist_noclobber() { |
| 71 | let mut tmpfile = NamedTempFile::new().unwrap(); |
| 72 | let old_path = tmpfile.path().to_path_buf(); |
| 73 | let persist_target = NamedTempFile::new().unwrap(); |
| 74 | let persist_path = persist_target.path().to_path_buf(); |
| 75 | write!(tmpfile, "abcde" ).unwrap(); |
| 76 | assert!(exists(&old_path)); |
| 77 | { |
| 78 | tmpfile = tmpfile.persist_noclobber(&persist_path).unwrap_err().into(); |
| 79 | assert!(exists(&old_path)); |
| 80 | std::fs::remove_file(&persist_path).unwrap(); |
| 81 | drop(persist_target); |
| 82 | } |
| 83 | tmpfile.persist_noclobber(&persist_path).unwrap(); |
| 84 | // Try opening it at the new path. |
| 85 | let mut f = File::open(&persist_path).unwrap(); |
| 86 | f.seek(SeekFrom::Start(0)).unwrap(); |
| 87 | let mut buf = String::new(); |
| 88 | f.read_to_string(&mut buf).unwrap(); |
| 89 | assert_eq!("abcde" , buf); |
| 90 | std::fs::remove_file(&persist_path).unwrap(); |
| 91 | } |
| 92 | |
| 93 | #[test] |
| 94 | fn test_customnamed() { |
| 95 | let tmpfile = Builder::new() |
| 96 | .prefix("tmp" ) |
| 97 | .suffix(&".rs" ) |
| 98 | .rand_bytes(12) |
| 99 | .tempfile() |
| 100 | .unwrap(); |
| 101 | let name = tmpfile.path().file_name().unwrap().to_str().unwrap(); |
| 102 | assert!(name.starts_with("tmp" )); |
| 103 | assert!(name.ends_with(".rs" )); |
| 104 | assert_eq!(name.len(), 18); |
| 105 | } |
| 106 | |
| 107 | #[test] |
| 108 | fn test_append() { |
| 109 | let mut tmpfile = Builder::new().append(true).tempfile().unwrap(); |
| 110 | tmpfile.write_all(b"a" ).unwrap(); |
| 111 | tmpfile.seek(SeekFrom::Start(0)).unwrap(); |
| 112 | tmpfile.write_all(b"b" ).unwrap(); |
| 113 | |
| 114 | tmpfile.seek(SeekFrom::Start(0)).unwrap(); |
| 115 | let mut buf = vec![0u8; 1]; |
| 116 | tmpfile.read_exact(&mut buf).unwrap(); |
| 117 | assert_eq!(buf, b"a" ); |
| 118 | } |
| 119 | |
| 120 | #[test] |
| 121 | fn test_reopen() { |
| 122 | let source = NamedTempFile::new().unwrap(); |
| 123 | let mut first = source.reopen().unwrap(); |
| 124 | let mut second = source.reopen().unwrap(); |
| 125 | drop(source); |
| 126 | |
| 127 | write!(first, "abcde" ).expect("write failed" ); |
| 128 | let mut buf = String::new(); |
| 129 | second.read_to_string(&mut buf).unwrap(); |
| 130 | assert_eq!("abcde" , buf); |
| 131 | } |
| 132 | |
| 133 | #[test] |
| 134 | fn test_into_file() { |
| 135 | let mut file = NamedTempFile::new().unwrap(); |
| 136 | let path = file.path().to_owned(); |
| 137 | write!(file, "abcde" ).expect("write failed" ); |
| 138 | |
| 139 | assert!(path.exists()); |
| 140 | let mut file = file.into_file(); |
| 141 | assert!(!path.exists()); |
| 142 | |
| 143 | file.seek(SeekFrom::Start(0)).unwrap(); |
| 144 | let mut buf = String::new(); |
| 145 | file.read_to_string(&mut buf).unwrap(); |
| 146 | assert_eq!("abcde" , buf); |
| 147 | } |
| 148 | |
| 149 | #[test] |
| 150 | fn test_immut() { |
| 151 | let tmpfile = NamedTempFile::new().unwrap(); |
| 152 | (&tmpfile).write_all(b"abcde" ).unwrap(); |
| 153 | (&tmpfile).seek(SeekFrom::Start(0)).unwrap(); |
| 154 | let mut buf = String::new(); |
| 155 | (&tmpfile).read_to_string(&mut buf).unwrap(); |
| 156 | assert_eq!("abcde" , buf); |
| 157 | } |
| 158 | |
| 159 | #[test] |
| 160 | fn test_temppath() { |
| 161 | let mut tmpfile = NamedTempFile::new().unwrap(); |
| 162 | write!(tmpfile, "abcde" ).unwrap(); |
| 163 | |
| 164 | let path = tmpfile.into_temp_path(); |
| 165 | assert!(path.is_file()); |
| 166 | } |
| 167 | |
| 168 | #[test] |
| 169 | fn test_temppath_persist() { |
| 170 | let mut tmpfile = NamedTempFile::new().unwrap(); |
| 171 | write!(tmpfile, "abcde" ).unwrap(); |
| 172 | |
| 173 | let tmppath = tmpfile.into_temp_path(); |
| 174 | |
| 175 | let old_path = tmppath.to_path_buf(); |
| 176 | let persist_path = env::temp_dir().join("persisted_temppath_file" ); |
| 177 | |
| 178 | { |
| 179 | assert!(exists(&old_path)); |
| 180 | tmppath.persist(&persist_path).unwrap(); |
| 181 | assert!(!exists(&old_path)); |
| 182 | } |
| 183 | |
| 184 | { |
| 185 | // Try opening it at the new path. |
| 186 | let mut f = File::open(&persist_path).unwrap(); |
| 187 | f.seek(SeekFrom::Start(0)).unwrap(); |
| 188 | let mut buf = String::new(); |
| 189 | f.read_to_string(&mut buf).unwrap(); |
| 190 | assert_eq!("abcde" , buf); |
| 191 | } |
| 192 | |
| 193 | std::fs::remove_file(&persist_path).unwrap(); |
| 194 | } |
| 195 | |
| 196 | #[test] |
| 197 | fn test_temppath_persist_noclobber() { |
| 198 | let mut tmpfile = NamedTempFile::new().unwrap(); |
| 199 | write!(tmpfile, "abcde" ).unwrap(); |
| 200 | |
| 201 | let mut tmppath = tmpfile.into_temp_path(); |
| 202 | |
| 203 | let old_path = tmppath.to_path_buf(); |
| 204 | let persist_target = NamedTempFile::new().unwrap(); |
| 205 | let persist_path = persist_target.path().to_path_buf(); |
| 206 | |
| 207 | assert!(exists(&old_path)); |
| 208 | |
| 209 | { |
| 210 | tmppath = tmppath.persist_noclobber(&persist_path).unwrap_err().into(); |
| 211 | assert!(exists(&old_path)); |
| 212 | std::fs::remove_file(&persist_path).unwrap(); |
| 213 | drop(persist_target); |
| 214 | } |
| 215 | |
| 216 | tmppath.persist_noclobber(&persist_path).unwrap(); |
| 217 | |
| 218 | // Try opening it at the new path. |
| 219 | let mut f = File::open(&persist_path).unwrap(); |
| 220 | f.seek(SeekFrom::Start(0)).unwrap(); |
| 221 | let mut buf = String::new(); |
| 222 | f.read_to_string(&mut buf).unwrap(); |
| 223 | assert_eq!("abcde" , buf); |
| 224 | std::fs::remove_file(&persist_path).unwrap(); |
| 225 | } |
| 226 | |
| 227 | #[test] |
| 228 | fn temp_path_from_existing() { |
| 229 | let tmp_dir = tempdir().unwrap(); |
| 230 | let tmp_file_path_1 = tmp_dir.path().join("testfile1" ); |
| 231 | let tmp_file_path_2 = tmp_dir.path().join("testfile2" ); |
| 232 | |
| 233 | File::create(&tmp_file_path_1).unwrap(); |
| 234 | assert!(tmp_file_path_1.exists(), "Test file 1 hasn't been created" ); |
| 235 | |
| 236 | File::create(&tmp_file_path_2).unwrap(); |
| 237 | assert!(tmp_file_path_2.exists(), "Test file 2 hasn't been created" ); |
| 238 | |
| 239 | let tmp_path = TempPath::from_path(&tmp_file_path_1); |
| 240 | assert!( |
| 241 | tmp_file_path_1.exists(), |
| 242 | "Test file has been deleted before dropping TempPath" |
| 243 | ); |
| 244 | |
| 245 | drop(tmp_path); |
| 246 | assert!( |
| 247 | !tmp_file_path_1.exists(), |
| 248 | "Test file exists after dropping TempPath" |
| 249 | ); |
| 250 | assert!( |
| 251 | tmp_file_path_2.exists(), |
| 252 | "Test file 2 has been deleted before dropping TempDir" |
| 253 | ); |
| 254 | } |
| 255 | |
| 256 | #[test] |
| 257 | #[allow (unreachable_code)] |
| 258 | fn temp_path_from_argument_types() { |
| 259 | // This just has to compile |
| 260 | return; |
| 261 | |
| 262 | TempPath::from_path("" ); |
| 263 | TempPath::from_path(String::new()); |
| 264 | TempPath::from_path(OsStr::new("" )); |
| 265 | TempPath::from_path(OsString::new()); |
| 266 | TempPath::from_path(Path::new("" )); |
| 267 | TempPath::from_path(PathBuf::new()); |
| 268 | TempPath::from_path(PathBuf::new().into_boxed_path()); |
| 269 | } |
| 270 | |
| 271 | #[test] |
| 272 | fn test_write_after_close() { |
| 273 | let path = NamedTempFile::new().unwrap().into_temp_path(); |
| 274 | File::create(path).unwrap().write_all(b"test" ).unwrap(); |
| 275 | } |
| 276 | |
| 277 | #[test] |
| 278 | fn test_change_dir() { |
| 279 | env::set_current_dir(env::temp_dir()).unwrap(); |
| 280 | let tmpfile = NamedTempFile::new_in("." ).unwrap(); |
| 281 | let path = env::current_dir().unwrap().join(tmpfile.path()); |
| 282 | env::set_current_dir("/" ).unwrap(); |
| 283 | drop(tmpfile); |
| 284 | assert!(!exists(path)) |
| 285 | } |
| 286 | |
| 287 | #[test] |
| 288 | fn test_into_parts() { |
| 289 | let mut file = NamedTempFile::new().unwrap(); |
| 290 | write!(file, "abcd" ).expect("write failed" ); |
| 291 | |
| 292 | let (mut file, temp_path) = file.into_parts(); |
| 293 | |
| 294 | let path = temp_path.to_path_buf(); |
| 295 | |
| 296 | assert!(path.exists()); |
| 297 | drop(temp_path); |
| 298 | assert!(!path.exists()); |
| 299 | |
| 300 | write!(file, "efgh" ).expect("write failed" ); |
| 301 | |
| 302 | file.seek(SeekFrom::Start(0)).unwrap(); |
| 303 | let mut buf = String::new(); |
| 304 | file.read_to_string(&mut buf).unwrap(); |
| 305 | assert_eq!("abcdefgh" , buf); |
| 306 | } |
| 307 | |
| 308 | #[test] |
| 309 | fn test_from_parts() { |
| 310 | let mut file = NamedTempFile::new().unwrap(); |
| 311 | write!(file, "abcd" ).expect("write failed" ); |
| 312 | |
| 313 | let (file, temp_path) = file.into_parts(); |
| 314 | |
| 315 | let file = NamedTempFile::from_parts(file, temp_path); |
| 316 | |
| 317 | assert!(file.path().exists()); |
| 318 | } |
| 319 | |
| 320 | #[test] |
| 321 | fn test_keep() { |
| 322 | let mut tmpfile = NamedTempFile::new().unwrap(); |
| 323 | write!(tmpfile, "abcde" ).unwrap(); |
| 324 | let (mut f, temp_path) = tmpfile.into_parts(); |
| 325 | let path; |
| 326 | { |
| 327 | assert!(exists(&temp_path)); |
| 328 | path = temp_path.keep().unwrap(); |
| 329 | assert!(exists(&path)); |
| 330 | |
| 331 | // Check original file |
| 332 | f.seek(SeekFrom::Start(0)).unwrap(); |
| 333 | let mut buf = String::new(); |
| 334 | f.read_to_string(&mut buf).unwrap(); |
| 335 | assert_eq!("abcde" , buf); |
| 336 | } |
| 337 | |
| 338 | { |
| 339 | // Try opening it again. |
| 340 | let mut f = File::open(&path).unwrap(); |
| 341 | f.seek(SeekFrom::Start(0)).unwrap(); |
| 342 | let mut buf = String::new(); |
| 343 | f.read_to_string(&mut buf).unwrap(); |
| 344 | assert_eq!("abcde" , buf); |
| 345 | } |
| 346 | std::fs::remove_file(&path).unwrap(); |
| 347 | } |
| 348 | |
| 349 | #[test] |
| 350 | fn test_make() { |
| 351 | let tmpfile = Builder::new().make(|path| File::create(path)).unwrap(); |
| 352 | |
| 353 | assert!(tmpfile.path().is_file()); |
| 354 | } |
| 355 | |
| 356 | #[test] |
| 357 | fn test_make_in() { |
| 358 | let tmp_dir = tempdir().unwrap(); |
| 359 | |
| 360 | let tmpfile = Builder::new() |
| 361 | .make_in(tmp_dir.path(), |path| File::create(path)) |
| 362 | .unwrap(); |
| 363 | |
| 364 | assert!(tmpfile.path().is_file()); |
| 365 | assert_eq!(tmpfile.path().parent(), Some(tmp_dir.path())); |
| 366 | } |
| 367 | |
| 368 | #[test] |
| 369 | fn test_make_fnmut() { |
| 370 | let mut count = 0; |
| 371 | |
| 372 | // Show that an FnMut can be used. |
| 373 | let tmpfile = Builder::new() |
| 374 | .make(|path| { |
| 375 | count += 1; |
| 376 | File::create(path) |
| 377 | }) |
| 378 | .unwrap(); |
| 379 | |
| 380 | assert!(tmpfile.path().is_file()); |
| 381 | } |
| 382 | |
| 383 | #[cfg (unix)] |
| 384 | #[test] |
| 385 | fn test_make_uds() { |
| 386 | use std::os::unix::net::UnixListener; |
| 387 | |
| 388 | let temp_sock = Builder::new() |
| 389 | .prefix("tmp" ) |
| 390 | .suffix(".sock" ) |
| 391 | .rand_bytes(12) |
| 392 | .make(|path| UnixListener::bind(path)) |
| 393 | .unwrap(); |
| 394 | |
| 395 | assert!(temp_sock.path().exists()); |
| 396 | } |
| 397 | |
| 398 | #[cfg (unix)] |
| 399 | #[test] |
| 400 | fn test_make_uds_conflict() { |
| 401 | use std::os::unix::net::UnixListener; |
| 402 | use std::sync::atomic::{AtomicUsize, Ordering}; |
| 403 | use std::sync::Arc; |
| 404 | |
| 405 | // Check that retries happen correctly by racing N different threads. |
| 406 | |
| 407 | const NTHREADS: usize = 20; |
| 408 | |
| 409 | // The number of times our callback was called. |
| 410 | let tries = Arc::new(AtomicUsize::new(0)); |
| 411 | |
| 412 | let mut threads = Vec::with_capacity(NTHREADS); |
| 413 | |
| 414 | for _ in 0..NTHREADS { |
| 415 | let tries = tries.clone(); |
| 416 | threads.push(std::thread::spawn(move || { |
| 417 | // Ensure that every thread uses the same seed so we are guaranteed |
| 418 | // to retry. Note that fastrand seeds are thread-local. |
| 419 | fastrand::seed(42); |
| 420 | |
| 421 | Builder::new() |
| 422 | .prefix("tmp" ) |
| 423 | .suffix(".sock" ) |
| 424 | .rand_bytes(12) |
| 425 | .make(|path| { |
| 426 | tries.fetch_add(1, Ordering::Relaxed); |
| 427 | UnixListener::bind(path) |
| 428 | }) |
| 429 | })); |
| 430 | } |
| 431 | |
| 432 | // Join all threads, but don't drop the temp file yet. Otherwise, we won't |
| 433 | // get a deterministic number of `tries`. |
| 434 | let sockets: Vec<_> = threads |
| 435 | .into_iter() |
| 436 | .map(|thread| thread.join().unwrap().unwrap()) |
| 437 | .collect(); |
| 438 | |
| 439 | // Number of tries is exactly equal to (n*(n+1))/2. |
| 440 | assert_eq!( |
| 441 | tries.load(Ordering::Relaxed), |
| 442 | (NTHREADS * (NTHREADS + 1)) / 2 |
| 443 | ); |
| 444 | |
| 445 | for socket in sockets { |
| 446 | assert!(socket.path().exists()); |
| 447 | } |
| 448 | } |
| 449 | |
| 450 | // Issue #224. |
| 451 | #[test] |
| 452 | fn test_overly_generic_bounds() { |
| 453 | pub struct Foo<T>(T); |
| 454 | |
| 455 | impl<T> Foo<T> |
| 456 | where |
| 457 | T: Sync + Send + 'static, |
| 458 | for<'a> &'a T: Write + Read, |
| 459 | { |
| 460 | pub fn new(foo: T) -> Self { |
| 461 | Self(foo) |
| 462 | } |
| 463 | } |
| 464 | |
| 465 | // Don't really need to run this. Only care if it compiles. |
| 466 | if let Ok(file) = File::open("i_do_not_exist" ) { |
| 467 | let mut f; |
| 468 | let _x = { |
| 469 | f = Foo::new(file); |
| 470 | &mut f |
| 471 | }; |
| 472 | } |
| 473 | } |
| 474 | |