mirror of
https://github.com/nushell/nushell.git
synced 2025-08-09 11:35:43 +02:00
Playground infraestructure (tests, etc) additions. (#3179)
* Playground infraestructure (tests, etc) additions. A few things to note: * Nu can be started with a custom configuration file (`nu --config-file /path/to/sample_config.toml`). Useful for mocking the configuration on test runs. * When given a custom configuration file Nu will save any changes to the file supplied appropiately. * The `$nu.config-path` variable either shows the default configuration file (or the custom one, if given) * We can now run end to end tests with finer grained control (currently, since this is baseline work, standard out) This will allow to check things like exit status, assert the contents with a format, etc) * Remove (for another PR)
This commit is contained in:
committed by
GitHub
parent
82b6300dcb
commit
d2213d18fa
@ -1,176 +1,12 @@
|
||||
use crate::fs;
|
||||
use crate::fs::Stub;
|
||||
mod director;
|
||||
pub mod matchers;
|
||||
pub mod nu_process;
|
||||
mod play;
|
||||
|
||||
use getset::Getters;
|
||||
use glob::glob;
|
||||
use std::path::{Path, PathBuf};
|
||||
use tempfile::{tempdir, TempDir};
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
pub struct Playground {
|
||||
root: TempDir,
|
||||
tests: String,
|
||||
cwd: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Getters)]
|
||||
#[get = "pub"]
|
||||
pub struct Dirs {
|
||||
pub root: PathBuf,
|
||||
pub test: PathBuf,
|
||||
pub fixtures: PathBuf,
|
||||
}
|
||||
|
||||
impl Dirs {
|
||||
pub fn formats(&self) -> PathBuf {
|
||||
self.fixtures.join("formats")
|
||||
}
|
||||
}
|
||||
|
||||
impl Playground {
|
||||
pub fn root(&self) -> &Path {
|
||||
self.root.path()
|
||||
}
|
||||
|
||||
pub fn back_to_playground(&mut self) -> &mut Self {
|
||||
self.cwd = PathBuf::from(self.root()).join(self.tests.clone());
|
||||
self
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if PathBuf::from(&nuplay_dir).exists() {
|
||||
std::fs::remove_dir_all(PathBuf::from(&nuplay_dir)).expect("can not remove directory");
|
||||
}
|
||||
|
||||
std::fs::create_dir(PathBuf::from(&nuplay_dir)).expect("can not create directory");
|
||||
|
||||
let mut playground = Playground {
|
||||
root,
|
||||
tests: topic.to_string(),
|
||||
cwd: nuplay_dir,
|
||||
};
|
||||
|
||||
let playground_root = playground.root.path();
|
||||
|
||||
let fixtures = fs::fixtures();
|
||||
let fixtures = dunce::canonicalize(fixtures.clone()).unwrap_or_else(|e| {
|
||||
panic!(
|
||||
"Couldn't canonicalize fixtures path {}: {:?}",
|
||||
fixtures.display(),
|
||||
e
|
||||
)
|
||||
});
|
||||
|
||||
let test = dunce::canonicalize(playground_root.join(topic)).unwrap_or_else(|e| {
|
||||
panic!(
|
||||
"Couldn't canonicalize test path {}: {:?}",
|
||||
playground_root.join(topic).display(),
|
||||
e
|
||||
)
|
||||
});
|
||||
|
||||
let root = dunce::canonicalize(playground_root).unwrap_or_else(|e| {
|
||||
panic!(
|
||||
"Couldn't canonicalize tests root path {}: {:?}",
|
||||
playground_root.display(),
|
||||
e
|
||||
)
|
||||
});
|
||||
|
||||
let dirs = Dirs {
|
||||
root,
|
||||
test,
|
||||
fixtures,
|
||||
};
|
||||
|
||||
block(dirs, &mut playground);
|
||||
}
|
||||
|
||||
pub fn mkdir(&mut self, directory: &str) -> &mut Self {
|
||||
self.cwd.push(directory);
|
||||
std::fs::create_dir_all(&self.cwd).expect("can not create directory");
|
||||
self.back_to_playground();
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn symlink(&mut self, from: impl AsRef<Path>, to: impl AsRef<Path>) -> &mut Self {
|
||||
let from = self.cwd.join(from);
|
||||
let to = self.cwd.join(to);
|
||||
|
||||
let create_symlink = {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
std::os::unix::fs::symlink
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
if from.is_file() {
|
||||
std::os::windows::fs::symlink_file
|
||||
} else if from.is_dir() {
|
||||
std::os::windows::fs::symlink_dir
|
||||
} else {
|
||||
panic!("symlink from must be a file or dir")
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
create_symlink(from, to).expect("can not create symlink");
|
||||
self.back_to_playground();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_files(&mut self, files: Vec<Stub>) -> &mut Self {
|
||||
let endl = fs::line_ending();
|
||||
|
||||
files
|
||||
.iter()
|
||||
.map(|f| {
|
||||
let mut path = PathBuf::from(&self.cwd);
|
||||
|
||||
let (file_name, contents) = match *f {
|
||||
Stub::EmptyFile(name) => (name, "fake data".to_string()),
|
||||
Stub::FileWithContent(name, content) => (name, content.to_string()),
|
||||
Stub::FileWithContentToBeTrimmed(name, content) => (
|
||||
name,
|
||||
content
|
||||
.lines()
|
||||
.skip(1)
|
||||
.map(|line| line.trim())
|
||||
.collect::<Vec<&str>>()
|
||||
.join(&endl),
|
||||
),
|
||||
};
|
||||
|
||||
path.push(file_name);
|
||||
|
||||
std::fs::write(path, contents.as_bytes()).expect("can not create file");
|
||||
})
|
||||
.for_each(drop);
|
||||
self.back_to_playground();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn within(&mut self, directory: &str) -> &mut Self {
|
||||
self.cwd.push(directory);
|
||||
std::fs::create_dir(&self.cwd).expect("can not create directory");
|
||||
self
|
||||
}
|
||||
|
||||
pub fn glob_vec(pattern: &str) -> Vec<PathBuf> {
|
||||
let glob = glob(pattern);
|
||||
|
||||
glob.expect("invalid pattern")
|
||||
.map(|path| {
|
||||
if let Ok(path) = path {
|
||||
path
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
pub use director::Director;
|
||||
pub use matchers::says;
|
||||
pub use nu_process::{Executable, NuProcess, NuResult, Outcome};
|
||||
pub use play::{Dirs, Playground};
|
||||
|
141
crates/nu-test-support/src/playground/director.rs
Normal file
141
crates/nu-test-support/src/playground/director.rs
Normal file
@ -0,0 +1,141 @@
|
||||
use super::nu_process::*;
|
||||
use std::ffi::OsString;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct Director {
|
||||
pub cwd: Option<OsString>,
|
||||
pub config: Option<OsString>,
|
||||
pub pipeline: Option<String>,
|
||||
pub executable: Option<NuProcess>,
|
||||
}
|
||||
|
||||
impl Director {
|
||||
pub fn cococo(&self, arg: &str) -> Self {
|
||||
let mut process = NuProcess::default();
|
||||
process.args(&["--testbin", "cococo", arg]);
|
||||
Director {
|
||||
config: self.config.clone(),
|
||||
executable: Some(process),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pipeline(&self, commands: &str) -> Self {
|
||||
let mut director = Director {
|
||||
pipeline: if commands.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(format!(
|
||||
"
|
||||
{}
|
||||
exit",
|
||||
commands
|
||||
))
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let mut process = NuProcess::default();
|
||||
|
||||
if let Some(working_directory) = &self.cwd {
|
||||
process.cwd(working_directory);
|
||||
}
|
||||
|
||||
process.arg("--skip-plugins");
|
||||
if let Some(config_file) = self.config.as_ref() {
|
||||
process.args(&[
|
||||
"--config-file",
|
||||
config_file.to_str().expect("failed to convert."),
|
||||
]);
|
||||
}
|
||||
|
||||
director.executable = Some(process);
|
||||
director
|
||||
}
|
||||
|
||||
pub fn executable(&self) -> Option<&NuProcess> {
|
||||
if let Some(binary) = &self.executable {
|
||||
Some(binary)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Executable for Director {
|
||||
fn execute(&self) -> NuResult {
|
||||
use std::io::Write;
|
||||
use std::process::Stdio;
|
||||
|
||||
match self.executable() {
|
||||
Some(binary) => {
|
||||
let mut process = match binary
|
||||
.construct()
|
||||
.stdout(Stdio::piped())
|
||||
.stdin(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()
|
||||
{
|
||||
Ok(child) => child,
|
||||
Err(why) => panic!("Can't run test {}", why.to_string()),
|
||||
};
|
||||
|
||||
if let Some(pipeline) = &self.pipeline {
|
||||
process
|
||||
.stdin
|
||||
.as_mut()
|
||||
.expect("couldn't open stdin")
|
||||
.write_all(pipeline.as_bytes())
|
||||
.expect("couldn't write to stdin");
|
||||
}
|
||||
|
||||
process
|
||||
.wait_with_output()
|
||||
.map_err(|_| {
|
||||
let reason = format!(
|
||||
"could not execute process {} ({})",
|
||||
binary, "No execution took place"
|
||||
);
|
||||
|
||||
NuError {
|
||||
desc: reason,
|
||||
exit: None,
|
||||
output: None,
|
||||
}
|
||||
})
|
||||
.and_then(|process| {
|
||||
let out =
|
||||
Outcome::new(&read_std(&process.stdout), &read_std(&process.stderr));
|
||||
|
||||
match process.status.success() {
|
||||
true => Ok(out),
|
||||
false => Err(NuError {
|
||||
desc: String::new(),
|
||||
exit: Some(process.status),
|
||||
output: Some(out),
|
||||
}),
|
||||
}
|
||||
})
|
||||
}
|
||||
None => Err(NuError {
|
||||
desc: String::from("err"),
|
||||
exit: None,
|
||||
output: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Director {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "director")
|
||||
}
|
||||
}
|
||||
|
||||
fn read_std(std: &[u8]) -> Vec<u8> {
|
||||
let out = String::from_utf8_lossy(std);
|
||||
let out = out.lines().collect::<Vec<_>>().join("\n");
|
||||
let out = out.replace("\r\n", "");
|
||||
out.replace("\n", "").into_bytes()
|
||||
}
|
106
crates/nu-test-support/src/playground/matchers.rs
Normal file
106
crates/nu-test-support/src/playground/matchers.rs
Normal file
@ -0,0 +1,106 @@
|
||||
use hamcrest2::core::{MatchResult, Matcher};
|
||||
use std::fmt;
|
||||
use std::str;
|
||||
|
||||
use super::nu_process::Outcome;
|
||||
use super::{Director, Executable};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Play {
|
||||
stdout_expectation: Option<String>,
|
||||
}
|
||||
|
||||
impl fmt::Display for Play {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "play")
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Play {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "play")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn says() -> Play {
|
||||
Play {
|
||||
stdout_expectation: None,
|
||||
}
|
||||
}
|
||||
|
||||
trait CheckerMatchers {
|
||||
fn output(&self, actual: &Outcome) -> MatchResult;
|
||||
fn std(&self, actual: &[u8], expected: Option<&String>, description: &str) -> MatchResult;
|
||||
fn stdout(&self, actual: &Outcome) -> MatchResult;
|
||||
}
|
||||
|
||||
impl CheckerMatchers for Play {
|
||||
fn output(&self, actual: &Outcome) -> MatchResult {
|
||||
self.stdout(actual)
|
||||
}
|
||||
|
||||
fn stdout(&self, actual: &Outcome) -> MatchResult {
|
||||
self.std(&actual.out, self.stdout_expectation.as_ref(), "stdout")
|
||||
}
|
||||
|
||||
fn std(&self, actual: &[u8], expected: Option<&String>, description: &str) -> MatchResult {
|
||||
let out = match expected {
|
||||
Some(out) => out,
|
||||
None => return Ok(()),
|
||||
};
|
||||
let actual = match str::from_utf8(actual) {
|
||||
Err(..) => return Err(format!("{} was not utf8 encoded", description)),
|
||||
Ok(actual) => actual,
|
||||
};
|
||||
|
||||
if actual != *out {
|
||||
return Err(format!(
|
||||
"not equal:\n actual: {}\n expected: {}\n\n",
|
||||
actual, out
|
||||
));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Matcher<Outcome> for Play {
|
||||
fn matches(&self, output: Outcome) -> MatchResult {
|
||||
self.output(&output)
|
||||
}
|
||||
}
|
||||
|
||||
impl Matcher<Director> for Play {
|
||||
fn matches(&self, mut director: Director) -> MatchResult {
|
||||
self.matches(&mut director)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Matcher<&'a mut Director> for Play {
|
||||
fn matches(&self, director: &'a mut Director) -> MatchResult {
|
||||
if director.executable().is_none() {
|
||||
return Err(format!("no such process {}", director));
|
||||
}
|
||||
|
||||
let res = director.execute();
|
||||
|
||||
match res {
|
||||
Ok(out) => self.output(&out),
|
||||
Err(err) => {
|
||||
if let Some(out) = &err.output {
|
||||
return self.output(out);
|
||||
}
|
||||
|
||||
Err(format!("could not exec process {}: {:?}", director, err))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Play {
|
||||
#[allow(clippy::clippy::wrong_self_convention)]
|
||||
pub fn to_stdout(mut self, expected: &str) -> Self {
|
||||
self.stdout_expectation = Some(expected.to_string());
|
||||
self
|
||||
}
|
||||
}
|
99
crates/nu-test-support/src/playground/nu_process.rs
Normal file
99
crates/nu-test-support/src/playground/nu_process.rs
Normal file
@ -0,0 +1,99 @@
|
||||
use crate::fs::executable_path;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use std::fmt;
|
||||
use std::path::Path;
|
||||
use std::process::{Command, ExitStatus};
|
||||
|
||||
pub trait Executable {
|
||||
fn execute(&self) -> NuResult;
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Outcome {
|
||||
pub out: Vec<u8>,
|
||||
pub err: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Outcome {
|
||||
pub fn new(out: &[u8], err: &[u8]) -> Outcome {
|
||||
Outcome {
|
||||
out: out.to_vec(),
|
||||
err: err.to_vec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type NuResult = Result<Outcome, NuError>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NuError {
|
||||
pub desc: String,
|
||||
pub exit: Option<ExitStatus>,
|
||||
pub output: Option<Outcome>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct NuProcess {
|
||||
pub arguments: Vec<OsString>,
|
||||
pub environment_vars: HashMap<String, Option<OsString>>,
|
||||
pub cwd: Option<OsString>,
|
||||
}
|
||||
|
||||
impl fmt::Display for NuProcess {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "`nu")?;
|
||||
|
||||
for arg in &self.arguments {
|
||||
write!(f, " {}", arg.to_string_lossy())?;
|
||||
}
|
||||
|
||||
write!(f, "`")
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for NuProcess {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
arguments: vec![],
|
||||
environment_vars: HashMap::default(),
|
||||
cwd: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NuProcess {
|
||||
pub fn arg<T: AsRef<OsStr>>(&mut self, arg: T) -> &mut Self {
|
||||
self.arguments.push(arg.as_ref().to_os_string());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn args<T: AsRef<OsStr>>(&mut self, arguments: &[T]) -> &mut NuProcess {
|
||||
self.arguments
|
||||
.extend(arguments.iter().map(|t| t.as_ref().to_os_string()));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn cwd<T: AsRef<OsStr>>(&mut self, path: T) -> &mut NuProcess {
|
||||
self.cwd = Some(path.as_ref().to_os_string());
|
||||
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() {
|
||||
command.current_dir(cwd);
|
||||
}
|
||||
|
||||
for arg in &self.arguments {
|
||||
command.arg(arg);
|
||||
}
|
||||
|
||||
command
|
||||
}
|
||||
}
|
216
crates/nu-test-support/src/playground/play.rs
Normal file
216
crates/nu-test-support/src/playground/play.rs
Normal file
@ -0,0 +1,216 @@
|
||||
use super::Director;
|
||||
use crate::fs;
|
||||
use crate::fs::Stub;
|
||||
use getset::Getters;
|
||||
use glob::glob;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str;
|
||||
use tempfile::{tempdir, TempDir};
|
||||
|
||||
pub struct Playground<'a> {
|
||||
root: TempDir,
|
||||
tests: String,
|
||||
cwd: PathBuf,
|
||||
config: PathBuf,
|
||||
dirs: &'a Dirs,
|
||||
}
|
||||
|
||||
#[derive(Default, Getters, Clone)]
|
||||
#[get = "pub"]
|
||||
pub struct Dirs {
|
||||
pub root: PathBuf,
|
||||
pub test: PathBuf,
|
||||
pub fixtures: PathBuf,
|
||||
}
|
||||
|
||||
impl Dirs {
|
||||
pub fn formats(&self) -> PathBuf {
|
||||
self.fixtures.join("formats")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Playground<'a> {
|
||||
pub fn root(&self) -> &Path {
|
||||
self.root.path()
|
||||
}
|
||||
|
||||
pub fn cwd(&self) -> &Path {
|
||||
&self.cwd
|
||||
}
|
||||
|
||||
pub fn back_to_playground(&mut self) -> &mut Self {
|
||||
self.cwd = PathBuf::from(self.root()).join(self.tests.clone());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn play(&mut self) -> &mut Self {
|
||||
self
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if PathBuf::from(&nuplay_dir).exists() {
|
||||
std::fs::remove_dir_all(PathBuf::from(&nuplay_dir)).expect("can not remove directory");
|
||||
}
|
||||
|
||||
std::fs::create_dir(PathBuf::from(&nuplay_dir)).expect("can not create directory");
|
||||
|
||||
let fixtures = fs::fixtures();
|
||||
let fixtures = dunce::canonicalize(fixtures.clone()).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: fixtures.join("playground/config/default.toml"),
|
||||
dirs: &Dirs::default(),
|
||||
};
|
||||
|
||||
let playground_root = playground.root.path();
|
||||
|
||||
let test = dunce::canonicalize(playground_root.join(topic)).unwrap_or_else(|e| {
|
||||
panic!(
|
||||
"Couldn't canonicalize test path {}: {:?}",
|
||||
playground_root.join(topic).display(),
|
||||
e
|
||||
)
|
||||
});
|
||||
|
||||
let root = dunce::canonicalize(playground_root).unwrap_or_else(|e| {
|
||||
panic!(
|
||||
"Couldn't canonicalize tests root path {}: {:?}",
|
||||
playground_root.display(),
|
||||
e
|
||||
)
|
||||
});
|
||||
|
||||
let dirs = Dirs {
|
||||
root,
|
||||
test,
|
||||
fixtures,
|
||||
};
|
||||
|
||||
playground.dirs = &dirs;
|
||||
|
||||
block(dirs.clone(), &mut playground);
|
||||
}
|
||||
|
||||
pub fn with_config(&mut self, source_file: impl AsRef<Path>) -> &mut Self {
|
||||
self.config = source_file.as_ref().to_path_buf();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn get_config(&self) -> &str {
|
||||
self.config.to_str().expect("could not convert path.")
|
||||
}
|
||||
|
||||
pub fn build(&mut self) -> Director {
|
||||
Director {
|
||||
cwd: Some(self.dirs.test().into()),
|
||||
config: Some(self.config.clone().into()),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cococo(&mut self, arg: &str) -> Director {
|
||||
self.build().cococo(arg)
|
||||
}
|
||||
|
||||
pub fn pipeline(&mut self, commands: &str) -> Director {
|
||||
self.build().pipeline(commands)
|
||||
}
|
||||
|
||||
pub fn mkdir(&mut self, directory: &str) -> &mut Self {
|
||||
self.cwd.push(directory);
|
||||
std::fs::create_dir_all(&self.cwd).expect("can not create directory");
|
||||
self.back_to_playground();
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn symlink(&mut self, from: impl AsRef<Path>, to: impl AsRef<Path>) -> &mut Self {
|
||||
let from = self.cwd.join(from);
|
||||
let to = self.cwd.join(to);
|
||||
|
||||
let create_symlink = {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
std::os::unix::fs::symlink
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
if from.is_file() {
|
||||
std::os::windows::fs::symlink_file
|
||||
} else if from.is_dir() {
|
||||
std::os::windows::fs::symlink_dir
|
||||
} else {
|
||||
panic!("symlink from must be a file or dir")
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
create_symlink(from, to).expect("can not create symlink");
|
||||
self.back_to_playground();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_files(&mut self, files: Vec<Stub>) -> &mut Self {
|
||||
let endl = fs::line_ending();
|
||||
|
||||
files
|
||||
.iter()
|
||||
.map(|f| {
|
||||
let mut path = PathBuf::from(&self.cwd);
|
||||
|
||||
let (file_name, contents) = match *f {
|
||||
Stub::EmptyFile(name) => (name, "fake data".to_string()),
|
||||
Stub::FileWithContent(name, content) => (name, content.to_string()),
|
||||
Stub::FileWithContentToBeTrimmed(name, content) => (
|
||||
name,
|
||||
content
|
||||
.lines()
|
||||
.skip(1)
|
||||
.map(|line| line.trim())
|
||||
.collect::<Vec<&str>>()
|
||||
.join(&endl),
|
||||
),
|
||||
};
|
||||
|
||||
path.push(file_name);
|
||||
|
||||
std::fs::write(path, contents.as_bytes()).expect("can not create file");
|
||||
})
|
||||
.for_each(drop);
|
||||
self.back_to_playground();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn within(&mut self, directory: &str) -> &mut Self {
|
||||
self.cwd.push(directory);
|
||||
std::fs::create_dir(&self.cwd).expect("can not create directory");
|
||||
self
|
||||
}
|
||||
|
||||
pub fn glob_vec(pattern: &str) -> Vec<PathBuf> {
|
||||
let glob = glob(pattern);
|
||||
|
||||
glob.expect("invalid pattern")
|
||||
.map(|path| {
|
||||
if let Ok(path) = path {
|
||||
path
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
47
crates/nu-test-support/src/playground/tests.rs
Normal file
47
crates/nu-test-support/src/playground/tests.rs
Normal file
@ -0,0 +1,47 @@
|
||||
use crate::playground::Playground;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use super::matchers::says;
|
||||
use hamcrest2::assert_that;
|
||||
use hamcrest2::prelude::*;
|
||||
|
||||
fn path(p: &Path) -> PathBuf {
|
||||
dunce::canonicalize(p)
|
||||
.unwrap_or_else(|e| panic!("Couldn't canonicalize path {}: {:?}", p.display(), e))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn asserts_standard_out_expectation_from_nu_executable() {
|
||||
Playground::setup("topic", |_, nu| {
|
||||
assert_that!(nu.cococo("andres"), says().to_stdout("andres"));
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn asserts_standard_out_expectation_from_nu_executable_pipeline_fed() {
|
||||
Playground::setup("topic", |_, nu| {
|
||||
assert_that!(nu.pipeline("echo 'andres'"), says().to_stdout("andres"));
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn current_working_directory_in_sandbox_directory_created() {
|
||||
Playground::setup("topic", |dirs, nu| {
|
||||
let original_cwd = dirs.test();
|
||||
nu.within("some_directory_within");
|
||||
|
||||
assert_eq!(path(&nu.cwd()), original_cwd.join("some_directory_within"));
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn current_working_directory_back_to_root_from_anywhere() {
|
||||
Playground::setup("topic", |dirs, nu| {
|
||||
let original_cwd = dirs.test();
|
||||
|
||||
nu.within("some_directory_within");
|
||||
nu.back_to_playground();
|
||||
|
||||
assert_eq!(path(&nu.cwd()), *original_cwd);
|
||||
})
|
||||
}
|
Reference in New Issue
Block a user