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
Add tests and an example solution to Rust training Project 1 #10
Conversation
Signed-off-by: Yilin Chen <sticnarf@gmail.com>
Signed-off-by: Yilin Chen <sticnarf@gmail.com>
I am not so confident about the answer to question D and E. I thought it unimportant whether to run an empty test or not. Running an empty test just looks more consistent. Still, I wrote something as the answer. As to question E, I must admit I didn't seen this pattern before, and now I still don't see how we can benifit from this pattern. |
rust/projects/tools/answers.md
Outdated
Answer: The compilation of our library file `lib.rs` is independent from | ||
`main.rs` and `tests.rs`. The library becomes an implicit dependency of | ||
`main.rs` and `tests.rs`, and is linked to the binaries `main.rs` and | ||
`tests.rs` compile into. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be worth mentioning that cargo decides what to do with all three of these files implicitly, that lib.rs is compiled into a library because cargo recognizes src/lib.rs
specially; that main.rs
is compiled into an executable because cargo interprets any rust file in src/bin/
as an executable; tests.rs
is compiled into test binary via cargo test
because cargo treats any source file in ... etc.
``` | ||
|
||
In fact, if you move `src/bin/main.rs` to `src/main.rs`, the binary name | ||
will be the same as the package name (`kvs` in this case). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good answer. It makes me think we should just set up the project differently, with main.rs
in src/
, and get rid of the question. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I think we can just use src/main.rs
but leave the question to be "modify the binary name in different ways".
Answer: `cargo test` run unit tests, integration tests and documentation | ||
tests. In our case, the four test suites include two unit test suites from | ||
the binary and the library respectively, one integration test suite from | ||
`tests/tests.rs` and one documentation test from the library only. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"cargo tet
run unit tests" -> "... runs unit tests"
rust/projects/tools/answers.md
Outdated
|
||
Answer: An empty test suite also needs the library to be built and linked | ||
to it. So we can use `cargo test` to make sure our library can be | ||
successfully built and linked. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The main reason having these empty test suites can be problematic is because they each need to be compiled and linked, which becomes more and more time consuming as a project grows. To run the tests in a library, for example, requires building the library once as a library, so other crates can link to it, and then a second time, as an executable.
Your first sentence captures that, but not in much detail. The second sentence I don't understand.
Can you do a little rework here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I misunderstood the question. I will do the rework.
[[bin]] | ||
name = "main" | ||
test = false | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice. I wasn't actually sure that doc tests could be disabled.
rust/projects/tools/answers.md
Outdated
Not exposing public APIs prevents us from distributing the library to other | ||
users who need the functions inside and do not need the user interface. | ||
Even though there are necessary public APIs, the user interface is definitely | ||
a waste of space and confusing to other users. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your last sentence here seems most important to me. I think it needs more explanation about what "waste of space" means. The UI is going to have dependencies that are not needed by the library, so any library consumers are going to be forced to link to UI code they don't need, and that will increase their compilation time and binary size.
Can you move the last sentence to the top and clarify it?
Hey @sticnarf! This all looks great, and I'm glad you've found some areas where things don't really fit together. It seems like you've uncovered a few issues that need to be reconsidered:
That last issue is one I've considered in the past, but it seems to have slipped my mind when I wrote the project. Offhand I only see a few solutions:
Upon investigation, it looks like cargo will compile the projects bins before it runs tests, so that last solution seems viable. I think we should land our outstanding PRs before trying to solve these problems though. The code commit looks great.
I left inline comments.
I left a comment describing what I see as the big downside (duplicated compiling and linking). If you don't find that insightful then we should probably delete the question.
It's kind of a cute trick, but it's definitely an anti-pattern in most code. I worded the question too strongly by saying it "often a reasonable thing to do". Treating a crate as both a library and executable does come in handy sometimes though. An obvious example is the stock test runner, which instruments your library with a synthetic main function and then compiles it into a binary. One has to do something similar manually when using e.g. the criterion benchmark tool - when you use it you define main yourself, so putting criterion benchmarks in a library requires writing a main function. Still, I'm convinced this is too esoteric a subject, and we can avoid talking about it by changing the cli test setup. So I left a few change requests. Please do them. I'll move on to project 2 so that Thursday you have something to work on. If you have solutions for any of the problems above I'm glad to hear them. How long did it take you to write the solutions? Should we expand the scope of this project, maybe with CI setup? |
Signed-off-by: Yilin Chen <sticnarf@gmail.com>
Signed-off-by: Yilin Chen <sticnarf@gmail.com>
Signed-off-by: Yilin Chen <sticnarf@gmail.com>
I have done the rework you mentioned. Please take a look again. I agree we should add CI setup. I find PingCAP projects use CircleCI mostly, that may be a good choice IMO. A problem I can think of is that perhaps it's hard to evaluate this task. Writing the answers and code took me about four or five hours. That's longer than expected because I got stuck in some problems I have no idea to solve (as you saw above) and was struggling with English writing which I am not very comfortable with yet. However, I feel happy as I think all these work is beneficial to me. |
I think it's still possible for us to test the cli using the rust test framework without changing our project structure. For example: #[test]
fn cli_no_args() {
let output = Command::new(binary_path())
.output()
.expect("failed to execute kvs binary");
assert!(!output.status.success())
}
// Path to kvs binary
fn binary_path() -> PathBuf {
// Path to cargo executables
// Adapted from https://github.com/rust-lang/cargo/blob/485670b3983b52289a2f353d589c57fae2f60f82/tests/testsuite/support/mod.rs#L507
let mut path = current_exe()
.ok()
.map(|mut path| {
path.pop();
if path.ends_with("deps") {
path.pop();
}
path
})
.unwrap();
path.push("kvs");
path
} |
Should the example solution contain the extension code? I thought it inappropriate not to show the common clap code and I didn't know where to put the structopt code so I didn't do that. |
Good question. For now I think no, per your comments. We can reconsider later. In #7 @nrc found one of the other questions unnecessary, so I think this idea of putting questions in the project is not working out and we should just remove them. Since we need to start on project 2, and I want to be able to see the existing content with the new content, I'm going to merge this now. Any additional work you still need to do on it can be in another PR. I hope that's ok with you. |
This pr is based on #7 .
The structopt extension is not included yet.
I don't know how to test the cli in integration tests, so I only write tests for
KvStore
.The clap
get_matches
function uses os_args, which I cannot easily hack.Another way is to use
get_matches_from_safe
and pass args in, but that will make the signature of ourmain
function horrible to beginners.assert_cli
orassert_cmd
should work too. But then I cannot think of the advantage of placing the user interface into the library.