Skip to content

Commit 1758338

Browse files
authored
Fix tests on Windows (#16)
This commit fixes the test suite on Windows, but in order to do so in a reasonable manner, makes some additional changes: * Turns some String errors into more structured errors * Escapes paths properly on Windows; this was the main issue with the suite failing * moves Utc::now inline, so it's more clear that no caching is happening * Sleeps between actions on some tests on Windows; Chrono only supports times in 100-nanosecond granularity, and so we need to ensure enough time passes to observe the forward motion of time.
1 parent 09abf77 commit 1758338

File tree

10 files changed

+204
-98
lines changed

10 files changed

+204
-98
lines changed

Cargo.lock

Lines changed: 4 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ tokio = { version = "0.2", features = [ "full" ] }
1919
toml = "0.5.6"
2020
uuid = { version = "0.8", features = [ "serde", "v4" ] }
2121

22+
[dev-dependencies]
23+
libc = "0.2.71"
24+
2225
[dependencies.slog]
2326
version = "2.5"
2427
features = [ "max_level_trace", "release_max_level_debug" ]

dropshot/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,6 @@ uuid = { version = "0.8", features = [ "serde", "v4" ] }
3030
[dependencies.slog]
3131
version = "2.5"
3232
features = [ "max_level_trace", "release_max_level_debug" ]
33+
34+
[dev-dependencies]
35+
libc = "0.2.71"

dropshot/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@
6363
* ConfigLogging::StderrTerminal {
6464
* level: ConfigLoggingLevel::Info,
6565
* }
66-
* .to_logger("minimal-example")?;
66+
* .to_logger("minimal-example")
67+
* .map_err(|e| e.to_string())?;
6768
*
6869
* // Describe the API.
6970
* let mut api = ApiDescription::new();

dropshot/src/logging.rs

Lines changed: 75 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use slog::Drain;
1010
use slog::Level;
1111
use slog::Logger;
1212
use std::fs::OpenOptions;
13-
use std::path::Path;
13+
use std::{io, path::Path};
1414

1515
/**
1616
* Represents the logging configuration for a server. This is expected to be a
@@ -77,7 +77,7 @@ impl ConfigLogging {
7777
pub fn to_logger<S: AsRef<str>>(
7878
&self,
7979
log_name: S,
80-
) -> Result<Logger, String> {
80+
) -> Result<Logger, io::Error> {
8181
match self {
8282
ConfigLogging::StderrTerminal {
8383
level,
@@ -141,18 +141,12 @@ fn log_drain_for_file(
141141
open_options: &OpenOptions,
142142
path: &Path,
143143
log_name: String,
144-
) -> Result<slog::Fuse<slog_json::Json<std::fs::File>>, String> {
144+
) -> Result<slog::Fuse<slog_json::Json<std::fs::File>>, io::Error> {
145145
if let Some(parent) = path.parent() {
146-
std::fs::create_dir_all(parent).map_err(|e| {
147-
let p = path.display();
148-
format!("open log file \"{}\": {}", p, e)
149-
})?;
146+
std::fs::create_dir_all(parent)?;
150147
}
151148

152-
let file = open_options.open(path).map_err(|e| {
153-
let p = path.display();
154-
format!("open log file \"{}\": {}", p, e)
155-
})?;
149+
let file = open_options.open(path)?;
156150

157151
/*
158152
* Record a message to the stderr so that a reader who doesn't already know
@@ -180,10 +174,11 @@ mod test {
180174
use crate::test_util::verify_bunyan_records_sequential;
181175
use crate::test_util::BunyanLogRecordSpec;
182176
use crate::ConfigLogging;
177+
use libc;
183178
use slog::Logger;
184179
use std::fs;
185180
use std::path::Path;
186-
use std::path::PathBuf;
181+
use std::{io, path::PathBuf};
187182

188183
/**
189184
* Generates a temporary filesystem path unique for the given label.
@@ -207,7 +202,7 @@ mod test {
207202
fn read_config_and_create_logger(
208203
label: &str,
209204
contents: &str,
210-
) -> Result<Logger, String> {
205+
) -> Result<Logger, io::Error> {
211206
let config = read_config::<ConfigLogging>(label, contents).unwrap();
212207
let result = config.to_logger("test-logger");
213208
if let Err(ref error) = result {
@@ -224,7 +219,8 @@ mod test {
224219
fn test_config_bad_log_mode() {
225220
let bad_config = r##" mode = "bonkers" "##;
226221
let error = read_config::<ConfigLogging>("bad_log_mode", bad_config)
227-
.unwrap_err();
222+
.unwrap_err()
223+
.to_string();
228224
assert!(error.starts_with(
229225
"unknown variant `bonkers`, expected `stderr-terminal` or `file` \
230226
for key `mode`"
@@ -243,7 +239,8 @@ mod test {
243239
let bad_config = r##" mode = "stderr-terminal" "##;
244240
assert_eq!(
245241
read_config::<ConfigLogging>("bad_terminal_no_level", bad_config)
246-
.unwrap_err(),
242+
.unwrap_err()
243+
.to_string(),
247244
"missing field `level`"
248245
);
249246
}
@@ -256,7 +253,8 @@ mod test {
256253
"##;
257254
assert_eq!(
258255
read_config::<ConfigLogging>("bad_terminal_bad_level", bad_config)
259-
.unwrap_err(),
256+
.unwrap_err()
257+
.to_string(),
260258
"unknown variant `everything`, expected one of `trace`, `debug`, \
261259
`info`, `warn`, `error`, `critical`"
262260
);
@@ -296,7 +294,8 @@ mod test {
296294
"##;
297295
let error =
298296
read_config::<ConfigLogging>("bad_file_no_file", bad_config)
299-
.unwrap_err();
297+
.unwrap_err()
298+
.to_string();
300299
assert_eq!(error, "missing field `path`");
301300
}
302301

@@ -308,7 +307,8 @@ mod test {
308307
"##;
309308
let error =
310309
read_config::<ConfigLogging>("bad_file_no_level", bad_config)
311-
.unwrap_err();
310+
.unwrap_err()
311+
.to_string();
312312
assert_eq!(error, "missing field `level`");
313313
}
314314

@@ -357,34 +357,32 @@ mod test {
357357

358358
/**
359359
* Records that the caller intends to create a directory with relative
360-
* path "path" underneath the root directory for this log test. Returns
361-
* a String representing the path to this directory. This directory
362-
* will be removed during teardown. Directories and files must be
363-
* recorded in the order they would be created so that the order can be
364-
* reversed at teardown (without needing any kind of recursive removal).
360+
* path "path" underneath the root directory for this log test. Returns
361+
* the path to this directory. This directory will be removed during
362+
* teardown. Directories and files must be recorded in the order they
363+
* would be created so that the order can be reversed at teardown
364+
* (without needing any kind of recursive removal).
365365
*/
366-
fn will_create_dir(&mut self, path: &str) -> String {
366+
fn will_create_dir(&mut self, path: &str) -> PathBuf {
367367
let mut pathbuf = self.directory.clone();
368368
pathbuf.push(path);
369-
let rv = pathbuf.as_path().display().to_string();
370-
self.cleanup_list.push(LogTestCleanup::Directory(pathbuf));
371-
rv
369+
self.cleanup_list.push(LogTestCleanup::Directory(pathbuf.clone()));
370+
pathbuf
372371
}
373372

374373
/**
375374
* Records that the caller intends to create a file with relative path
376-
* "path" underneath the root directory for this log test. Returns a
377-
* String representing the path to this file. This file will be removed
378-
* during teardown. Directories and files must be recorded in the order
379-
* they would be created so that the order can be reversed at teardown
380-
* (without needing any kind of recursive removal).
375+
* "path" underneath the root directory for this log test. Returns a
376+
* the path to this file. This file will be removed during teardown.
377+
* Directories and files must be recorded in the order they would be
378+
* created so that the order can be reversed at teardown (without
379+
* needing any kind of recursive removal).
381380
*/
382-
fn will_create_file(&mut self, path: &str) -> String {
381+
fn will_create_file(&mut self, path: &str) -> PathBuf {
383382
let mut pathbuf = self.directory.clone();
384383
pathbuf.push(path);
385-
let rv = pathbuf.as_path().display().to_string();
386-
self.cleanup_list.push(LogTestCleanup::File(pathbuf));
387-
rv
384+
self.cleanup_list.push(LogTestCleanup::File(pathbuf.clone()));
385+
pathbuf
388386
}
389387
}
390388

@@ -413,26 +411,32 @@ mod test {
413411
let path = logtest.will_create_dir("log_file_as_dir");
414412
fs::create_dir(&path).unwrap();
415413

414+
// Windows paths need to have \ turned into \\
415+
let escaped_path =
416+
path.display().to_string().escape_default().to_string();
417+
416418
let bad_config = format!(
417-
"{}\"{}\"\n",
418-
r##"
419+
r#"
419420
mode = "file"
420421
level = "warn"
421422
if_exists = "append"
422-
path = "##,
423-
&path
423+
path = "{}"
424+
"#,
425+
escaped_path
424426
);
425427

426428
let error = read_config_and_create_logger(
427429
"bad_file_bad_path_type",
428430
&bad_config,
429431
)
430432
.unwrap_err();
431-
let message = format!("{}", error);
432-
assert!(message.starts_with(&format!(
433-
"open log file \"{}\": Is a directory",
434-
&path
435-
)));
433+
434+
if cfg!(windows) {
435+
assert_eq!(error.kind(), std::io::ErrorKind::PermissionDenied);
436+
} else {
437+
assert_eq!(error.kind(), std::io::ErrorKind::Other);
438+
assert_eq!(error.raw_os_error(), Some(libc::EISDIR));
439+
}
436440
}
437441

438442
#[test]
@@ -441,26 +445,27 @@ mod test {
441445
let logpath = logtest.will_create_file("log.out");
442446
fs::write(&logpath, "").expect("writing empty file");
443447

448+
// Windows paths need to have \ turned into \\
449+
let escaped_path =
450+
logpath.display().to_string().escape_default().to_string();
451+
444452
let bad_config = format!(
445-
"{}\"{}\"\n",
446-
r##"
453+
r#"
447454
mode = "file"
448455
level = "warn"
449456
if_exists = "fail"
450-
path = "##,
451-
&logpath
457+
path = "{}"
458+
"#,
459+
escaped_path
452460
);
453461

454462
let error = read_config_and_create_logger(
455463
"bad_file_bad_path_exists_fail",
456464
&bad_config,
457465
)
458466
.unwrap_err();
459-
let message = format!("{}", error);
460-
assert!(message.starts_with(&format!(
461-
"open log file \"{}\": File exists",
462-
&logpath
463-
)));
467+
468+
assert_eq!(error.kind(), std::io::ErrorKind::AlreadyExists);
464469
}
465470

466471
/*
@@ -475,15 +480,19 @@ mod test {
475480
let logpath = logtest.will_create_file("log.out");
476481
let time_before = chrono::offset::Utc::now();
477482

483+
// Windows paths need to have \ turned into \\
484+
let escaped_path =
485+
logpath.display().to_string().escape_default().to_string();
486+
478487
/* The first attempt should succeed. The log file doesn't exist yet. */
479488
let config = format!(
480-
"{}\"{}\"\n",
481-
r##"
489+
r#"
482490
mode = "file"
483491
level = "warn"
484492
if_exists = "fail"
485-
path = "##,
486-
&logpath
493+
path = "{}"
494+
"#,
495+
escaped_path
487496
);
488497

489498
{
@@ -499,13 +508,13 @@ mod test {
499508

500509
/* Try again with if_exists = "append". This should also work. */
501510
let config = format!(
502-
"{}\"{}\"\n",
503-
r##"
511+
r#"
504512
mode = "file"
505513
level = "warn"
506514
if_exists = "append"
507-
path = "##,
508-
&logpath
515+
path = "{}"
516+
"#,
517+
escaped_path
509518
);
510519

511520
{
@@ -541,13 +550,13 @@ mod test {
541550
let time_before = time_after;
542551
let time_after = chrono::offset::Utc::now();
543552
let config = format!(
544-
"{}\"{}\"\n",
545-
r##"
553+
r#"
546554
mode = "file"
547555
level = "trace"
548556
if_exists = "truncate"
549-
path = "##,
550-
&logpath
557+
path = "{}"
558+
"#,
559+
escaped_path
551560
);
552561

553562
{

dropshot/src/test_util.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -625,9 +625,8 @@ pub fn log_file_for_test(test_name: &str) -> PathBuf {
625625
pub fn read_config<T: DeserializeOwned + Debug>(
626626
label: &str,
627627
contents: &str,
628-
) -> Result<T, String> {
629-
let result: Result<T, String> =
630-
toml::from_str(contents).map_err(|error| format!("{}", error));
628+
) -> Result<T, toml::de::Error> {
629+
let result = toml::from_str(contents);
631630
eprintln!("config \"{}\": {:?}", label, result);
632631
result
633632
}
@@ -653,7 +652,7 @@ pub struct BunyanLogRecord {
653652
/**
654653
* Read a file containing a Bunyan-format log, returning an array of records.
655654
*/
656-
pub fn read_bunyan_log(logpath: &str) -> Vec<BunyanLogRecord> {
655+
pub fn read_bunyan_log(logpath: &Path) -> Vec<BunyanLogRecord> {
657656
let log_contents = fs::read_to_string(logpath).unwrap();
658657
let log_records = log_contents
659658
.split("\n")

0 commit comments

Comments
 (0)