Conversation
thufschmitt
left a comment
There was a problem hiding this comment.
That’s great :)
(The tests here are probably way to simple to be really useful, but it’s great to have a foundation for writing more of these).
I think something must be done about the EvalState issue, but apart from that the comments are rather minor
src/libexpr/tests/primops.cc
Outdated
| : store(openStore("dummy://")) | ||
| , state({"."}, store) |
There was a problem hiding this comment.
This will probably be problematic as EvalState is inherently stateful, so having several instances of it at the same time is very likely to break.
Maybe we should instead have only one global EvalState for the whole testsuite as long as reentrance isn’t guaranteed (which obviously would be detrimental for purity, but would prevent a whole class of false-positives)
There was a problem hiding this comment.
Could you point out where it is stateful beyond its own lifetime? Happy to start reducing the statefulness of it as part of this work.
There was a problem hiding this comment.
I've now used this extensively and with that one primOp fix applied I've not seen any issues. I used this work to fuzz the parser multiple thousand times per second within one process for a few days without issues. If there is still some global state (besides the GC) I can't find it....
|
(Also: I think you accidentally committed the binary) |
Yeah. This was pretty much a WIP commit after my evening activities yesterday. Just wanted to put it out there in case I never get back to it and someone else wants to pick up on that. I've once before written these tests (and a dummy store implementation) but never submitted the PR... :/ I'll try to go through your review comments in the evening. Meanwhile: The EvalState teardown seems to actually work. I've added a couple of tests (same content, just different names) to do a smoke test if things start failing after a "magic" number of tests. They didn't so that was good. In any case the tests using the On the EvalState argument: I'd rather work towards a setup where we can have multiple of those instances. I would like this approach to be useful for things like fuzzing the Nix language/interpreter and requiring a common shared state or process restart isn't really going to cut it. Parsing will leak memory (which will be a bit of a downer for fuzzing etc..) but that is probably tolerable for now. |
|
https://github.com/NixOS/nix/tree/master/tests/lang might be rewritten to C++ with gtest |
I purposely want the simple expressions as well. They are fast and if they break something is fundamentally broken. Not sure if makes sense to move those tests over. Perhaps in the long run but that isn't my call. |
I meant that in that case we could see the test coverage of libexpr and what tests need be added |
|
Picking up my debugging quest I've found out that the primOps aren't always registered with the correct arity when more than one EvalState is being initialized: |
|
As it turned out, the arguments were moved out of the registration structure and thus every initialization after would get the wrong data. If we really care about that small amount of memory overhead, we should put them into a shared_ptr that is cheap to clone. As far as I can tell the evaluator only needs the structure to figure out how many arguments should there be. There is a one time operation. It is probably more of an impact on the new repl |
82356ae to
f39a988
Compare
|
@regnat mind giving this another review? |
|
Note that we already have libexpr tests in |
I wanted some simple & fast tests that ensure that some basic assumptions around the language continue to hold true. It also serves as a way to ensure that we can eval in multiple separate "environments" (multiple EvalStates) during the runtime of a single process. The tests currently execut within ~20ms and compile within a couple of seconds. I don't need a full (re)build of Nix executables to verify my changes aren't breaking fundamental assumptions. I don't think this will replace the tests we have in The In the past made several attempts at refactoring various parts in |
3b1008d to
e15376f
Compare
| mkString(v, "test"); | ||
| ASSERT_EQ(getJSONValue(v), "\"test\""); | ||
| } | ||
|
|
There was a problem hiding this comment.
Maybe you could also add a test for unicode strings here - and as for "how is this even relevant" I would probably just say: it helps to stick to the same behaviour 🤷
d6d0f80 to
a8c3e65
Compare
|
Rebased one more time and added another corner case that is odd in our current language implementation ( |
The example was previously indicating that multiple whitespaces would be collapsed into a single captured whitespace. That isn't true and was likely a mistake when being documented initially.
Since lists are just chunks of memory the individual elements in the list might be unitilized when a programming error happens within Nix. In this case the values are null-initialized (at least with Boehm GC) and we can avoid a nullptr deref when printing them. I ran into this issue while ensuring that new expression tests would show the actual value on an assertion failure. This is unlikely to cause any runtime performance regressions as printing values is not really in the hot path (unless the repl is the primary use case).
This introduces tests for libexpr that evalulate various trivial Nix language expressions and primop invocations that should be good smoke tests wheter or not the implementation is behaving as expected.
thufschmitt
left a comment
There was a problem hiding this comment.
@andir Sorry for taking so long to get back to it.
I’ve rebased the branch on top of master. I let you double-check that I didn’t screw anything up in the process if you want, and I think we can merge it afterwards
I did a brief casual browsing through the diff and I couldn't see anything that is obviously wrong. Go ahead and merge :-) |
|
I'm not very happy that we now have two different test suites for the evaluator. This just makes it confusing whether tests should go into libexpr-tests or tests/lang. If this PR had replaced tests/lang with libexpr-tests, that would have been one thing, but now it just increases maintainer burden. |
|
The other problem is that libexpr-tests is intimately tied to internal APIs, such as the Value representation. So changes to those internal interfaces (e.g. |
|
This style of writing tests is also extremely verbose. Take this one: This is a very verbose way of saying that the result should be equal to |
A bit yes. But at the same time I wouldn’t want to commit to it too quickly (esp. wrt the dependency on the internal APIs that you mention: a priori, I think it’s a worthwile tradeoff, but I’d rather give it some time too ensure that it doesn’t cause too much pain before making a hard-to-revert change). Keeping it in addition to the existing testsuite makes it so that we have time to see how that’s gonna work out and migrate incrementally what we think needs to be migrated.
Yes, it is :) Maybe we could just compare the two values for this kind of more complex examples (either print the result and compare the strings, or use the equality operator from Nix).
Yup’ this should have been settled on and documented more clearly. My take on that is that we should try to write new tests in libexpr-tests (both to make the experiment meaningful and because despite the verbosity overhead writing the tests here is so much faster). I’ll open a PR for that, letting you judge whether that seems a good plan. |
|
This pull request has been mentioned on NixOS Discourse. There might be relevant details there: |
This is an initial step towards working on the libexpr code and trying
to optimize it. Having unit tests will be a great starting point to
write a few benchmarks that focus on various features of NIx. Those
might not have to be part of the code base but are likely very helpful
while developing.