Path migration part 2: nu-test-support (#13329)

# Description
Part 2 of replacing `std::path` types with `nu_path` types added in
#13115. This PR targets `nu-test-support`.
This commit is contained in:
Ian Manske
2024-07-12 02:43:10 +00:00
committed by GitHub
parent 4bd87d0496
commit d56457d63e
17 changed files with 142 additions and 338 deletions

View File

@ -1,137 +1,5 @@
use std::fmt::Display;
use nu_path::{AbsolutePath, AbsolutePathBuf, Path};
use std::io::Read;
use std::ops::Div;
use std::path::{Path, PathBuf};
pub struct AbsoluteFile {
inner: PathBuf,
}
impl AbsoluteFile {
pub fn new(path: impl AsRef<Path>) -> AbsoluteFile {
let path = path.as_ref();
if !path.is_absolute() {
panic!(
"AbsoluteFile::new must take an absolute path :: {}",
path.display()
)
} else if path.is_dir() {
// At the moment, this is not an invariant, but rather a way to catch bugs
// in tests.
panic!(
"AbsoluteFile::new must not take a directory :: {}",
path.display()
)
} else {
AbsoluteFile {
inner: path.to_path_buf(),
}
}
}
pub fn dir(&self) -> AbsolutePath {
AbsolutePath::new(if let Some(parent) = self.inner.parent() {
parent
} else {
unreachable!("Internal error: could not get parent in dir")
})
}
}
impl From<AbsoluteFile> for PathBuf {
fn from(file: AbsoluteFile) -> Self {
file.inner
}
}
pub struct AbsolutePath {
pub inner: PathBuf,
}
impl AbsolutePath {
pub fn new(path: impl AsRef<Path>) -> AbsolutePath {
let path = path.as_ref();
if path.is_absolute() {
AbsolutePath {
inner: path.to_path_buf(),
}
} else {
panic!("AbsolutePath::new must take an absolute path")
}
}
}
impl Div<&str> for &AbsolutePath {
type Output = AbsolutePath;
fn div(self, rhs: &str) -> Self::Output {
let parts = rhs.split('/');
let mut result = self.inner.clone();
for part in parts {
result = result.join(part);
}
AbsolutePath::new(result)
}
}
impl AsRef<Path> for AbsolutePath {
fn as_ref(&self) -> &Path {
self.inner.as_path()
}
}
pub struct RelativePath {
inner: PathBuf,
}
impl RelativePath {
pub fn new(path: impl Into<PathBuf>) -> RelativePath {
let path = path.into();
if path.is_relative() {
RelativePath { inner: path }
} else {
panic!("RelativePath::new must take a relative path")
}
}
}
impl<T: AsRef<str>> Div<T> for &RelativePath {
type Output = RelativePath;
fn div(self, rhs: T) -> Self::Output {
let parts = rhs.as_ref().split('/');
let mut result = self.inner.clone();
for part in parts {
result = result.join(part);
}
RelativePath::new(result)
}
}
impl Display for AbsoluteFile {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.inner.display())
}
}
impl Display for AbsolutePath {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.inner.display())
}
}
impl Display for RelativePath {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.inner.display())
}
}
pub enum Stub<'a> {
FileWithContent(&'a str, &'a str),
@ -140,7 +8,7 @@ pub enum Stub<'a> {
FileWithPermission(&'a str, bool),
}
pub fn file_contents(full_path: impl AsRef<Path>) -> String {
pub fn file_contents(full_path: impl AsRef<AbsolutePath>) -> String {
let mut file = std::fs::File::open(full_path.as_ref()).expect("can not open file");
let mut contents = String::new();
file.read_to_string(&mut contents)
@ -148,7 +16,7 @@ pub fn file_contents(full_path: impl AsRef<Path>) -> String {
contents
}
pub fn file_contents_binary(full_path: impl AsRef<Path>) -> Vec<u8> {
pub fn file_contents_binary(full_path: impl AsRef<AbsolutePath>) -> Vec<u8> {
let mut file = std::fs::File::open(full_path.as_ref()).expect("can not open file");
let mut contents = Vec::new();
file.read_to_end(&mut contents).expect("can not read file");
@ -167,56 +35,32 @@ pub fn line_ending() -> String {
}
}
pub fn delete_file_at(full_path: impl AsRef<Path>) {
let full_path = full_path.as_ref();
if full_path.exists() {
std::fs::remove_file(full_path).expect("can not delete file");
}
pub fn files_exist_at(files: Vec<impl AsRef<Path>>, path: impl AsRef<AbsolutePath>) -> bool {
let path = path.as_ref();
files.iter().all(|f| path.join(f.as_ref()).exists())
}
pub fn create_file_at(full_path: impl AsRef<Path>) -> Result<(), std::io::Error> {
let full_path = full_path.as_ref();
if full_path.parent().is_some() {
panic!("path exists");
}
std::fs::write(full_path, b"fake data")
}
pub fn copy_file_to(source: &str, destination: &str) {
std::fs::copy(source, destination).expect("can not copy file");
}
pub fn files_exist_at(files: Vec<impl AsRef<Path>>, path: impl AsRef<Path>) -> bool {
files.iter().all(|f| {
let mut loc = PathBuf::from(path.as_ref());
loc.push(f);
loc.exists()
})
}
pub fn delete_directory_at(full_path: &str) {
std::fs::remove_dir_all(PathBuf::from(full_path)).expect("can not remove directory");
}
pub fn executable_path() -> PathBuf {
pub fn executable_path() -> AbsolutePathBuf {
let mut path = binaries();
path.push("nu");
path
}
pub fn installed_nu_path() -> PathBuf {
pub fn installed_nu_path() -> AbsolutePathBuf {
let path = std::env::var_os(crate::NATIVE_PATH_ENV_VAR);
which::which_in("nu", path, ".").unwrap_or_else(|_| executable_path())
if let Ok(path) = which::which_in("nu", path, ".") {
AbsolutePathBuf::try_from(path).expect("installed nushell path is absolute")
} else {
executable_path()
}
}
pub fn root() -> PathBuf {
pub fn root() -> AbsolutePathBuf {
let manifest_dir = if let Ok(manifest_dir) = std::env::var("CARGO_MANIFEST_DIR") {
PathBuf::from(manifest_dir)
AbsolutePathBuf::try_from(manifest_dir).expect("CARGO_MANIFEST_DIR is not an absolute path")
} else {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
AbsolutePathBuf::try_from(env!("CARGO_MANIFEST_DIR"))
.expect("CARGO_MANIFEST_DIR is not an absolute path")
};
let test_path = manifest_dir.join("Cargo.lock");
@ -228,11 +72,11 @@ pub fn root() -> PathBuf {
.expect("Couldn't find the debug binaries directory")
.parent()
.expect("Couldn't find the debug binaries directory")
.to_path_buf()
.into()
}
}
pub fn binaries() -> PathBuf {
pub fn binaries() -> AbsolutePathBuf {
let build_target = std::env::var("CARGO_BUILD_TARGET").unwrap_or_default();
let profile = if let Ok(env_profile) = std::env::var("NUSHELL_CARGO_PROFILE") {
@ -244,27 +88,28 @@ pub fn binaries() -> PathBuf {
};
std::env::var("CARGO_TARGET_DIR")
.map(PathBuf::from)
.unwrap_or_else(|_| root().join("target"))
.ok()
.and_then(|p| AbsolutePathBuf::try_from(p).ok())
.unwrap_or_else(|| root().join("target"))
.join(build_target)
.join(profile)
}
pub fn fixtures() -> PathBuf {
root().join("tests").join("fixtures")
pub fn fixtures() -> AbsolutePathBuf {
let mut path = root();
path.push("tests");
path.push("fixtures");
path
}
pub fn assets() -> PathBuf {
root().join("tests/assets")
}
// FIXME: re-enable nu_json tests
// pub fn assets() -> AbsolutePathBuf {
// let mut path = root();
// path.push("tests");
// path.push("assets");
// path
// }
pub fn in_directory(str: impl AsRef<Path>) -> String {
let path = str.as_ref();
let path = if path.is_relative() {
root().join(path)
} else {
path.to_path_buf()
};
path.display().to_string()
pub fn in_directory(path: impl AsRef<nu_path::Path>) -> AbsolutePathBuf {
root().join(path)
}

View File

@ -234,16 +234,16 @@ macro_rules! nu_with_plugins {
}
use crate::{Outcome, NATIVE_PATH_ENV_VAR};
use std::ffi::OsStr;
use nu_path::{AbsolutePath, AbsolutePathBuf, Path};
use std::{
path::Path,
ffi::OsStr,
process::{Command, Stdio},
};
use tempfile::tempdir;
#[derive(Default)]
pub struct NuOpts {
pub cwd: Option<String>,
pub cwd: Option<AbsolutePathBuf>,
pub locale: Option<String>,
pub envs: Option<Vec<(String, String)>>,
pub collapse_output: Option<bool>,
@ -251,19 +251,12 @@ pub struct NuOpts {
}
pub fn nu_run_test(opts: NuOpts, commands: impl AsRef<str>, with_std: bool) -> Outcome {
let test_bins = crate::fs::binaries();
let cwd = std::env::current_dir().expect("Could not get current working directory.");
let test_bins = nu_path::canonicalize_with(&test_bins, cwd).unwrap_or_else(|e| {
panic!(
"Couldn't canonicalize dummy binaries path {}: {:?}",
test_bins.display(),
e
)
});
let test_bins = crate::fs::binaries()
.canonicalize()
.expect("Could not canonicalize dummy binaries path");
let mut paths = crate::shell_os_paths();
paths.insert(0, test_bins);
paths.insert(0, test_bins.into());
let commands = commands.as_ref().lines().collect::<Vec<_>>().join("; ");
@ -272,7 +265,7 @@ pub fn nu_run_test(opts: NuOpts, commands: impl AsRef<str>, with_std: bool) -> O
Err(_) => panic!("Couldn't join paths for PATH var."),
};
let target_cwd = opts.cwd.unwrap_or(".".to_string());
let target_cwd = opts.cwd.unwrap_or_else(crate::fs::root);
let locale = opts.locale.unwrap_or("en_US.UTF-8".to_string());
let executable_path = crate::fs::executable_path();
@ -450,7 +443,7 @@ fn collapse_output(out: &str) -> String {
out.replace('\n', "")
}
fn setup_command(executable_path: &Path, target_cwd: &str) -> Command {
fn setup_command(executable_path: &AbsolutePath, target_cwd: &AbsolutePath) -> Command {
let mut command = Command::new(executable_path);
command

View File

@ -6,5 +6,5 @@ mod play;
mod tests;
pub use director::Director;
pub use nu_process::{Executable, NuProcess, NuResult, Outcome};
pub use nu_process::{Executable, NuProcess, Outcome};
pub use play::{Dirs, EnvironmentVariable, Playground};

View File

@ -83,7 +83,7 @@ impl Director {
}
impl Executable for Director {
fn execute(&mut self) -> NuResult {
fn execute(&mut self) -> Result<Outcome, NuError> {
use std::process::Stdio;
match self.executable() {

View File

@ -1,12 +1,13 @@
use super::EnvironmentVariable;
use crate::fs::{binaries as test_bins_path, executable_path};
use std::ffi::{OsStr, OsString};
use std::fmt;
use std::path::Path;
use std::process::{Command, ExitStatus};
use std::{
ffi::{OsStr, OsString},
fmt,
process::{Command, ExitStatus},
};
pub trait Executable {
fn execute(&mut self) -> NuResult;
fn execute(&mut self) -> Result<Outcome, NuError>;
}
#[derive(Clone, Debug)]
@ -24,8 +25,6 @@ impl Outcome {
}
}
pub type NuResult = Result<Outcome, NuError>;
#[derive(Debug)]
pub struct NuError {
pub desc: String,
@ -69,14 +68,10 @@ impl NuProcess {
self
}
pub fn get_cwd(&self) -> Option<&Path> {
self.cwd.as_ref().map(Path::new)
}
pub fn construct(&self) -> Command {
let mut command = Command::new(executable_path());
if let Some(cwd) = self.get_cwd() {
if let Some(cwd) = &self.cwd {
command.current_dir(cwd);
}

View File

@ -1,8 +1,9 @@
use super::Director;
use crate::fs;
use crate::fs::Stub;
use crate::fs::{self, Stub};
use nu_glob::glob;
use std::path::{Path, PathBuf};
#[cfg(not(target_arch = "wasm32"))]
use nu_path::Path;
use nu_path::{AbsolutePath, AbsolutePathBuf};
use std::str;
use tempfile::{tempdir, TempDir};
@ -22,46 +23,46 @@ impl EnvironmentVariable {
}
pub struct Playground<'a> {
root: TempDir,
_root: TempDir,
tests: String,
cwd: PathBuf,
config: Option<PathBuf>,
cwd: AbsolutePathBuf,
config: Option<AbsolutePathBuf>,
environment_vars: Vec<EnvironmentVariable>,
dirs: &'a Dirs,
}
#[derive(Default, Clone)]
#[derive(Clone)]
pub struct Dirs {
pub root: PathBuf,
pub test: PathBuf,
pub fixtures: PathBuf,
pub root: AbsolutePathBuf,
pub test: AbsolutePathBuf,
pub fixtures: AbsolutePathBuf,
}
impl Dirs {
pub fn formats(&self) -> PathBuf {
pub fn formats(&self) -> AbsolutePathBuf {
self.fixtures.join("formats")
}
pub fn root(&self) -> &Path {
self.root.as_path()
pub fn root(&self) -> &AbsolutePath {
&self.root
}
pub fn test(&self) -> &Path {
self.test.as_path()
pub fn test(&self) -> &AbsolutePath {
&self.test
}
}
impl<'a> Playground<'a> {
pub fn root(&self) -> &Path {
self.root.path()
pub fn root(&self) -> &AbsolutePath {
&self.dirs.root
}
pub fn cwd(&self) -> &Path {
pub fn cwd(&self) -> &AbsolutePath {
&self.cwd
}
pub fn back_to_playground(&mut self) -> &mut Self {
self.cwd = PathBuf::from(self.root()).join(self.tests.clone());
self.cwd = self.root().join(&self.tests);
self
}
@ -70,68 +71,46 @@ impl<'a> Playground<'a> {
}
pub fn setup(topic: &str, block: impl FnOnce(Dirs, &mut Playground)) {
let root = tempdir().expect("Couldn't create a tempdir");
let nuplay_dir = root.path().join(topic);
let temp = tempdir().expect("Could not create a tempdir");
if PathBuf::from(&nuplay_dir).exists() {
std::fs::remove_dir_all(PathBuf::from(&nuplay_dir)).expect("can not remove directory");
let root = AbsolutePathBuf::try_from(temp.path())
.expect("Tempdir is not an absolute path")
.canonicalize()
.expect("Could not canonicalize tempdir");
let test = root.join(topic);
if test.exists() {
std::fs::remove_dir_all(&test).expect("Could not remove directory");
}
std::fs::create_dir(&test).expect("Could not create directory");
let test = test
.canonicalize()
.expect("Could not canonicalize test path");
std::fs::create_dir(PathBuf::from(&nuplay_dir)).expect("can not create directory");
let fixtures = fs::fixtures();
let cwd = std::env::current_dir().expect("Could not get current working directory.");
let fixtures = nu_path::canonicalize_with(fixtures.clone(), cwd).unwrap_or_else(|e| {
panic!(
"Couldn't canonicalize fixtures path {}: {:?}",
fixtures.display(),
e
)
});
let mut playground = Playground {
root,
tests: topic.to_string(),
cwd: nuplay_dir,
config: None,
environment_vars: Vec::default(),
dirs: &Dirs::default(),
};
let playground_root = playground.root.path();
let cwd = std::env::current_dir().expect("Could not get current working directory.");
let test =
nu_path::canonicalize_with(playground_root.join(topic), cwd).unwrap_or_else(|e| {
panic!(
"Couldn't canonicalize test path {}: {:?}",
playground_root.join(topic).display(),
e
)
});
let cwd = std::env::current_dir().expect("Could not get current working directory.");
let root = nu_path::canonicalize_with(playground_root, cwd).unwrap_or_else(|e| {
panic!(
"Couldn't canonicalize tests root path {}: {:?}",
playground_root.display(),
e
)
});
let fixtures = fs::fixtures()
.canonicalize()
.expect("Could not canonicalize fixtures path");
let dirs = Dirs {
root,
test,
fixtures,
root: root.into(),
test: test.as_path().into(),
fixtures: fixtures.into(),
};
playground.dirs = &dirs;
let mut playground = Playground {
_root: temp,
tests: topic.to_string(),
cwd: test.into(),
config: None,
environment_vars: Vec::default(),
dirs: &dirs,
};
block(dirs.clone(), &mut playground);
}
pub fn with_config(&mut self, source_file: impl AsRef<Path>) -> &mut Self {
self.config = Some(source_file.as_ref().to_path_buf());
pub fn with_config(&mut self, source_file: AbsolutePathBuf) -> &mut Self {
self.config = Some(source_file);
self
}
@ -205,7 +184,6 @@ impl<'a> Playground<'a> {
files
.iter()
.map(|f| {
let mut path = PathBuf::from(&self.cwd);
let mut permission_set = false;
let mut write_able = true;
let (file_name, contents) = match *f {
@ -227,7 +205,7 @@ impl<'a> Playground<'a> {
}
};
path.push(file_name);
let path = self.cwd.join(file_name);
std::fs::write(&path, contents.as_bytes()).expect("can not create file");
if permission_set {
@ -252,7 +230,7 @@ impl<'a> Playground<'a> {
self
}
pub fn glob_vec(pattern: &str) -> Vec<PathBuf> {
pub fn glob_vec(pattern: &str) -> Vec<std::path::PathBuf> {
let glob = glob(pattern);
glob.expect("invalid pattern")

View File

@ -1,11 +1,4 @@
use crate::playground::Playground;
use std::path::{Path, PathBuf};
fn path(p: &Path) -> PathBuf {
let cwd = std::env::current_dir().expect("Could not get current working directory.");
nu_path::canonicalize_with(p, cwd)
.unwrap_or_else(|e| panic!("Couldn't canonicalize path {}: {:?}", p.display(), e))
}
#[test]
fn current_working_directory_in_sandbox_directory_created() {
@ -13,7 +6,7 @@ fn current_working_directory_in_sandbox_directory_created() {
let original_cwd = dirs.test();
nu.within("some_directory_within");
assert_eq!(path(nu.cwd()), original_cwd.join("some_directory_within"));
assert_eq!(nu.cwd(), original_cwd.join("some_directory_within"));
})
}
@ -25,6 +18,6 @@ fn current_working_directory_back_to_root_from_anywhere() {
nu.within("some_directory_within");
nu.back_to_playground();
assert_eq!(path(nu.cwd()), *original_cwd);
assert_eq!(nu.cwd(), original_cwd);
})
}