-
Notifications
You must be signed in to change notification settings - Fork 40
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
What is needed for 1.0? #74
Comments
#63 already includes a good heap of feedback |
@coreyja, @azriel91, @AnderEnder, @dbrgn, @fernandobatels, @mssun, @gnzlbg, @quininer, @zetok: you all have participated in some issues. Could you provide input on this? |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
I think I am running into #51 right now in my testing. Here is the code I am working with
Color commands from https://github.com/mackwic/colored I am also fairly new to Rust, so I am still working on getting my head how lifetimes work, and I also can't comment on how Rust-y the code is! To answer some of your questions:
It pretty much IS my testing strategy so far, though my app under test is pretty small. See https://github.com/coreyja/devicon-lookup/blob/master/tests/integration.rs This library has made it really easy to add some test coverage to my project, even when I am just learning how to write Rust! |
Recently I use
Might be a bit out of topic but I would love it if |
This comment has been minimized.
This comment has been minimized.
I've not dealt with a REPL yet, so I'm a bit lacking on knowing what kinds of challenges there are and what could be done to help. In what ways are you thinking that the experience could be improved over just using rexpect?
Hmm, currently we don't have output sanitizing and I know there are people who want to explore testing things like color, so I don't think we should cover it universally. However, I think it'd be a great idea for us to have a predicate modifier that could strip them, like our modifier for normalizing newlines. I've created assert-rs/predicates-rs#76 for this. |
As food for thoughts, here's how I'm using The guiding idea is that I build a struct describing programs to run/delay/kill, a set of input files, and a set of post-run checks that operate on created files (including the program's stdout/stderr). I can /// Main struct described above. It's got builder-pattern methods,
/// and a `run` method that will do all the work and `assert` upon failure.
pub struct Exec {
cmds: Vec<Cmd>,
timeout: Duration,
files: Vec<FileIn>,
checks: Vec<FilePredicate>,
}
pub struct Cmd {
/// Program to run.
bin: Command,
/// Command name for `after()`, std/err filname prefix, and logs.
name: String,
/// Expected exit status. Some(0)=success, Some(n)=failure, None=timeout.
exit: Option<i32>,
/// Fail if the cmd exits too early.
mintime: Duration,
/// Fail if the cmd run for too long.
maxtime: Duration,
/// Current state.
state: CmdState,
/// List of signals to send to the process after startup.
signals: Vec<(CmdCond, c_int)>,
}
enum CmdState {
Wait(CmdCond),
Started(Child, Instant),
Done,
}
pub enum CmdCond {
/// Immediately true
None,
/// Duration elapsed
Delay(Duration),
/// Other Cmd exited
Cmd(String),
/// File-based predicate
Predicate(FilePredicate),
}
pub struct FilePredicate {
/// Desciption for assert-logging purpose.
desc: String,
/// Which file to operate on.
file: String,
/// Closure that tests the content of the file.
pred: Box<dyn Fn(&str) -> bool>,
}
pub enum FileIn {
FromFs(&'static str, &'static str),
Bin(&'static str, Vec<u8>),
} With that in place, I have a pretty powerful way to write a unittest, using my crate's // As basic as it gets.
exec().cmd(Cmd::any("echo", "echo", "-n a")).check("echo a", "echo.out", |s| s == "a").run();
// File from hardcoded data.
exec().inbytes("a", "a")
.cmd(Cmd::any("cat", "cat", "a"))
.check("cat a", "cat.out", |s| s == "a")
.run();
// File from file in test/ directory.
exec().infile("j", "input.basic.json")
.cmd(Cmd::any("cat", "cat", "j"))
.check("cat j", "cat.out", |s| s.starts_with("{"))
.run();
// run sequentially
let start = Instant::now();
exec().cmd(Cmd::any("s1", "sleep", "0.3"))
.cmd(Cmd::any("s2", "sleep", "0.3").after("s1"))
.cmd(Cmd::any("s3", "sleep", "0.3").after("s2"))
.cmd(Cmd::any("cat", "cat", "s1.out s2.out s3.out").after("s3"))
.run();
assert!(Instant::now() - start > Duration::from_millis(900));
// delayed start
exec().cmd(Cmd::any("0", "cat", "1.out 2.out").after(20))
.cmd(Cmd::any("1", "echo", "a"))
.cmd(Cmd::any("2", "echo", "b"))
.check("cat", "0.out", |s| s == "a\nb\n")
.run();
// timeout
exec().cmd(Cmd::any("s", "sleep", "1").maxtime(100).exit(None)).run();
exec().cmd(Cmd::any("s", "sleep", "0.05").mintime(50).maxtime(70)).run();
// Multiple signals, ordered by Instant.
exec().cmd(Cmd::any("s", "sleep", "1").signal(SIGINT, 70) // Added first but triggers last
.signal(SIGCONT, 10) // Triggers first but doesn't terminate
.signal(SIGTERM, 30) // Actual terminator
.exit(SIGTERM))
.run();
// Signal after another cmd exit.
exec().cmd(Cmd::any("s1", "sleep", "1").signal(SIGINT, "s2").maxtime(50).exit(SIGINT))
.cmd(Cmd::any("s2", "sleep", "0.01"))
.run();
// Signal after file content matches
exec().cmd(Cmd::any("s1", "sleep", "1").signal(SIGINT, ("s2.out", "4"))
.maxtime(100)
.exit(SIGINT))
.cmd(Cmd::bash("s2", "for i in $(seq 10);do echo $i;sleep 0.01;done"))
.timeout(1000)
.run();
// Main binary
exec().cmd(Cmd::main("m", "-V")).check("progname", "0.out", |s| s.starts_with("mybin"));
// Set/unset env
exec().cmd(Cmd::any("env", "env", "").env("foo", "bar")
.env("PATH", &format!("/ut:{}", env::var("PATH").unwrap()))
.env_remove("HOME"))
.check("added_foo", "env.out", |s| s.lines().any(|l| l == "foo=bar"))
.check("changed_path", "env.out", |s| s.lines().any(|l| l.starts_with("PATH=/ut:")))
.check("removed_home", "env.out", |s| !s.lines().any(|l| l.starts_with("HOME=")))
.run(); If there's enough interest, I could try to get this cleared for upstreaming. Currently there are a few extensions and API decisions that are specific to my project, and the code is a bit ad-hoc in places. |
Thanks for sharing this! There is a lot here, so let's see what a break down looks like:
So this crate started as
This is what led to the current extension-trait API. The challenge with this API design is carrying state with it because the So options for
Resolving this is going to be the next thing I focus on. Now for command orchestration. That seems like it is going beyond the planned scope for I would be curious to know what is the use case that leads you to need command orchestration so I can better evaluate whether what I just said is right or not :).
Is there a reason you use this rather than |
@vincentdephily where can I find your code so I can steal parts of it :) Just posted 0.12 with a |
@epage Currently the code isn't public. I've redacted the file to keep only the relevant parts, and will get that cleared for release. I could probably have used A wrapper API is easier to design, that's what I went after with my I won't have time for a PR, but should be able to help with the design discussions and dogfooding. |
I can understand the release issues and the time. For now, I'm interested in seeing how you handled timeouts. If you have anything to add to #10, I'd appreciate it. I've started down the path of providing a wrapper type. This cleaned up |
I've done a quick filtering out of the private code and got the thumbs up to share https://gist.github.com/vincentdephily/592546c41ff9713adff234e5535aa6d4 which you can treat as public domain. I've added a few |
Created a new issue for vincentdephily's fancy stuff. I feel we're ready to close this one out. |
I know this is some time ago, but I just stumbled across this and felt like it was worth mentioning. While I don't have a problem with this, and don't know how you've written to other people that provided feedback over email, at least I wasn't told that my feedback would be made public. I think this would be the correct thing to do in these situations. |
Sorry, I hadn't considered that at the time but that is the right thing to do. |
Collecting feedback or assert_fs. Please post even if you think it might not be needed for 1.0 so we can try to get the big picture of uses, challenges, etc. We're also looking at including experience reports in the 1.0 announcement blog post.
Preferably, please update to the latest version first but don't let upgrading stop you from providing feedback
To help spur feedback, consider these questions
Also, if your programs deals with the file system, please provide feedback on assert_fs. Even if you don't use it, I want to know why.
Summary
Areas to improve
stdin
handling is an unfriendly API #73stdin
handling is an unfriendly APISuccesses
fitzgen
@passcod
@volks73
@coreyja
The text was updated successfully, but these errors were encountered: