summaryrefslogtreecommitdiffstats
path: root/third_party/typ
diff options
context:
space:
mode:
authordpranke <dpranke@chromium.org>2014-10-16 20:16:31 -0700
committerCommit bot <commit-bot@chromium.org>2014-10-17 03:16:45 +0000
commit2bcc3a44e028d4b1e11a3131fafd5fdaa5a35eb0 (patch)
tree2d5cf26779074b332300c38eaf9091ede9fffcf2 /third_party/typ
parent8cd8c9e87bfea3fa8de580578e45a1d6148d6b68 (diff)
downloadchromium_src-2bcc3a44e028d4b1e11a3131fafd5fdaa5a35eb0.zip
chromium_src-2bcc3a44e028d4b1e11a3131fafd5fdaa5a35eb0.tar.gz
chromium_src-2bcc3a44e028d4b1e11a3131fafd5fdaa5a35eb0.tar.bz2
Roll typ to v0.8.5
From typ commit df1f462ec978d47c7f092e86f4be3fafc7cc446f: Rework typ to handle unimportable modules better. multiprocessing on windows requires that the __main__ module be importable in order to work properly. If developers are mostly working and testing on unix, it's possible for them to not realize this and attempt to call typ from a wrapper script that may not be importable, and then get unexpected errors. This change makes the typ entry points check if it is being called from an unimportable __main__, and error out if so. Developers must then specify several different ways to work around this by passing the 'win_multiprocessing' flag to typ.main() or typ.Runner.main(). The flag takes one of the values specified in the typ.WinMultiprocessing class: ignore - The tests are not expected to run on Windows, so ignore the fact that __main__ is not importable. (If the tests are actually run on Windows, it will err out). run_serially - Run serially (one at a time) on Windows, regardless of how many cores are available (hence multiprocessing is not used). spawn - Typ will convert the args it was passed back into a command line, and spawn off an invocation of itself on Windows to actually execute the tests in parallel (Since typ itself is importable, this works fine). This mode cannot be used in conjunction with contexts or setup and teardown functions at this time. force - This is like 'spawn', except that it forces the spawn even on non-Windows platforms. This mostly exists for testing. As part of this change, we also no longer expose spawn_main() as a public entry point (main() handles spawning instead), and merge the cmdline.py code directly into runner, as it didn't make sense to split things across the two modules once runner had to do the importability checking. We also add a host.call_inline() wrapper that is a dumb wrapper around subprocess.call(); it does not do the output capturing that host.call() does, and is used when spawning the typ subprocess if needed. TBR=dtu@chromium.org BUG=402172 Review URL: https://codereview.chromium.org/660133004 Cr-Commit-Position: refs/heads/master@{#300052}
Diffstat (limited to 'third_party/typ')
-rw-r--r--third_party/typ/README.chromium4
-rwxr-xr-xthird_party/typ/run4
-rw-r--r--third_party/typ/setup.py2
-rw-r--r--third_party/typ/typ/__init__.py5
-rw-r--r--third_party/typ/typ/__main__.py7
-rw-r--r--third_party/typ/typ/arg_parser.py47
-rw-r--r--third_party/typ/typ/cmdline.py56
-rw-r--r--third_party/typ/typ/fakes/host_fake.py4
-rw-r--r--third_party/typ/typ/fakes/tests/host_fake_test.py5
-rw-r--r--third_party/typ/typ/host.py5
-rw-r--r--third_party/typ/typ/runner.py105
-rw-r--r--third_party/typ/typ/tests/arg_parser_test.py14
-rw-r--r--third_party/typ/typ/tests/cmdline_test.py45
-rw-r--r--third_party/typ/typ/tests/host_test.py12
-rw-r--r--third_party/typ/typ/tests/main_test.py2
-rw-r--r--third_party/typ/typ/tests/runner_test.py160
-rw-r--r--third_party/typ/typ/version.py2
17 files changed, 358 insertions, 121 deletions
diff --git a/third_party/typ/README.chromium b/third_party/typ/README.chromium
index a6fcca5..a49eb39 100644
--- a/third_party/typ/README.chromium
+++ b/third_party/typ/README.chromium
@@ -1,7 +1,7 @@
Name: typ
URL: https://github.com/dpranke/typ.git
-Version: 0.8.4
-Revision: 5e73c13993ecb44b00f93ca2fe97a7c18158f7ff
+Version: 0.8.5
+Revision: aba6b5defd08d74dc1e52d25838b04a38088072d
Security Critical: no
License: Apache 2.0
License File: NOT_SHIPPED
diff --git a/third_party/typ/run b/third_party/typ/run
index c825055..9fb0bd2 100755
--- a/third_party/typ/run
+++ b/third_party/typ/run
@@ -139,13 +139,13 @@ def run_lint(args):
def run_tests(args):
# Tests that we can run the command line directly if typ is in sys.path.
- call(['python', os.path.join('typ', 'cmdline.py'),
+ call(['python', os.path.join('typ', 'runner.py'),
'typ.tests.main_test.TestMain.test_basic'])
# Test that we can run the command line directly if typ is not in sys.path.
repo_dir = os.path.abspath(os.path.dirname(__file__))
home_dir = os.environ['HOME']
- call(['python', os.path.join(repo_dir, 'typ', 'cmdline.py'),
+ call(['python', os.path.join(repo_dir, 'typ', 'runner.py'),
'typ.tests.main_test.TestMain.test_basic'], cwd=home_dir)
# Now run all the tests under Python2 and Python3.
diff --git a/third_party/typ/setup.py b/third_party/typ/setup.py
index ebc740a..170398f 100644
--- a/third_party/typ/setup.py
+++ b/third_party/typ/setup.py
@@ -34,7 +34,7 @@ setup(
package_data={'': ['../README.rst']},
entry_points={
'console_scripts': [
- 'typ=typ.cmdline:main',
+ 'typ=typ.runner:main',
]
},
install_requires=[
diff --git a/third_party/typ/typ/__init__.py b/third_party/typ/typ/__init__.py
index 6013a3a..ab04414 100644
--- a/third_party/typ/typ/__init__.py
+++ b/third_party/typ/typ/__init__.py
@@ -59,11 +59,10 @@ functionality:
from typ.arg_parser import ArgumentParser
from typ.fakes.host_fake import FakeHost
from typ.host import Host
-from typ.cmdline import main, spawn_main
from typ.json_results import exit_code_from_full_results
from typ.json_results import make_full_results, make_upload_request
from typ.json_results import Result, ResultSet, ResultType
-from typ.runner import Runner, TestInput, TestSet
+from typ.runner import Runner, TestInput, TestSet, WinMultiprocessing, main
from typ.stats import Stats
from typ.printer import Printer
from typ.test_case import convert_newlines, TestCase, MainTestCase
@@ -85,10 +84,10 @@ __all__ = [
'TestInput',
'TestSet',
'VERSION',
+ 'WinMultiprocessing',
'convert_newlines',
'exit_code_from_full_results',
'main',
'make_full_results',
'make_upload_request',
- 'spawn_main',
]
diff --git a/third_party/typ/typ/__main__.py b/third_party/typ/typ/__main__.py
index 3b8b541..375e3e2 100644
--- a/third_party/typ/typ/__main__.py
+++ b/third_party/typ/typ/__main__.py
@@ -14,11 +14,8 @@
import sys
-from typ import main, spawn_main
+from typ import main
if __name__ == '__main__':
- if sys.platform == 'win32': # pragma: win32
- sys.exit(spawn_main(sys.argv[1:], sys.stdout, sys.stderr))
- else: # pragma: no win32
- sys.exit(main())
+ sys.exit(main(win_multiprocessing='spawn'))
diff --git a/third_party/typ/typ/arg_parser.py b/third_party/typ/typ/arg_parser.py
index ac2d33e..b55dee8 100644
--- a/third_party/typ/typ/arg_parser.py
+++ b/third_party/typ/typ/arg_parser.py
@@ -246,6 +246,49 @@ class ArgumentParser(argparse.ArgumentParser):
options.append(optparse.make_option(*args, **kwargs))
return options
+ def argv_from_args(self, args):
+ default_parser = ArgumentParser(host=self._host)
+ default_args = default_parser.parse_args([])
+ argv = []
+ tests = []
+ d = vars(args)
+ for k in sorted(d.keys()):
+ v = d[k]
+ argname = _argname_from_key(k)
+ action = self._action_for_key(k)
+ action_str = _action_str(action)
+ if k == 'tests':
+ tests = v
+ continue
+ if getattr(default_args, k) == v:
+ # this arg has the default value, so skip it.
+ continue
+
+ assert action_str in ['append', 'count', 'store', 'store_true']
+ if action_str == 'append':
+ for el in v:
+ argv.append(argname)
+ argv.append(el)
+ elif action_str == 'count':
+ for _ in range(v):
+ argv.append(argname)
+ elif action_str == 'store':
+ argv.append(argname)
+ argv.append(str(v))
+ else:
+ # action_str == 'store_true'
+ argv.append(argname)
+
+ return argv + tests
+
+ def _action_for_key(self, key):
+ for action in self._actions:
+ if action.dest == key:
+ return action
+
+ assert False, ('Could not find an action for %s' # pragma: no cover
+ % key)
+
def _action_str(action):
# Access to a protected member pylint: disable=W0212
@@ -264,3 +307,7 @@ def _action_str(action):
return 'store'
if isinstance(action, argparse._StoreTrueAction):
return 'store_true'
+
+
+def _argname_from_key(key):
+ return '--' + key.replace('_', '-')
diff --git a/third_party/typ/typ/cmdline.py b/third_party/typ/typ/cmdline.py
deleted file mode 100644
index 4bb718f..0000000
--- a/third_party/typ/typ/cmdline.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# Copyright 2014 Google Inc. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import os
-import subprocess
-import sys
-
-
-# This ensures that absolute imports of typ modules will work when
-# running typ/cmdline.py as a script even if typ is not installed.
-# We need this entry in addition to the one in __main__.py to ensure
-# that typ/cmdline.py works when invoked via subprocess on windows in
-# _spawn_main().
-path_to_file = os.path.realpath(__file__)
-dir_above_typ = os.path.dirname(os.path.dirname(path_to_file))
-if dir_above_typ not in sys.path: # pragma: no cover
- sys.path.append(dir_above_typ)
-
-from typ.runner import Runner
-
-
-def main(argv=None, host=None, **defaults):
- runner = Runner(host=host)
- return runner.main(argv, **defaults)
-
-
-def spawn_main(argv, stdout, stderr):
- # This function is called from __main__.py when running 'python -m typ' on
- # windows.
- #
- # In order to use multiprocessing on windows, the initial module needs
- # to be importable, and __main__.py isn't.
- #
- # This code instead spawns a subprocess that invokes main.py directly,
- # getting around the problem.
- #
- # We don't want to always spawn a subprocess, because doing so is more
- # heavyweight than it needs to be on other platforms (and can make
- # debugging a bit more annoying).
- return subprocess.call([sys.executable, path_to_file] + argv,
- stdout=stdout, stderr=stderr)
-
-
-if __name__ == '__main__': # pragma: no cover
- sys.exit(main())
diff --git a/third_party/typ/typ/fakes/host_fake.py b/third_party/typ/typ/fakes/host_fake.py
index cb74d27..3e8643c 100644
--- a/third_party/typ/typ/fakes/host_fake.py
+++ b/third_party/typ/typ/fakes/host_fake.py
@@ -40,6 +40,7 @@ class FakeHost(object):
self.stdin = io.StringIO()
self.stdout = io.StringIO()
self.stderr = io.StringIO()
+ self.platform = 'linux2'
self.env = {}
self.sep = '/'
self.dirs = set([])
@@ -89,6 +90,9 @@ class FakeHost(object):
self.cmds.append(argv)
return 0, '', ''
+ def call_inline(self, argv):
+ return self.call(argv)[0]
+
def chdir(self, *comps):
path = self.join(*comps)
if not path.startswith('/'):
diff --git a/third_party/typ/typ/fakes/tests/host_fake_test.py b/third_party/typ/typ/fakes/tests/host_fake_test.py
index 02f74e7..a16b7ba 100644
--- a/third_party/typ/typ/fakes/tests/host_fake_test.py
+++ b/third_party/typ/typ/fakes/tests/host_fake_test.py
@@ -43,6 +43,11 @@ class TestFakeHost(host_test.TestHost):
self.assertEqual(err, '')
self.assertEqual(h.cmds, [['echo', 'hello, world']])
+ def test_call_inline(self):
+ h = self.host()
+ ret = h.call_inline(['echo', 'hello, world'])
+ self.assertEqual(ret, 0)
+
def test_capture_output(self):
h = self.host()
self.host = lambda: h
diff --git a/third_party/typ/typ/host.py b/third_party/typ/typ/host.py
index 4859d9c..c5f6ac8 100644
--- a/third_party/typ/typ/host.py
+++ b/third_party/typ/typ/host.py
@@ -48,6 +48,7 @@ class Host(object):
self.stderr = sys.stderr
self.stdin = sys.stdin
self.env = os.environ
+ self.platform = sys.platform
def abspath(self, *comps):
return os.path.abspath(self.join(*comps))
@@ -75,6 +76,10 @@ class Host(object):
# pylint type checking bug - pylint: disable=E1103
return proc.returncode, stdout.decode('utf-8'), stderr.decode('utf-8')
+ def call_inline(self, argv, env=None):
+ return subprocess.call(argv, stdin=self.stdin, stdout=self.stdout,
+ stderr=self.stderr, env=env)
+
def chdir(self, *comps):
return os.chdir(self.join(*comps))
diff --git a/third_party/typ/typ/runner.py b/third_party/typ/typ/runner.py
index 67cfd26..3a8dc82 100644
--- a/third_party/typ/typ/runner.py
+++ b/third_party/typ/typ/runner.py
@@ -16,11 +16,24 @@ import fnmatch
import importlib
import inspect
import json
+import os
import pdb
+import sys
import unittest
from collections import OrderedDict
+# This ensures that absolute imports of typ modules will work when
+# running typ/runner.py as a script even if typ is not installed.
+# We need this entry in addition to the one in __main__.py to ensure
+# that typ/runner.py works when invoked via subprocess on windows in
+# _spawn_main().
+path_to_file = os.path.realpath(__file__)
+dir_above_typ = os.path.dirname(os.path.dirname(path_to_file))
+if dir_above_typ not in sys.path: # pragma: no cover
+ sys.path.append(dir_above_typ)
+
+
from typ import json_results
from typ.arg_parser import ArgumentParser
from typ.host import Host
@@ -36,6 +49,19 @@ ResultSet = json_results.ResultSet
ResultType = json_results.ResultType
+def main(argv=None, host=None, stdout=None, stderr=None,
+ win_multiprocessing=None, **defaults):
+ host = host or Host()
+ if stdout:
+ host.stdout = stdout
+ if stderr:
+ host.stderr = stderr
+ runner = Runner(host=host)
+
+ return runner.main(argv, win_multiprocessing=win_multiprocessing,
+ **defaults)
+
+
class TestInput(object):
def __init__(self, name, msg='', timeout=None, expected=None):
@@ -64,6 +90,15 @@ class TestSet(object):
self.teardown_fn = teardown_fn
+class WinMultiprocessing(object):
+ force = 'force'
+ ignore = 'ignore'
+ run_serially = 'run_serially'
+ spawn = 'spawn'
+
+ values = [force, ignore, run_serially, spawn]
+
+
class _AddTestsError(Exception):
pass
@@ -84,14 +119,16 @@ class Runner(object):
parser = ArgumentParser(self.host)
self.parse_args(parser, [])
- def main(self, argv=None, **defaults):
+ def main(self, argv=None, win_multiprocessing=None, **defaults):
parser = ArgumentParser(self.host)
self.parse_args(parser, argv, **defaults)
if parser.exit_status is not None:
return parser.exit_status
try:
- ret, _, _ = self.run()
+ ret = self._handle_win_multiprocessing('main', win_multiprocessing)
+ if ret is None:
+ ret, _, _ = self.run(win_multiprocessing=win_multiprocessing)
return ret
except KeyboardInterrupt:
self.print_("interrupted, exiting", stream=self.host.stderr)
@@ -108,11 +145,66 @@ class Runner(object):
if parser.exit_status is not None:
return
+ def _handle_win_multiprocessing(self, entry_point, win_multiprocessing,
+ allow_spawn=True):
+ wmp = win_multiprocessing
+ force, ignore, run_serially, spawn = WinMultiprocessing.values
+
+ if (wmp is not None and wmp not in WinMultiprocessing.values):
+ raise ValueError('illegal value %s for win_multiprocessing' %
+ wmp)
+
+ # First, check if __main__ is importable; if it is, we're fine.
+ if (self._main_is_importable() and wmp != force):
+ return None
+
+ if wmp is None and self.args.jobs == 1:
+ return None
+
+ if wmp is None:
+ raise ValueError(
+ 'The __main__ module is not importable; The caller '
+ 'must pass a valid WinMultiprocessing value (one of %s) '
+ 'to %s to tell typ how to handle Windows.' %
+ (WinMultiprocessing.values, entry_point))
+
+ h = self.host
+
+ if (h.platform != 'win32' and wmp != force):
+ return
+
+ if wmp == ignore: # pragma: win32
+ raise ValueError('Cannot use WinMultiprocessing.ignore for '
+ 'win_multiprocessing when actually running '
+ 'on Windows.')
+
+ if wmp == run_serially: # pragma: win32
+ self.args.jobs = 1
+ return None
+
+ assert allow_spawn, ('Cannot use WinMultiprocessing.spawn '
+ 'in %s' % entry_point)
+ assert wmp in (force, spawn)
+ argv = ArgumentParser(h).argv_from_args(self.args)
+ return h.call_inline([h.python_interpreter, path_to_file] + argv)
+
+ def _main_is_importable(self):
+ path = self.host.realpath(sys.modules['__main__'].__file__)
+ if not path or not path.endswith('.py'): # pragma: no cover
+ return False
+
+ for d in sys.path:
+ if path.startswith(self.host.realpath(d)):
+ return True
+ return False # pragma: no cover
+
def print_(self, msg='', end='\n', stream=None):
self.host.print_(msg, end, stream=stream)
def run(self, test_set=None, classifier=None,
- context=None, setup_fn=None, teardown_fn=None):
+ context=None, setup_fn=None, teardown_fn=None,
+ win_multiprocessing=None):
+
ret = 0
h = self.host
@@ -120,6 +212,9 @@ class Runner(object):
self.print_(VERSION)
return ret, None, None
+ self._handle_win_multiprocessing('Runner.run', win_multiprocessing,
+ allow_spawn=False)
+
ret = self._set_up_runner()
if ret: # pragma: no cover
return ret, None, None
@@ -799,3 +894,7 @@ def _load_via_load_tests(child, test_name):
def _sort_inputs(inps):
return sorted(inps, key=lambda inp: inp.name)
+
+
+if __name__ == '__main__': # pragma: no cover
+ sys.exit(main(win_multiprocessing='spawn'))
diff --git a/third_party/typ/typ/tests/arg_parser_test.py b/third_party/typ/typ/tests/arg_parser_test.py
index caaf373..aabaebe 100644
--- a/third_party/typ/typ/tests/arg_parser_test.py
+++ b/third_party/typ/typ/tests/arg_parser_test.py
@@ -29,3 +29,17 @@ class ArgumentParserTest(unittest.TestCase):
skip='[-d]')
options, _ = parser.parse_args(['-j', '1'])
self.assertEqual(options.jobs, 1)
+
+ def test_argv_from_args(self):
+
+ def check(argv, expected=None):
+ parser = ArgumentParser()
+ args = parser.parse_args(argv)
+ actual_argv = parser.argv_from_args(args)
+ expected = expected or argv
+ self.assertEqual(expected, actual_argv)
+
+ check(['--version'])
+ check(['--coverage', '--coverage-omit', 'foo'])
+ check(['--jobs', '4'])
+ check(['-vv'], ['--verbose', '--verbose'])
diff --git a/third_party/typ/typ/tests/cmdline_test.py b/third_party/typ/typ/tests/cmdline_test.py
deleted file mode 100644
index fbb908c..0000000
--- a/third_party/typ/typ/tests/cmdline_test.py
+++ /dev/null
@@ -1,45 +0,0 @@
-# Copyright 2014 Dirk Pranke. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import os
-import tempfile
-
-from typ import spawn_main
-from typ import test_case
-from typ import VERSION
-
-
-class TestSpawnMain(test_case.MainTestCase):
- def call(self, host, argv, stdin, env):
- out = err = None
- out_str = err_str = ''
- try:
- out = tempfile.NamedTemporaryFile(delete=False)
- err = tempfile.NamedTemporaryFile(delete=False)
- ret = spawn_main(argv, stdout=out, stderr=err)
- out.close()
- out_str = open(out.name).read()
- err.close()
- err_str = open(err.name).read()
- finally:
- if out:
- out.close()
- os.remove(out.name)
- if err:
- err.close()
- os.remove(err.name)
- return ret, out_str, err_str
-
- def test_version(self):
- self.check(['--version'], ret=0, out=VERSION + '\n')
diff --git a/third_party/typ/typ/tests/host_test.py b/third_party/typ/typ/tests/host_test.py
index 8bb94f8..50db37f 100644
--- a/third_party/typ/typ/tests/host_test.py
+++ b/third_party/typ/typ/tests/host_test.py
@@ -184,6 +184,14 @@ class TestHost(unittest.TestCase):
self.assertEqual(out, '')
self.assertIn(err, ('err\n', 'err\r\n'))
+ def test_call_inline(self):
+ h = self.host()
+ h.stdout = None
+ h.stderr = None
+ ret = h.call_inline([h.python_interpreter,
+ '-c', 'import sys; sys.exit(0)'])
+ self.assertEqual(ret, 0)
+
def test_add_to_path(self):
orig_sys_path = sys.path[:]
try:
@@ -196,3 +204,7 @@ class TestHost(unittest.TestCase):
self.assertNotEqual(sys.path, orig_sys_path)
finally:
sys.path = orig_sys_path
+
+ def test_platform(self):
+ h = self.host()
+ self.assertNotEqual(h.platform, None)
diff --git a/third_party/typ/typ/tests/main_test.py b/third_party/typ/typ/tests/main_test.py
index ff9b975..cc690d5 100644
--- a/third_party/typ/typ/tests/main_test.py
+++ b/third_party/typ/typ/tests/main_test.py
@@ -165,7 +165,7 @@ LOAD_TEST_FILES = {'load_test.py': LOAD_TEST_PY}
path_to_main = os.path.join(
os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
- 'cmdline.py')
+ 'runner.py')
class TestCli(test_case.MainTestCase):
diff --git a/third_party/typ/typ/tests/runner_test.py b/third_party/typ/typ/tests/runner_test.py
index 3409879..1d3b0cc 100644
--- a/third_party/typ/typ/tests/runner_test.py
+++ b/third_party/typ/typ/tests/runner_test.py
@@ -12,10 +12,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import tempfile
+
from textwrap import dedent as d
from typ import Host, Runner, TestCase, TestSet, TestInput
+from typ import WinMultiprocessing, main
def _setup_process(child, context): # pylint: disable=W0613
@@ -36,12 +39,12 @@ class RunnerTests(TestCase):
def test_bad_default(self):
r = Runner()
- ret = r.main(foo='bar')
+ ret = r.main([], foo='bar')
self.assertEqual(ret, 2)
def test_good_default(self):
r = Runner()
- ret = r.main(tests=['typ.tests.runner_test.ContextTests'])
+ ret = r.main([], tests=['typ.tests.runner_test.ContextTests'])
self.assertEqual(ret, 0)
@@ -80,6 +83,159 @@ class TestSetTests(TestCase):
h.rmtree(tmpdir)
+class TestWinMultiprocessing(TestCase):
+ def make_host(self):
+ return Host()
+
+ def call(self, argv, platform=None, importable=None, **kwargs):
+ h = self.make_host()
+ orig_wd = h.getcwd()
+ tmpdir = None
+ try:
+ tmpdir = h.mkdtemp()
+ h.chdir(tmpdir)
+ h.capture_output()
+ if platform is not None:
+ h.platform = platform
+ r = Runner(h)
+ if importable is not None:
+ r._main_is_importable = lambda: importable
+ ret = r.main(argv, **kwargs)
+ finally:
+ out, err = h.restore_output()
+ h.chdir(orig_wd)
+ if tmpdir:
+ h.rmtree(tmpdir)
+
+ return ret, out, err
+
+ def test_bad_value(self):
+ self.assertRaises(ValueError, self.call, [], win_multiprocessing='foo')
+
+ def test_force(self):
+ h = self.make_host()
+ tmpdir = None
+ orig_wd = h.getcwd()
+ out = err = None
+ out_str = err_str = ''
+ try:
+ tmpdir = h.mkdtemp()
+ h.chdir(tmpdir)
+ out = tempfile.NamedTemporaryFile(delete=False)
+ err = tempfile.NamedTemporaryFile(delete=False)
+ ret = main([], stdout=out, stderr=err,
+ win_multiprocessing=WinMultiprocessing.force)
+ finally:
+ h.chdir(orig_wd)
+ if tmpdir:
+ h.rmtree(tmpdir)
+ if out:
+ out.close()
+ out = open(out.name)
+ out_str = out.read()
+ out.close()
+ h.remove(out.name)
+ if err:
+ err.close()
+ err = open(err.name)
+ err_str = err.read()
+ err.close()
+ h.remove(err.name)
+
+ self.assertEqual(ret, 1)
+ self.assertEqual(out_str, 'No tests to run.\n')
+ self.assertEqual(err_str, '')
+
+ def test_ignore(self):
+ h = self.make_host()
+ if h.platform == 'win32': # pragma: win32
+ self.assertRaises(ValueError, self.call, [],
+ win_multiprocessing=WinMultiprocessing.ignore)
+ else:
+ result = self.call([], platform=None, importable=False,
+ win_multiprocessing=WinMultiprocessing.ignore)
+ ret, out, err = result
+ self.assertEqual(ret, 1)
+ self.assertEqual(out, 'No tests to run.\n')
+ self.assertEqual(err, '')
+
+ def test_multiple_jobs(self):
+ self.assertRaises(ValueError, self.call, ['-j', '2'],
+ platform='win32', importable=False)
+
+ def test_normal(self):
+ # This tests that typ itself is importable ...
+ ret, out, err = self.call([])
+ self.assertEqual(ret, 1)
+ self.assertEqual(out, 'No tests to run.\n')
+ self.assertEqual(err, '')
+
+ def test_real_unimportable_main(self):
+ h = self.make_host()
+ tmpdir = None
+ orig_wd = h.getcwd()
+ out = err = None
+ out_str = err_str = ''
+ try:
+ tmpdir = h.mkdtemp()
+ h.chdir(tmpdir)
+ out = tempfile.NamedTemporaryFile(delete=False)
+ err = tempfile.NamedTemporaryFile(delete=False)
+ path_above_typ = h.realpath(h.dirname(__file__), '..', '..')
+ env = {'PYTHONPATH': path_above_typ}
+ h.write_text_file('test', d("""
+ import sys
+ import typ
+ sys.exit(typ.main())
+ """))
+ h.stdout = out
+ h.stderr = err
+ ret = h.call_inline([h.python_interpreter, h.join(tmpdir, 'test')],
+ env=env)
+ finally:
+ h.chdir(orig_wd)
+ if tmpdir:
+ h.rmtree(tmpdir)
+ if out:
+ out.close()
+ out = open(out.name)
+ out_str = out.read()
+ out.close()
+ h.remove(out.name)
+ if err:
+ err.close()
+ err = open(err.name)
+ err_str = err.read()
+ err.close()
+ h.remove(err.name)
+
+ self.assertEqual(ret, 1)
+ self.assertEqual(out_str, '')
+ self.assertIn('ValueError: The __main__ module is not importable',
+ err_str)
+
+ def test_run_serially(self):
+ ret, out, err = self.call([], importable=False,
+ win_multiprocessing=WinMultiprocessing.spawn)
+ self.assertEqual(ret, 1)
+ self.assertEqual(out, 'No tests to run.\n')
+ self.assertEqual(err, '')
+
+ def test_single_job(self):
+ ret, out, err = self.call(['-j', '1'], platform='win32',
+ importable=False)
+ self.assertEqual(ret, 1)
+ self.assertEqual(out, 'No tests to run.\n')
+ self.assertEqual(err, '')
+
+ def test_spawn(self):
+ ret, out, err = self.call([], importable=False,
+ win_multiprocessing=WinMultiprocessing.spawn)
+ self.assertEqual(ret, 1)
+ self.assertEqual(out, 'No tests to run.\n')
+ self.assertEqual(err, '')
+
+
class ContextTests(TestCase):
def test_context(self):
# This test is mostly intended to be called by
diff --git a/third_party/typ/typ/version.py b/third_party/typ/typ/version.py
index 58b2468..96ebc7a 100644
--- a/third_party/typ/typ/version.py
+++ b/third_party/typ/typ/version.py
@@ -12,4 +12,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-VERSION = '0.8.4'
+VERSION = '0.8.5'