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
DM-11514: Use pytest as simple test runner #34
Conversation
Has not been needed for unittest in a long time and breaks pytest.
pytest will report the full path to the test directory and that might include spaces.
This runs each test file separately and writes a .xml JUnit file for each one.
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.
This looks like a very nice set of changes.
My only question is whether the pytest runner should start out by running on tests/*.py
instead of using automatic test discovery, until we are sure the names of all our tests have been converted. But perhaps the RFC is further along than I realize.
bin/makeLinkFarm
Outdated
@@ -1,5 +1,6 @@ | |||
#!/usr/bin/env python | |||
|
|||
from __future__ import print_function |
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.
Please consider using our standard from __future__ import absolute_import, division, print_function
. I admit division is unlikely in these files, but requiring absolute import is a useful addition, and it is the typical thing we add to all .py files.
@@ -155,7 +155,7 @@ def filesToTag(root=None, fileRegex=None, ignoreDirs=None): | |||
# | |||
# Remove files generated by swig | |||
# | |||
for swigFile in [f for f in filenames if re.search(r"\.i$", f)]: | |||
for swigFile in [f for f in filenames if f.endswith(".i")]: |
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'm sorry to see we still need this, but presumably removing SWIG support should be done on a different ticket.
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 svn support can almost certainly be removed as well.
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.
Not in this ticket.
bin/makeLinkFarm
Outdated
@@ -1,5 +1,6 @@ | |||
#!/usr/bin/env python | |||
|
|||
from __future__ import print_function | |||
import argparse, os, re, sys |
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.
Are multiple package imports on the same line OK?
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.
No. Unless it's __future__
.
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.
Oh I see. I wasn't running flake8 on this file since it's not one we actually use. I'll fix it if you are insisting.
I'm not going to touch the SWIG support as part of this cleanup. The reason I'm still using SCons for test detection (and why the Switching to running |
Deprecation warnings are easily lost so make them a bit more obvious in the test output. Verbose mode is not needed for Jenkins which will be using JUnit XML. It can be added by users by having a setup.cfg. If we want to allow use of $PYTEST_ADDOPTS we may have to pass that through the environment.
@pschella now that I'm reading |
* A single call to pytest * Add new argument to enable auto test discovery * pytest always runs but uses --lf option * A single JUnit XML file is generated
Bug introduced in da6154c when "sources" was renamed to "objs".
When scons is run with -j (>1) pytest will now use pytest_xdist with -n argument.
Good news. @ktlim, @r-owen, @RobertLuptonTheGood, if you now call |
Nice! |
daf_persistence fails with multi-process because test classes were sharing a temp directory. I've opened a ticket for that. |
@@ -195,7 +195,16 @@ def finish(defaultTargets=DEFAULT_TARGETS, | |||
nfail=`find {0} -name \*.failed | wc -l | sed -e 's/ //g'`; \ | |||
if [ $$nfail -gt 0 ]; then \ | |||
echo "Failed test output:" >&2; \ | |||
find {0} -name \*.failed -exec cat {{}} \; >&2; \ | |||
for f in `find {0} -name \*.failed`; do \ |
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.
Why are the newline escapes needed inside of a heredoc?
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.
You mean the bit after the do
? Probably not needed. It was left over from the edited line.
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.
No -- I meant why do any of the lines need escapes?
$ python
Python 3.6.2 (default, Jul 19 2017, 13:09:21)
[GCC 7.1.1 20170622 (Red Hat 7.1.1-3)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> os.system("""
... if [[ 1 == 1 ]]; then
... echo hi
... fi
... """)
hi
0
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.
No idea. Haven't checked. I was using the style from the pre-existing code. They all do turn up on a single line when you print the debug so I'm not sure why @RobertLuptonTheGood did it like that originally.
python/lsst/sconsUtils/scripts.py
Outdated
@@ -481,9 +507,31 @@ def s(l): | |||
swigMods.extend( | |||
state.env.SwigLoadableModule("_" + name, src, LIBS=state.env.getLibs("main python")) | |||
) | |||
|
|||
# Warn about insisting that a test in pySingles starts with test_ and therefore | |||
# might be automatically discovered by pytest. This warning could only be issued |
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.
"should 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.
Don't know. Do you think it should only be issued if auto test discovery is enabled? I think I'd prefer the warning regardless so that we don't get hit with renaming the file again when auto test discovery is turned on. If we don't warn we are going to get problems later when people think they can just run pytest
in the root directory.
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 was attempting to call out that "could" either is in past tense or implies a level of uncertainty. It would be better to be definitive and say either "can only be" or "should only be".
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.
Fair enough. I'll insist on it happening.
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.
To answer the functional question, I'm in favor of always generating the warning to make sure the developer aware of the potential problem.
@@ -467,6 +491,8 @@ def tests(pyList=None, ccList=None, swigNameList=None, swigSrc=None, | |||
ignoreList = [] | |||
|
|||
def s(l): | |||
if l is None: | |||
return ['None'] | |||
return [str(i) for i in l] |
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 know this is prior formatting but a newline would be nice here to visually separate the function block. I'm surprised that flake8 doesn't catch nested functions.
@@ -127,18 +127,26 @@ def messages(self, test): | |||
"false", "failed") | |||
|
|||
def run(self, fileGlob): | |||
"""Create a test target for each file matching the supplied glob. | |||
""" |
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 don't object to this style but I think it is inconsistent with the rest of the package?
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 documentation styles in sconsUtils are a disaster...
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 other methods in that file use a similar style so I'm not sure what the problem is.
python/lsst/sconsUtils/tests.py
Outdated
pyList is a list of nodes corresponding to python test files. | ||
The IgnoreList is respected when scanning for entries. | ||
If pyList is None, we will use automated test discovery within | ||
pytest. Returns a list containing a single target.""" |
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.
What is the rule being used for line breaking?
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.
you mean, for formatting the doc paragraph? You want a 72 character wide M-x fill-paragraph
?
tests/SConscript
Outdated
@@ -5,7 +5,7 @@ import subprocess | |||
from lsst.sconsUtils import env, scripts, targets | |||
import SCons.Script | |||
|
|||
scripts.BasicSConscript.tests() | |||
scripts.BasicSConscript.tests(pyList=[]) |
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.
Doesn't this completely disable running tests?
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.
No.
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.
That docstring you commented on above explains this. If you say pyList=[]
that enables automatic test discovery. pyList=None
invokes old style "find all *.py files". Using pyList
like this was the simplest way to retain backwards compatibility and let you enable auto discovery. An entirely new argument to enable auto test discovery and disable pyList
seemed overkill (I did try it that way first).
echo "Global pytest run: failed"; \ | ||
mv ${{TARGET}}.out ${{TARGET}}.failed; \ | ||
fi; | ||
""".format(interpreter, " ".join([pipes.quote(p) for p in pythonTestFiles]), libpathstr)) |
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 just checked and pipes.quote
is still present in py 3.6. The best option I could find for py2/3 compat is a backport module:
https://pypi.python.org/pypi/shellescape
which seems like a PITA for one small function. Perhaps add a comment that pipes.quote
is deprecated and will probably break someday?
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 had no idea that pipes.quote
was deprecated. I'm happy to switch to shlex.quote
next year when we drop python2... We use pipes.quote
all over the place in sconsUtils
so making a special note for this one is not going to be helpful.
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.
(and we have to quote because my test system at home is sitting on a volume with a space in the name so everything breaks without doing that).
python/lsst/sconsUtils/tools/cuda.py
Outdated
@@ -19,15 +19,15 @@ | |||
def CUDANVCCStaticObjectEmitter(target, source, env): | |||
tgt, src = SCons.Defaults.StaticObjectEmitter(target, source, env) | |||
for file in src: | |||
lifile = os.path.splitext(src[0].rstr())[0] + '.linkinfo' | |||
lifile = os.path.splitext(src[0].rstr())[0] + '.linkinfo' # noqa |
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.
What checks are being disabled?
@@ -1,3 +1,4 @@ | |||
.cache |
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.
Will the cache file show up here (at the package root) when scons runs pytest? In the past I think it may have showed up in tests/.cache
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 seems to in this case. This will ignore all directories named .cache
so it will work either way.
@@ -2,7 +2,7 @@ | |||
import os | |||
|
|||
try: | |||
from eups import * | |||
from eups import * # noqa F403 F401 Importing everything into this namespace |
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 suppose this is so all eups symbols come in when this module is imported by other code?
If it's just for local usage it'd be better to change this.
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, it's so that all the normal eups routines can be available instead of the fallbacks. Since we are explicitly specifying fallbacks then it doesn't seem entirely crazy to import an explicit list from eups but is it worth it?
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.
Please at least expand on the comment a bit. If I was coding it I'd use a list, so the linter could detect undefined symbols. But I think the current code is defensible, especially if the list of all needed eups symbols is very large.
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.
Now that I try it, I think the current situation is to be preferred. As soon as you import the EUPS versions, the linter complains about the redefinition of symbols later in the file and you have to # noqa
those. The current approach has fewer noqa lines.
python/lsst/sconsUtils/tests.py
Outdated
"""Add a single target for testing all python files. pyList is | ||
a list of nodes corresponding to python test files. The | ||
IgnoreList is respected when scanning for entries. If pyList | ||
is None, we will use automated test discovery within pytest. |
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 condition for automatic test discovery is a bit more complicated than that. It is, at least: If pyList is None or an empty list...
but if no tests are found in the provided nodes then it also goes into auto discovery mode, and that worries me a bit because auto discovery may be triggered unexpectedly.
If the current behavior (go into auto discovery if pythonTestFiles
is empty) is desirable for some reason then please explain in much more detail in the doc string when this function may use auto test discovery.
If the current behavior is not needed, then please consider changing the code so that it tests pyList
, not pythonTestFiles
, to decide whether to use automatic discovery. And expanding the doc string only enough to state that [] and None will trigger it.
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.
Ok. Yes. This is an internal function that does not distinguish []
from None
(unlike the public interface).
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.
Auto discovery can only be triggered unexpectedly if you are using the internal interface incorrectly. I didn't think it made sense to call runPythonTests
and have it return immediately without running any tests.
With the EUPS product name and, optionally, an additional string taken from the `LSST_JUNIT_PREFIX` env var. This allows the test reports to be easily aggregated across multiple products that make up a build. Eg. tests.test_angle becomes either afw.tests.test_angle or afw.${LSST_JUNIT_PREFIX}.tests.test_angle
3bcc894
to
e000e06
Compare
This changes the behavior of sconsUtils testing:
pytest
as the test runner, running a single process for all python tests, and multiple subprocesses if-j
is used.tests/.tests
(converting that to.failed
as before).