Migrate expect tests to ocamltest#1519
Conversation
|
Cc @diml |
These actions can be used to make the execution of a test depend on whether the compiler has been configured with -flat-float-array or -no-flat-float-array.
e58260e to
adfa328
Compare
|
FWIW, this PR passes Inria's precheck in addition to AppVeyor and Travis. |
|
|
||
| val setup_build_env : Actions.t | ||
|
|
||
| val setup_simple_build_env : Actions.t |
There was a problem hiding this comment.
What is setup_simple_build_env doing compared to setup_build_env?
| [ | ||
| Builtin_variables.reference, input_file; | ||
| Builtin_variables.output, output_file | ||
| ] env2 in |
There was a problem hiding this comment.
An intermediary file input_file ^ ".corrected" is generated by running the command twice. Is this intermediary file simply ignored, or will it get automatically cleaned by ocamltest?
| flags env; | ||
| repo_root; | ||
| principal_flag; | ||
| input_file |
There was a problem hiding this comment.
The original invocation command
TERM=dumb $(EXPECT_TEST) $(EXPECT_FLAGS) -repo-root $(OTOPDIR) $$filealso starts with a TERM=dumb setting to control the behavior of toplevel location printing (I suppose). Is it reproduced here, for example by being globally set by ocamltest, or by Actions_helpers.run_cmd?
ocamltest/ocaml_actions.ml
Outdated
| Builtin_variables.output, output_file | ||
| ] env2 in | ||
| Pass output_env | ||
| | Skip _ | Fail _ -> second_run |
There was a problem hiding this comment.
This is minor, but I find the code more readable if the short cases are first (Skip _ | Fail _ before Pass env). Otherwise when I read the short cases, I don't remember well what was being matched.
| Actions_helpers.run_script log newenv | ||
| let ocamlsrcdir = ocamlsrcdir () in | ||
| let input_file = Actions_helpers.testfile env in | ||
| run_expect_twice ocamlsrcdir input_file log env |
There was a problem hiding this comment.
When are the result and reference file actually compared? I don't see a call to Actions_helpers.check_output, which I thought was used for this.
|
Thanks a lot for the carefull review, @gasche
Gabriel Scherer (2017/12/11 23:34 +0000):
gasche commented on this pull request.
> @@ -24,6 +24,10 @@ val dumpenv : Actions.t
val unix : Actions.t
val windows : Actions.t
+val setup_build_env : Actions.t
+
+val setup_simple_build_env : Actions.t
What is `setup_simple_build_env` doing compared to `setup_build_env`?
It's definitely not obvious and should ultimately be documented, I
agree.
setup_build_env sets up a build environment in the test_build_directory
variable. setup_simple_build_env calls setup_build_env after having set
the value of test_build_directory to the value of
test_build_directory_prefix.
The intention is this. When you run ocamltest on, say, foo.ml, the test
build directory prefix will be _ocamltest/foo. For tests like the expect
test where things get compiled only once, this can take place under this
directory, directly. For tests like bytecode or native which involve
several compilations, each compilation takes place in a different
subdirectory (named according to the compiler which is used) under the
test build directory prefix.
Does this respond to your question, @gasche?
> +let run_expect_twice ocamlsrcdir input_file log env =
+ let corrected filename = Filename.make_filename filename "corrected" in
+ let first_run = run_expect_once ocamlsrcdir input_file false log env in
+ match first_run with
+ | Pass env1 ->
+ let intermediate_file = corrected input_file in
+ let second_run =
+ run_expect_once ocamlsrcdir intermediate_file true log env1 in
+ (match second_run with
+ | Pass env2 ->
+ let output_file = corrected intermediate_file in
+ let output_env = Environments.add_bindings
+ [
+ Builtin_variables.reference, input_file;
+ Builtin_variables.output, output_file
+ ] env2 in
An intermediary file `input_file ^ ".corrected"` is generated by
running the command twice.
No the intermediate file is generated by the first run of the command.
Is this intermediary file simply ignored, or will it get automatically
cleaned by ocamltest?
It's used as input for the second run of the command.
> @@ -450,11 +459,51 @@ let ocamlopt_opt = Actions.make
"ocamlopt.opt"
(compile_test_program Builtin_variables.program2 ocamlopt_opt_compiler)
+let run_expect_once ocamlsrcdir input_file principal log env =
+ let expect_flags = try Sys.getenv "EXPECT_FLAGS" with Not_found -> "" in
+ let repo_root = "-repo-root " ^ ocamlsrcdir in
+ let principal_flag = if principal then "-principal" else "" in
+ let commandline =
+ [
+ expect_command ocamlsrcdir;
+ expect_flags;
+ flags env;
+ repo_root;
+ principal_flag;
+ input_file
The original invocation command
```shell
TERM=dumb $(EXPECT_TEST) $(EXPECT_FLAGS) -repo-root $(OTOPDIR) $$file
```
also starts with a `TERM=dumb` setting to control the behavior of
toplevel location printing (I suppose). Is it reproduced here, for
example by being globally set by ocamltest, or by
`Actions_helpers.run_cmd`?
It's reproduced in testsuite/Makefile
> + let first_run = run_expect_once ocamlsrcdir input_file false log env in
+ match first_run with
+ | Pass env1 ->
+ let intermediate_file = corrected input_file in
+ let second_run =
+ run_expect_once ocamlsrcdir intermediate_file true log env1 in
+ (match second_run with
+ | Pass env2 ->
+ let output_file = corrected intermediate_file in
+ let output_env = Environments.add_bindings
+ [
+ Builtin_variables.reference, input_file;
+ Builtin_variables.output, output_file
+ ] env2 in
+ Pass output_env
+ | Skip _ | Fail _ -> second_run
This is minor, but I find the code more readable if the short cases
are first (`Skip _ | Fail _` before `Pass env`). Otherwise when I read
the short cases, I don't remember well what was being matched.
Good idea, thanks. I did that in an additional commit to ease reviewing
but plan to sqaush in the existing history, ultimately.
> let run_expect log env =
- let newenv = Environments.apply_modifiers env Ocaml_modifiers.expect in
- Actions_helpers.run_script log newenv
+ let ocamlsrcdir = ocamlsrcdir () in
+ let input_file = Actions_helpers.testfile env in
+ run_expect_twice ocamlsrcdir input_file log env
When are the result and reference file actually compared? I don't see
a call to `Actions_helpers.check_output`, which I thought was used for
this.
You're correct! It's done in ocaml_tests.ml where the expect test is
defined.
|
|
Yes, thanks for the clarification on the Re.
I understand that, but I wonder what becomes of this intermediate file after the second run. Is it removed / cleaned up ? In the original Makefile, |
|
Gabriel Scherer (2017/12/12 09:08 +0000):
Yes, thanks for the clarification on the `setup{,_simple}_build_env`
functions.
Great.
Re. `input_file ^ ".corrected"`
> No the intermediate file is generated by the first run of the command.
> It's used as input for the second run of the command.
I understand that, but I wonder what becomes of this intermediate file
after the second run. Is it removed / cleaned up ?
No it's kept, on purpose. I thought it may be useful to have it, for
debugging purposes.
In the original
Makefile, `mv foo.corrected.corrected foo.corrected` is performed
after the second run, which guarantees that, by the time the script
terminates, only two files are left on the disk, the original input
file and the output file (of the second run).
Correct. To me this important also because all the builds happened in
the source tree. With ocamltest where the builds are done in a dedicated
build directory, I thought it was a better approach to keep both files.
They are not that big and I think the inconvenience of keeping them is
smaller than the inconvenience of not having them when they are needed.
|
gasche
left a comment
There was a problem hiding this comment.
Thanks for the clarifications. Good to merge.
|
Gabriel Scherer (2017/12/12 09:16 +0000):
gasche approved this pull request.
Thanks for the clarifications. Good to merge.
Thanks. I'm planning to also include in this PR the move of expect to
the ocamltest directory so that both get built at the same time.
Is it okay to do that? Shall I wait for @diml's approval?
|
|
Gabriel Scherer (2017/12/12 01:18 -0800):
Merged #1519.
Doh, that was a big quick! But thanks.
|
|
@gasche why did you add a Changes entry for this PR? Do you intent to do that for all the test migration PRs? |
|
I thought that there was enough non-trivial work in this PR that you deserved to be credited for the work (and I for the review!). I guessed that you had opted out of the the Changes-entry-writing business because you might not be so comfortable with it, so instead of pinging you to write one before I merge I decided to just write it. We can consolidate this entry for other test migrations, and/or only mention the ones that require significant amount of adaptation work. |
|
Gabriel Scherer (2017/12/12 10:21 +0000):
I thought that there was enough non-trivial work in this PR that you
deserved to be credited for the work (and I for the review!).
Well I personnally really prefer to have Changes document user-visible
changes, I find it more objective and am not so much looking for being
credited. But I fully respect your own wish to be credited.
I guessed that you had opted out of the the Changes-entry-writing
business because you might not be so comfortable with it, so instead
of pinging you to write one before I merge I decided to just write it.
No I don't mind writing changes entry when they seem legitimate to me.
The thing I would have want to do before merging, though, was the squashing I
mentionned in my previous comment.
|
|
I just rebased past this. |
|
sliquister (2017/12/13 05:14 +0000):
I just rebased past this.
Which PR are your referring to, here, please?
Can ocamltest put TERM=dumb in the environment of ocaml-expect,
otherwise the tests break because colors?
See PR #1527 which proposes an alternative way to achieve this.
Also is there any way to see the diff of the failure now,
Normally the log file of the tests should include a diff when the result
differs from the reference.
and to promote the corrected file?
No, that has not been implemented. But promoting is just a matter of
moving / renaming a file, right?
|
|
Promoting matters in practice when operating on a large number of tests whose output has to change. For example when working on #1511, I had to update the reference files of all the expect tests in the testsuite, and this was done with a |
|
Gabriel Scherer (2017/12/13 08:05 +0000):
Promoting matters in practice when operating on a large number of
tests whose output has to change. For example when working on #1511, I
had to update the reference files of all the expect tests in the
testsuite, and this was done with a `for f in tests/*; do cd $f; make
promote; done`.
Understood. However, I have to say that, at the moment, I don't really
know how promoting should be defined in the general set-up of ocamltest.
Ideas welcome, patches even more! ;-)
|
|
Right now you can compute a diff between the expected output and the output; it may be possible to add a |
|
Gabriel Scherer (2017/12/13 09:24 +0000):
Right now you can compute a diff between the expected output and the
output; it may be possible to add a `--promote` mode to ocamltest
where the output is copied into the expected output instead of being
compared.
Yep, I started to think about it. But it's not that obvious because at the
very general level, ocamltest has no notion of reference, output etc.
One can for instance imagine to introduce a promote mode and then it
would be up to each action to figure out whether to implement a
different behaviour in that mode or not and what this different
behaviour should be.
For instance, I guess the compiler actions should ignore this mode,
similarly for the run action, so that the output file can be produced.
And then the check-output action could implement promote mode as you
describe.
Also note that using such a mode "blindly" may lead to not so nice
results. To give you an example, when checking the output of a compiler
run on a test file foo.ml,
ocamltest implements the following heuristic to determine which
reference file it should use:
1. Lookup the test source directory for a foo.COMPILERNAME.reference
file, where COMPILERNAME can be ocamlc.byte, for instance. If this file
is found, use it.
2. Otherwise, lookup the test source directory for a
foo.BACKEND.reference file. Where BACKEND is one of bytecode or native,
at the moment. If this file is found, use it as a reference.
3. Otherwise, lookup the test source directory for a
foo.compilers.reference file. If it is found, use it as the reference
file.
4. Otherwise, compiler's output must be empty. If it is not, the test
fails.
This may seem overkill but it is so because all these levels have proven
to be useful at least once.
Coming back to the promote mode, it's not completely obvious to know to
which reference file to copy the result. One obvious choice would be to
copy it to the compiler-specific file mentionned in step 1, but then
that will not take into account the fact that the results of different
compilers may be similar. Or there should be another test while
promoting to "merge" two compiler-specific reference files into a
backend specific file if they are same, and then to merge the
backend-specific files into the compilers.reference file, if they
are similar.
Well that does not sound completely undoable, in fact. Just that I'm not
sure it has a high priority, compared to enhancing the tool to be able
to migrate the remaining tests.
|
|
I think that the file(s) that should be promoted is precisely the one(s) that cause(s) the test to fail. So I'd wait until ocamltest has decided which reference file to use, and promote this one. |
|
I think yourresponse makes no sense to me, @gasche.
It's not a matter of which file to promote, because this, we know. The
file that needs to be promoted is the actually compiler output.
The question si more _how_ it should be promoted. As a compiler-specific
output, as a backend-specific output or as the output of all the
compilers.
But the heuristic I describe would deal with that so I think it's okay.
|
|
If the test that I am running fails because the output (however it is computed) does not correspond to |
|
Gabriel Scherer (2017/12/13 10:00 +0000):
If the test that I am running fails because the output (however it is
computed) does not correspond to `foo.compilers.reference`, then
`foo.compilers.reference` should be modified.
No, because that may still work for other compilers and you may just
want to overload the reference for that specific compiler.
|
|
Then the user will notice that there is an issue (because the tests will still fail after promotion) and will have to think harder about the problem, which is what we want to happen: if all compiler outputs used to be the same and now they are not, something interesting occurred and we need human supervision. |
|
#1491 is what I was rebasing.
I don't get it. It sounds like you're saying the way to see the output of a failing test, beyond whether it failed or not, is something like: (well actually that would show both failing and passing tests, so this command is actually 90% noise) So in practice, it looks like the way to go for now is: which is not noisy and allows me to use the diff program I want.
Yes, but given that the .corrected files are hidden in the _ocamltest directory, it's annoying to actually find which command to type. |
|
Also: ocamltest is leaking all these ocamltestXXXXXXdiff files, in filecompare.ml. |
|
sliquister (2017/12/13 18:22 -0800):
Also: ocamltest is leaking all these ocamltestXXXXXXdiff files, in
filecompare.ml.
But aren't they created under a temporary directory?
So do we care, really?
Well if we do just let me know I'll update the code to get rid of them.
This code definitely needs some updating anyway.
|
|
sliquister (2017/12/13 06:52 -0800):
#1491 is what I was rebasing.
Okay thanks.
> Normally the log file of the tests should include a diff when the result differs from the reference.
I don't get it. It sounds like you're saying the way to see the output of a failing test, beyond whether it failed or not, is something like:
```
$ ocamltest *.ml
$ grep '' _ocamltest/*/*.log
```
(well actually that would show both failing and passing tests, so this command is actually 90% noise)
So in practice, it looks like the way to go for now is:
```
$ ocamltest *.ml
$ for v in _ocamltest/*; do diff $v/*.ml $v/*.corrected.corrected; done
```
which is not noisy and allows me to use the diff program I want.
Great. As I understand it, what you are looking for is a way to get a
list of failed tests and, for those, to see the diff, right?
Well if your command suits your needs and if you can live with it at the
moment, I'd rather postpone working on this and concentrate on migrating
the remaining tests.
>> and to promote the corrected file?
> No, that has not been implemented. But promoting is just a matter of moving / renaming a file, right?
Yes, but given that the .corrected files are hidden in the _ocamltest
directory, it's annoying to actually find which command to type.
Understood. I think in the light of the recent exchange regarding how
promote could be implemented there is definitely something to do here.
Just not now, if nobody minds. Theplan is really to migrate as many
tests as possible, as quickly as possible, so that we can get rid of the
legacy framework. Then we cna change ocamltest's output format to TAP
and do other fancy things.
|
|
It is a bit uncomfortable to break the workflow of people writing tests today when we migrate. (Another workflow that is not available anymore in the new system is to perform the tests in parallel (I realized while trying to reproduce the color issue), but I think that this is easy to add and I'll try to send a PR soon.) |
|
Yeah the workflow is kind of broken right now.
Now:
I can live with the current state of things, because now I know enough to script around it, but I'm guessing other people will run into this (or run all the tests I suppose, but you can't iterate with that, it's horribly slow). |
|
In case it's useful to others, the script below should allow one to work on a single expect-test (doesn't help with @gasche's problem of promoting all tests). The workflow becomes (even from a clean repo):
No need for configure, or make world, or ../../../ocamltest/ocamltest, etc $ cat ocamltest
#!/bin/bash
# Help:
# ocamltest: runs the expect tests in the current directory, after building
# everything that's necessary
# ocamltest promote: promote the output of last run of tests to be the expected output
set -e -u -o pipefail
if [ $# -ge 1 ]; then
case "$1" in
promote)
shopt -s nullglob
for v in _ocamltest/*/*.corrected.corrected; do
mv $v $(basename $v .corrected.corrected)
done
exit 0
;;
*) echo >&2 "unknown command $1"
exit 1
;;
esac
fi
root=$(git rev-parse --show-toplevel)
start_dir=$PWD
cd "$root"
if ! [ -f utils/config.ml ]; then
./configure -prefix "$PWD"/installed
fi
make coldstart ocaml ocamlc runtime
make -C testsuite/tools expect_test
make -C lex
make -C ocamltest
cd "$start_dir"
rm -rf _ocamltest
"$root"/ocamltest/ocamltest *.ml
function diff {
if ! cmp -s "$@"; then
if which patdiff &> /dev/null; then
patdiff "$@" && patdiff -keep-whitespace "$@" && command diff -u "$@"
else
command diff -u "$@"
fi
fi
}
for v in _ocamltest/*; do ${DIFF-diff} $v/*.ml $v/*.corrected.corrected; done |
When -promote is set, running the test automatically overwrites each reference output file with the actual output of the test. This option, which mirrors the "make promote" target of old-style testsuite tests, is very useful when a minor change in compiler/toplevel output affects a lot of reference files in innocuous ways. This is the only option of ocamltest that overwrites file in the source directory, so it should be used with care -- under strict version-control supervision. The 'make promote' target is not yet supported for new-style testsuite tests, as this feature depends on the not-yet-merged GPR#1574 ocaml#1574 For a previous discussions of this feature, see GPR#1519 ocaml#1519
When -promote is set, running the test automatically overwrites each reference output file with the actual output of the test. This option, which mirrors the "make promote" target of old-style testsuite tests, is very useful when a minor change in compiler/toplevel output affects a lot of reference files in innocuous ways. This is the only option of ocamltest that overwrites file in the source directory, so it should be used with care -- under strict version-control supervision. The 'make promote' target is not yet supported for new-style testsuite tests, as this feature depends on the not-yet-merged GPR#1574 ocaml#1574 For a previous discussions of this feature, see GPR#1519 ocaml#1519
When -promote is set, running the test automatically overwrites each reference output file with the actual output of the test. This option, which mirrors the "make promote" target of old-style testsuite tests, is very useful when a minor change in compiler/toplevel output affects a lot of reference files in innocuous ways. This is the only option of ocamltest that overwrites file in the source directory, so it should be used with care -- under strict version-control supervision. The 'make promote' target is not yet supported for new-style testsuite tests, as this feature depends on the not-yet-merged GPR#1574 ocaml#1574 For a previous discussions of this feature, see GPR#1519 ocaml#1519
Cc @diml
Most of the tests could be migrated in a straightforward way.
One notable exception is the pr6939 test in typing-misc.
For this one, two actions have been added to ocamltest to figure out
whether flat float arrays are enabled or not.
In the long run, I am wondering whether expect couldn't be integrated
to ocamltest.
At the moment, I suggest to move expect to the ocamltest directory so
that it gets compiled alongside.
If there is an agreement on this, I'd be happy to add a commit doing that
to this PR.