Skip to content

Commit e875e2e

Browse files
authored
Merge pull request #616 from SteveL-MSFT/path-windows
Ensure `systemRoot()` always has trailing separator character
2 parents 2c0b681 + f17e266 commit e875e2e

File tree

5 files changed

+75
-9
lines changed

5 files changed

+75
-9
lines changed

dsc/src/subcommand.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,12 @@ pub fn config(subcommand: &ConfigSubCommand, parameters: &Option<String>, mounte
341341
exit(EXIT_INVALID_ARGS);
342342
}
343343

344-
configurator.set_system_root(path);
344+
// make sure path has a trailing separator if it's a drive letter
345+
if path.len() == 2 && path.chars().nth(1).unwrap_or(' ') == ':' {
346+
configurator.set_system_root(&format!("{path}\\"));
347+
} else {
348+
configurator.set_system_root(path);
349+
}
345350
}
346351

347352
if let Err(err) = configurator.set_context(parameters.as_ref()) {

dsc/tests/dsc_functions.tests.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ Describe 'tests for function expressions' {
5454
'@
5555

5656
$expected = if ($IsWindows) {
57-
$env:SYSTEMDRIVE
57+
$env:SYSTEMDRIVE + '\'
5858
} else {
5959
'/'
6060
}

dsc_lib/src/configure/context.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ impl Default for Context {
4545

4646
#[cfg(target_os = "windows")]
4747
fn get_default_os_system_root() -> PathBuf {
48-
// use SYSTEMDRIVE env var to get the default target path
48+
// use SYSTEMDRIVE env var to get the default target path, append trailing separator
4949
let system_drive = std::env::var("SYSTEMDRIVE").unwrap_or_else(|_| "C:".to_string());
50-
PathBuf::from(system_drive)
50+
PathBuf::from(system_drive + "\\")
5151
}
5252

5353
#[cfg(not(target_os = "windows"))]

dsc_lib/src/functions/path.rs

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,19 +48,80 @@ mod tests {
4848
use crate::configure::context::Context;
4949
use crate::parser::Statement;
5050

51+
const SEPARATOR: char = std::path::MAIN_SEPARATOR;
52+
53+
#[test]
54+
fn start_with_drive_letter() {
55+
let mut parser = Statement::new().unwrap();
56+
let result = parser.parse_and_execute("[path('C:\\','test')]", &Context::new()).unwrap();
57+
58+
#[cfg(target_os = "windows")]
59+
assert_eq!(result, format!("C:{SEPARATOR}test"));
60+
61+
#[cfg(not(target_os = "windows"))]
62+
assert_eq!(result, format!("C:\\{SEPARATOR}test"));
63+
}
64+
65+
#[test]
66+
fn drive_letter_in_middle() {
67+
let mut parser = Statement::new().unwrap();
68+
let result = parser.parse_and_execute("[path('a','C:\\','test')]", &Context::new()).unwrap();
69+
70+
// if any part of the path is absolute, it replaces it instead of appending on Windows
71+
#[cfg(target_os = "windows")]
72+
assert_eq!(result, format!("C:{SEPARATOR}test"));
73+
74+
// non-Windows, the colon is a valid character in a path
75+
#[cfg(not(target_os = "windows"))]
76+
assert_eq!(result, format!("a{SEPARATOR}C:\\{SEPARATOR}test"));
77+
}
78+
79+
#[test]
80+
fn multiple_drive_letters() {
81+
let mut parser = Statement::new().unwrap();
82+
let result = parser.parse_and_execute("[path('C:\\','D:\\','test')]", &Context::new()).unwrap();
83+
84+
// if any part of the path is absolute, it replaces it instead of appending on Windows
85+
#[cfg(target_os = "windows")]
86+
assert_eq!(result, format!("D:\\test"));
87+
88+
// non-Windows, the colon is a valid character in a path
89+
#[cfg(not(target_os = "windows"))]
90+
assert_eq!(result, format!("C:\\{SEPARATOR}D:\\{SEPARATOR}test"));
91+
}
92+
93+
#[test]
94+
fn relative_path() {
95+
let mut parser = Statement::new().unwrap();
96+
let result = parser.parse_and_execute("[path('a','..','b')]", &Context::new()).unwrap();
97+
assert_eq!(result, format!("a{SEPARATOR}..{SEPARATOR}b"));
98+
}
99+
100+
#[test]
101+
fn path_segement_with_separator() {
102+
let mut parser = Statement::new().unwrap();
103+
let result = parser.parse_and_execute(format!("[path('a','b{SEPARATOR}c')]").as_str(), &Context::new()).unwrap();
104+
assert_eq!(result, format!("a{SEPARATOR}b{SEPARATOR}c"));
105+
}
106+
107+
#[test]
108+
fn unix_absolute_path() {
109+
let mut parser = Statement::new().unwrap();
110+
let result = parser.parse_and_execute("[path('/','a','b')]", &Context::new()).unwrap();
111+
assert_eq!(result, format!("/a{SEPARATOR}b"));
112+
}
113+
51114
#[test]
52115
fn two_args() {
53116
let mut parser = Statement::new().unwrap();
54-
let separator = std::path::MAIN_SEPARATOR;
55117
let result = parser.parse_and_execute("[path('a','b')]", &Context::new()).unwrap();
56-
assert_eq!(result, format!("a{separator}b"));
118+
assert_eq!(result, format!("a{SEPARATOR}b"));
57119
}
58120

59121
#[test]
60122
fn three_args() {
61123
let mut parser = Statement::new().unwrap();
62-
let separator = std::path::MAIN_SEPARATOR;
63124
let result = parser.parse_and_execute("[path('a','b','c')]", &Context::new()).unwrap();
64-
assert_eq!(result, format!("a{separator}b{separator}c"));
125+
assert_eq!(result, format!("a{SEPARATOR}b{SEPARATOR}c"));
65126
}
66127
}

dsc_lib/src/functions/system_root.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ mod tests {
4444
let result = parser.parse_and_execute("[systemRoot()]", &Context::new()).unwrap();
4545
// on windows, the default is SYSTEMDRIVE env var
4646
#[cfg(target_os = "windows")]
47-
assert_eq!(result, std::env::var("SYSTEMDRIVE").unwrap());
47+
assert_eq!(result, format!("{}\\", std::env::var("SYSTEMDRIVE").unwrap()));
4848
// on linux/macOS, the default is /
4949
#[cfg(not(target_os = "windows"))]
5050
assert_eq!(result, "/");

0 commit comments

Comments
 (0)