Repository navigation
wip: appveyor: enable parallel testing #2713
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
Changes from all commits
39c1d64
8686aca
ab6dc19
c143a85
a977a80
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,7 +15,7 @@ environment: | |
| # See: http://stackoverflow.com/a/13751649/163740 | ||
| CMD_IN_ENV: 'cmd /E:ON /V:ON /C .\tests\scripts\appveyor\run_with_env.cmd' | ||
|
|
||
| PYTEST: py.test --maxfail 5 --durations=10 --junitxml=junit-results.xml | ||
| PYTEST: py.test -n3 --maxfail 5 --durations=10 --junitxml=junit-results.xml | ||
| # Break the tests into two runs, since together they exceed the 1 hour limit. | ||
| # See https://github.com/pyinstaller/pyinstaller/issues/2024#issuecomment-224129520 | ||
| # for more discussion. | ||
|
|
@@ -27,63 +27,26 @@ environment: | |
| - PYTHON: C:\Python36-x64 | ||
| PYTHON_VERSION: 3.6 | ||
| PYTHON_ARCH: 64 | ||
| TEST_LIBRARIES: NO | ||
|
|
||
| - PYTHON: C:\Python36-x64 | ||
| PYTHON_VERSION: 3.6 | ||
| PYTHON_ARCH: 64 | ||
| TEST_LIBRARIES: YES | ||
|
|
||
| - PYTHON: C:\Python36 | ||
| PYTHON_VERSION: 3.6 | ||
| PYTHON_ARCH: 32 | ||
| TEST_LIBRARIES: NO | ||
|
|
||
| - PYTHON: C:\Python36 | ||
| PYTHON_VERSION: 3.6 | ||
| PYTHON_ARCH: 32 | ||
| TEST_LIBRARIES: YES | ||
|
|
||
| - PYTHON: C:\Python35 | ||
| PYTHON_VERSION: 3.5 | ||
| PYTHON_ARCH: 32 | ||
| TEST_LIBRARIES: NO | ||
|
|
||
| - PYTHON: C:\Python35 | ||
| PYTHON_VERSION: 3.5 | ||
| PYTHON_ARCH: 32 | ||
| TEST_LIBRARIES: YES | ||
|
|
||
| - PYTHON: C:\Python34 | ||
| PYTHON_VERSION: 3.4 | ||
| PYTHON_ARCH: 32 | ||
| TEST_LIBRARIES: NO | ||
|
|
||
| - PYTHON: C:\Python34 | ||
| PYTHON_VERSION: 3.4 | ||
| PYTHON_ARCH: 32 | ||
| TEST_LIBRARIES: YES | ||
|
|
||
| - PYTHON: C:\Python33 | ||
| PYTHON_VERSION: 3.3 | ||
| PYTHON_ARCH: 32 | ||
| TEST_LIBRARIES: NO | ||
|
|
||
| - PYTHON: C:\Python33 | ||
| PYTHON_VERSION: 3.3 | ||
| PYTHON_ARCH: 32 | ||
| TEST_LIBRARIES: YES | ||
|
|
||
| - PYTHON: C:\Python27 | ||
| PYTHON_VERSION: 2.7 | ||
| PYTHON_ARCH: 32 | ||
| TEST_LIBRARIES: NO | ||
|
|
||
| - PYTHON: C:\Python27 | ||
| PYTHON_VERSION: 2.7 | ||
| PYTHON_ARCH: 32 | ||
| TEST_LIBRARIES: YES | ||
|
|
||
|
|
||
| init: | ||
| - "ECHO %PYTHON% %PYTHON_VERSION% %PYTHON_ARCH%" | ||
|
|
@@ -136,11 +99,7 @@ install: | |
|
|
||
| # Install the PyInstaller test dependencies. | ||
| - '%CMD_IN_ENV% pip install -U --timeout 5 --retries 2 | ||
| -r tests/requirements-tools.txt' | ||
| - 'if "%TEST_LIBRARIES%" == "YES" ( | ||
| %CMD_IN_ENV% pip install -U --timeout 5 --retries 2 | ||
| -r tests/requirements-libraries.txt | ||
| )' | ||
| -r tests/requirements-libraries.txt -r tests/requirements-tools.txt' | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Un-splitting the tests is a different change then "enable parallel testing" and should go into a separate commit. I suggest adding to the commit message something like "Now the test are running in parallel, the test-suite runs short/quick enough to not time-out. So we can unify the split test-runs." (If this is correct, of course, - which would be nice :-) |
||
| # Install PyInstaller into virtualenv. | ||
| - '%CMD_IN_ENV% pip install -e .' | ||
|
|
||
|
|
@@ -150,11 +109,8 @@ test_script: | |
| # Run the tests appropriate for this entry in the test matrix. Skip tests | ||
| # if we're just updating the cache. | ||
| - 'if not "%APPVEYOR_SCHEDULED_BUILD%" == "True" ( | ||
| if /i "%TEST_LIBRARIES%" == "YES" ( | ||
| %CMD_IN_ENV% %PYTEST% %TESTS_LIBRARIES% | ||
| ) else ( | ||
| %CMD_IN_ENV% %PYTEST% %TESTS_UNITTESTS% | ||
| ))' | ||
| %CMD_IN_ENV% %PYTEST% 2> nul | ||
| )' | ||
|
|
||
| on_success: | ||
| # Remove old or huge cache files to hopefully not exceed the 1GB cache limit. | ||
|
|
@@ -173,6 +129,7 @@ on_success: | |
|
|
||
| on_finish: | ||
| - ps: | | ||
| python .\scripts\compact-junit.py junit-results.xml | ||
| (new-object net.webclient).UploadFile( | ||
| "https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)", | ||
| (Resolve-Path .\junit-results.xml) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| import sys | ||
|
|
||
| from junitparser import JUnitXml, Failure | ||
|
|
||
|
|
||
| def main(): | ||
| junit_xml_file = sys.argv[1] | ||
| suite = JUnitXml.fromfile(junit_xml_file) | ||
| for case in suite: | ||
| if not isinstance(case.result, Failure): | ||
| case.system_out = '' | ||
| case.system_err = '' | ||
| suite.write() | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm curious: This script is simply reading and writing back the data. How it is "compressing" then? maybe a short comment explaining this would be useful.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All of the data is loaded into memory as objects, not xml. Therefore, xml metadata is lost during the read, which reduces file size. The xml metadata has no influence on the test results displayed. |
||
|
|
||
|
|
||
| if __name__ == '__main__': | ||
| main() | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This change is unrelated to "enable parallel testing". Please move this (and the related changes) into a separate commit. |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -26,6 +26,8 @@ | |
| import textwrap | ||
| import io | ||
| import shutil | ||
| import tempfile | ||
| import shutil | ||
|
|
||
| # Third-party imports | ||
| # ------------------- | ||
|
|
@@ -41,7 +43,7 @@ | |
| from PyInstaller import configure, config | ||
| from PyInstaller import __main__ as pyi_main | ||
| from PyInstaller.utils.cliutils import archive_viewer | ||
| from PyInstaller.compat import is_darwin, is_win, is_py2, safe_repr, \ | ||
| from PyInstaller.compat import is_darwin, is_win, is_py2, is_py3, safe_repr, \ | ||
| architecture | ||
| from PyInstaller.depend.analysis import initialize_modgraph | ||
| from PyInstaller.utils.win32 import winutils | ||
|
|
@@ -343,27 +345,59 @@ def _run_executable(self, prog, args, run_from_path, runtime): | |
| # Windows command prompt. Py.test is then able to collect stdout/sterr | ||
| # messages and display them if a test fails. | ||
|
|
||
| process = psutil.Popen(args, executable=exe_path, stdout=sys.stdout, | ||
| stderr=sys.stderr, env=prog_env, cwd=prog_cwd) | ||
| # 'psutil' allows to use timeout in waiting for a subprocess. | ||
| # If not timeout was specified then it is 'None' - no timeout, just waiting. | ||
| # Runtime is useful mostly for interactive tests. | ||
| try: | ||
| timeout = runtime if runtime else _EXE_TIMEOUT | ||
| retcode = process.wait(timeout=timeout) | ||
| except psutil.TimeoutExpired: | ||
| # Note that we cannot let Popen get ahold of sys.stderr/out because it | ||
| # will attempt to close the files with nonstandard (system-level) | ||
| # methods via fileno(). This causes problems on win32 from which pytest | ||
| # cannot correctly recover. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So this is the core issue? There's no need to patch xdist if Popen is made to use pipes instead of stdio?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct. Even passing in my own tempfiles did not fix the problem on py3 because they were closed early. |
||
|
|
||
| def timeout_process(process): | ||
| # Kill the subprocess and its child processes. | ||
| for p in process.children(recursive=True): | ||
| p.kill() | ||
|
|
||
| if runtime: | ||
| # When 'runtime' is set then expired timeout is a good sing | ||
| # that the executable was running successfully for a specified time. | ||
| # TODO Is there a better way return success than 'retcode = 0'? | ||
| retcode = 0 | ||
| return 0 | ||
| else: | ||
| # Exe is still running and it is not an interactive test. Fail the test. | ||
| retcode = 1 | ||
| # Kill the subprocess and its child processes. | ||
| for p in process.children(recursive=True): | ||
| p.kill() | ||
| process.kill() | ||
| return 1 | ||
|
|
||
| # 'psutil' allows to use timeout in waiting for a subprocess. | ||
| # If not timeout was specified then it is 'None' - no timeout, just waiting. | ||
| # Runtime is useful mostly for interactive tests. | ||
| timeout = runtime if runtime else _EXE_TIMEOUT | ||
| if is_py3: | ||
| process = subprocess.Popen(args, executable=exe_path, stdout=subprocess.PIPE, | ||
| stderr=subprocess.PIPE, env=prog_env, cwd=prog_cwd) | ||
| try: | ||
| stdout, stderr = process.communicate(timeout=timeout) | ||
| retcode = process.returncode | ||
| except subprocess.TimeoutExpired: | ||
| retcode = timeout_process(psutil.Process(process.pid)) | ||
| process.kill() | ||
| stdout, stderr = process.communicate() | ||
|
|
||
| sys.stdout.write(stdout) | ||
| sys.stderr.write(stderr) | ||
| else: | ||
| stderr = tempfile.TemporaryFile() | ||
| stdout = tempfile.TemporaryFile() | ||
| process = psutil.Popen(args, executable=exe_path, stdout=stdout, | ||
| stderr=stderr, env=prog_env, cwd=prog_cwd) | ||
|
|
||
| stderr.seek(0) | ||
| stdout.seek(0) | ||
|
|
||
| shutil.copyfileobj(stderr, sys.stderr) | ||
| shutil.copyfileobj(stdout, sys.stdout) | ||
|
|
||
| try: | ||
| retcode = process.wait(timeout=timeout) | ||
| except psutil.TimeoutExpired: | ||
| retcode = timeout_process(process) | ||
| process.kill() | ||
|
|
||
| return retcode | ||
|
|
||
|
|
||
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.
Be very careful. When I last tested (#2024), xdist caused seemingly random test failures on windows. Unless these problems have been fixed, I wouldn't do 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.
I thought that I had quashed all of the pytest errors but there is apparently one more.
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.
Just to be sure we're on the same page, the root case I saw (some time ago) when playing with the
-n3option was that the xdist plugin didn't work reliably on Windows; it seemed to work quite well on Linux. I would, of course, be overjoyed if this works now, since Appveyor testing time is sooooo long.