Skip to content

Commit 26eb13b

Browse files
committed
Fix parallel build
1 parent e99f10e commit 26eb13b

File tree

12 files changed

+143
-97
lines changed

12 files changed

+143
-97
lines changed

.travis.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,19 @@ before_install:
1313
install:
1414
- pip install --upgrade pip
1515
- pip install --upgrade setuptools
16-
- pip install coverage pytest-cov pytest-django
16+
#- pip install coverage pytest-cov pytest-django
1717
script:
1818
# Travis clones the project to $TRAVIS_BUILD_DIR and cds there before running
1919
# the scripts.
2020
- ./dev_tools/src/d1_dev/setup-all.py --root $TRAVIS_BUILD_DIR develop
2121
- ./dev_tools/src/d1_dev/syspath.py
2222
- pip freeze
2323
- pip check
24-
- pytest --cov=. --cov-report=term --cov-report=xml
24+
- pytest -n auto --cov=. --cov-report=term --cov-report=xml
2525
after_success:
2626
# Submit results to Coveralls.io.
2727
# Coveralls has a requirement for requests >= 1.0.0, so we install it after
2828
# our packages to prevent it from pulling in the latest version, which is
2929
# likely to conflict with the fixed version our packages pull in.
30-
- pip install coveralls
30+
# - pip install coveralls
3131
- coveralls

client_cli/src/d1_cli/tests/test_check_dependencies.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,11 @@ def test_1000(self):
3333
"""check_dependencies(): Returns True given modules known to be present"""
3434
assert d1_cli.impl.check_dependencies.are_modules_importable(['os', 'sys'])
3535

36-
def test_1010(self):
36+
def test_1010(self, caplog):
3737
"""check_dependencies(): Returns false and logs error on invalid module"""
38-
with d1_test.d1_test_case.capture_log() as log_stream:
39-
assert not d1_cli.impl.check_dependencies. \
40-
are_modules_importable(['os', 'invalid_module'])
41-
assert 'dependencies failed' in log_stream.getvalue()
38+
assert not (
39+
d1_cli.impl.check_dependencies.are_modules_importable(
40+
['os', 'invalid_module']
41+
)
42+
)
43+
assert 'dependencies failed' in d1_test.d1_test_case.get_caplog_text(caplog)

conftest.py

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,15 @@
2424
import logging
2525
import os
2626
import subprocess
27+
import tempfile
2728

2829
import psycopg2
2930
import psycopg2.extensions
3031
import pytest
3132

33+
import d1_gmn.app.sciobj_store
34+
35+
import d1_test.instance_generator.random_data
3236
import d1_test.sample
3337

3438
from d1_client.cnclient_1_2 import CoordinatingNodeClient_1_2 as cn_v1
@@ -102,7 +106,7 @@ def pytest_addoption(parser):
102106
'--skip-clear', action='store_true', help='Clear the list of passed tests'
103107
)
104108
parser.addoption(
105-
'--skip-list', action='store_true', help='Print the list of passed tests'
109+
'--skip-print', action='store_true', help='Print the list of passed tests'
106110
)
107111

108112

@@ -113,18 +117,20 @@ def pytest_sessionstart(session):
113117
"""Called by pytest before calling session.main()"""
114118
if pytest.config.getoption('--sample-tidy'):
115119
d1_test.sample.start_tidy()
120+
pytest.exit('Tidy started. Run complete to complete')
116121
if pytest.config.getoption('--skip-clear'):
117122
_clear_skip_list()
118-
if pytest.config.getoption('--skip-list'):
123+
if pytest.config.getoption('--skip-print'):
119124
_print_skip_list()
120125

121126

122127
def pytest_sessionfinish(session, exitstatus):
123128
"""Called by pytest after the test session ends"""
124129
# if exitstatus != 2:
125-
skipped_count = pytest.config.cache.get(D1_SKIP_COUNT, 0)
126-
if skipped_count:
127-
logging.warn('Skipped {} previously passed tests'.format(skipped_count))
130+
if pytest.config.getoption('--skip'):
131+
skipped_count = pytest.config.cache.get(D1_SKIP_COUNT, 0)
132+
if skipped_count:
133+
logging.warn('Skipped {} previously passed tests'.format(skipped_count))
128134

129135

130136
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
@@ -298,20 +304,34 @@ def mn_client_v1_v2(request):
298304
yield request.param(MOCK_BASE_URL)
299305

300306

307+
# Settings
308+
309+
310+
@pytest.fixture(scope='session', autouse=True)
311+
def set_unique_sciobj_store_path(request):
312+
tmp_store_path = os.path.join(
313+
tempfile.gettempdir(), 'gmn_test_obj_store_{}{}'.format(
314+
d1_test.instance_generator.random_data.random_lower_ascii(
315+
min_len=12, max_len=12
316+
), get_xdist_suffix(request)
317+
)
318+
)
319+
django.conf.settings.OBJECT_STORE_PATH = tmp_store_path
320+
d1_gmn.app.sciobj_store.create_clean_tmp_store()
321+
322+
301323
# DB fixtures
302324

303325

304326
@pytest.yield_fixture(scope='session')
305327
def django_db_setup(request, django_db_blocker):
306328
"""Set up DB fixture
307329
"""
308-
xdist_suffix = getattr(request.config, 'slaveinput', {}).get('slaveid', '')
309-
310330
logging.info('Setting up DB fixture')
311331

312332
test_db_key = 'default'
313333
test_db_name = django.conf.settings.DATABASES[test_db_key]['NAME']
314-
test_db_name += xdist_suffix
334+
test_db_name += get_xdist_suffix(request)
315335
django.conf.settings.DATABASES[test_db_key]['NAME'] = test_db_name
316336

317337
template_db_key = 'template'
@@ -339,6 +359,15 @@ def django_db_setup(request, django_db_blocker):
339359
yield
340360

341361

362+
def get_xdist_suffix(request):
363+
"""When running in parallel with xdist, each thread gets a different suffix.
364+
- In parallel run, return '_gw1', etc.
365+
- In single run, return ''.
366+
"""
367+
s = getattr(request.config, 'slaveinput', {}).get('slaveid')
368+
return '_{}'.format(s) if s is not None else ''
369+
370+
342371
def create_db_from_template(test_db_name, template_db_name):
343372
logging.info(
344373
'Creating test DB from template. test_db="{}" template_db="{}"'.

gmn/src/d1_gmn/tests/test_mgmt_cert.py

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -32,38 +32,37 @@
3232

3333

3434
class TestCmdCert(d1_gmn.tests.gmn_test_case.GMNTestCase):
35-
def _cert_whitelist(self):
35+
def _cert_whitelist(self, caplog):
3636
cert_path = self.sample.get_path(
3737
'cert_cn_ucsb_1_dataone_org_20150709_180838.pem'
3838
)
3939
with self.mock.disable_management_command_logging():
40-
with d1_test.d1_test_case.capture_log() as log_stream:
41-
with d1_test.d1_test_case.disable_debug_level_logging():
42-
django.core.management.call_command('cert', 'whitelist', cert_path)
43-
django.core.management.call_command('whitelist', 'view', cert_path)
44-
return log_stream.getvalue()
40+
with d1_test.d1_test_case.disable_debug_level_logging():
41+
django.core.management.call_command('cert', 'whitelist', cert_path)
42+
django.core.management.call_command('whitelist', 'view', cert_path)
43+
return d1_test.d1_test_case.get_caplog_text(caplog)
4544

46-
def test_1000(self):
45+
def test_1000(self, caplog):
4746
"""cert view <pem>: Lists subjects from DN and SubjectInfo
4847
"""
49-
# print (logging.Logger.manager.loggerDict.keys())
5048
cert_path = self.sample.get_path('cert_with_full_subject_info.pem')
51-
with d1_test.d1_test_case.capture_log() as log_stream:
52-
with self.mock.disable_management_command_logging():
53-
with d1_test.d1_test_case.disable_debug_level_logging():
54-
django.core.management.call_command('cert', 'view', cert_path)
55-
self.sample.assert_equals(log_stream.getvalue(), 'view')
49+
with self.mock.disable_management_command_logging():
50+
with d1_test.d1_test_case.disable_debug_level_logging():
51+
django.core.management.call_command('cert', 'view', cert_path)
52+
self.sample.assert_equals(
53+
d1_test.d1_test_case.get_caplog_text(caplog), 'view'
54+
)
5655

57-
def test_1010(self):
56+
def test_1010(self, caplog):
5857
"""cert whitelist <pem>: Whitelists subj from cert if not already whitelisted
5958
"""
60-
log_str = self._cert_whitelist()
59+
log_str = self._cert_whitelist(caplog)
6160
self.sample.assert_equals(log_str, 'whitelist_new')
6261

63-
def test_1020(self):
62+
def test_1020(self, caplog):
6463
"""cert whitelist <pem>: Raise on cert with subj already whitelisted
6564
"""
66-
self._cert_whitelist()
65+
self._cert_whitelist(caplog)
6766
with pytest.raises(django.core.management.CommandError) as exc_info:
68-
self._cert_whitelist()
67+
self._cert_whitelist(caplog)
6968
assert 'already enabled' in str(exc_info.value)

gmn/src/d1_gmn/tests/test_mgmt_import.py

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
"""
2222
from __future__ import absolute_import
2323

24+
import logging
25+
2426
import responses
2527

2628
import d1_gmn.tests.gmn_test_case
@@ -38,21 +40,22 @@
3840
@d1_test.d1_test_case.reproducible_random_decorator('TestMgmtImport')
3941
class TestMgmtImport(d1_gmn.tests.gmn_test_case.GMNTestCase):
4042
@responses.activate
41-
def test_1000(self):
42-
mock_object_list.add_callback(d1_test.d1_test_case.MOCK_REMOTE_BASE_URL)
43-
mock_log_records.add_callback(d1_test.d1_test_case.MOCK_REMOTE_BASE_URL)
44-
mock_get_system_metadata.add_callback(
45-
d1_test.d1_test_case.MOCK_REMOTE_BASE_URL
46-
)
47-
mock_get.add_callback(d1_test.d1_test_case.MOCK_REMOTE_BASE_URL)
48-
49-
with self.mock.disable_management_command_logging():
50-
with d1_test.d1_test_case.capture_log() as log_stream:
43+
def test_1000(self, caplog):
44+
with caplog.at_level(logging.INFO):
45+
mock_object_list.add_callback(d1_test.d1_test_case.MOCK_REMOTE_BASE_URL)
46+
mock_log_records.add_callback(d1_test.d1_test_case.MOCK_REMOTE_BASE_URL)
47+
mock_get_system_metadata.add_callback(
48+
d1_test.d1_test_case.MOCK_REMOTE_BASE_URL
49+
)
50+
mock_get.add_callback(d1_test.d1_test_case.MOCK_REMOTE_BASE_URL)
51+
52+
with self.mock.disable_management_command_logging():
5153
with d1_test.d1_test_case.disable_debug_level_logging():
5254
django.core.management.call_command(
5355
'import', '--force', '--major=2',
5456
d1_test.d1_test_case.MOCK_REMOTE_BASE_URL
5557
)
5658

57-
log_str = log_stream.getvalue()
58-
self.sample.assert_equals(log_str, 'import')
59+
self.sample.assert_equals(
60+
d1_test.d1_test_case.get_caplog_text(caplog), 'import'
61+
)

lib_client/src/d1_client/tests/test_iter_sysmeta_multi.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def page_size(request):
4343
yield request.param
4444

4545

46-
@pytest.fixture(scope='function', params=[1, 4, 11])
46+
@pytest.fixture(scope='function', params=[1, 5])
4747
def n_workers(request):
4848
yield request.param
4949

lib_common/src/d1_common/tests/test_jwt.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,11 +153,13 @@ def test_1070(self):
153153
) is None
154154

155155
@freezegun.freeze_time('2021-01-01')
156-
def test_1080(self):
156+
def test_1080(self, caplog):
157157
"""log_jwt_bu64_info(): Outputs expected log"""
158158
jwt_bu64 = self.sample.load('jwt_token_20170612_232523.base64')
159-
with d1_test.d1_test_case.capture_log() as log_stream:
159+
with caplog.at_level(logging.INFO):
160+
d1_test.d1_test_case.clear_caplog(caplog)
160161
d1_common.cert.jwt.log_jwt_bu64_info(logging.info, "test msg", jwt_bu64)
161162
self.sample.assert_equals(
162-
log_stream.getvalue(), 'jwt_token_log_jwt_bu64_info_expected'
163+
d1_test.d1_test_case.get_caplog_text(caplog),
164+
'jwt_token_log_jwt_bu64_info_expected'
163165
)

lib_common/src/d1_common/tests/test_x509.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,11 +108,13 @@ def test_1050(self):
108108
}, 'primary_and_equivalent_without_subject_info')
109109

110110
@freezegun.freeze_time('2021-01-01')
111-
def test_1060(self):
111+
def test_1060(self, caplog):
112112
"""log_cert_info(): Outputs expected log"""
113113
cert_obj = d1_common.cert.x509.deserialize_pem(
114114
self.cert_simple_subject_info_pem
115115
)
116-
with d1_test.d1_test_case.capture_log() as log_stream:
116+
with caplog.at_level(logging.WARN):
117117
d1_common.cert.x509.log_cert_info(logging.warn, 'test msg', cert_obj)
118-
self.sample.assert_equals(log_stream.getvalue(), 'log_cert_info_expected')
118+
self.sample.assert_equals(
119+
d1_test.d1_test_case.get_caplog_text(caplog), 'log_cert_info_expected'
120+
)

test_utilities/src/d1_test/d1_test_case.py

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -82,23 +82,37 @@ def capture_std():
8282
sys.stdout, sys.stderr = old_out, old_err
8383

8484

85-
@contextlib.contextmanager
86-
def capture_log(log_level=logging.DEBUG):
87-
"""Capture anything output by the logging module. Uses a handler that does not
88-
not include any context, such as date-time or originating module to help keep
89-
the result stable over time.
85+
# @contextlib.contextmanager
86+
# def capture_log(log_level=logging.DEBUG):
87+
# """Capture anything output by the logging module. Uses a handler that does not
88+
# not include any context, such as timestamp and module name, so that the
89+
# result is reproducible over time.
90+
# """
91+
# stream = StringIO.StringIO()
92+
# logger = None
93+
# stream_handler = None
94+
# try:
95+
# logger = logging.getLogger()
96+
# logger.level = log_level
97+
# stream_handler = logging.StreamHandler(stream)
98+
# logger.addHandler(stream_handler)
99+
# yield stream
100+
# finally:
101+
# logger.removeHandler(stream_handler)
102+
103+
104+
def get_caplog_text(caplog, logger_name=None):
105+
"""Return the log messages currently captured by the caplog fixture
106+
If {module_name} is set, only messages from the given module are returned.
90107
"""
91-
stream = StringIO.StringIO()
92-
logger = None
93-
stream_handler = None
94-
try:
95-
logger = logging.getLogger()
96-
logger.level = log_level
97-
stream_handler = logging.StreamHandler(stream)
98-
logger.addHandler(stream_handler)
99-
yield stream
100-
finally:
101-
logger.removeHandler(stream_handler)
108+
return '\n'.join([
109+
r.getMessage()
110+
for r in caplog.records if (logger_name is None) or logger_name == r.name
111+
])
112+
113+
114+
def clear_caplog(caplog):
115+
caplog.handler.records = []
102116

103117

104118
@contextlib.contextmanager

test_utilities/src/d1_test/sample.py

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import re
2626
import subprocess
2727
import tempfile
28+
import thread
2829
import traceback
2930

3031
import pytest
@@ -42,6 +43,8 @@
4243
import django.core
4344
import django.core.management
4445

46+
tidy_lock = thread.allocate_lock()
47+
4548

4649
def start_tidy():
4750
"""Call at start of test run to tidy the samples directory.
@@ -129,16 +132,17 @@ def assert_no_diff(a_obj, b_obj):
129132

130133

131134
def get_path(filename):
132-
path = os.path.join(d1_common.util.abs_path('test_docs'), filename)
133-
if os.path.isfile(path):
135+
with tidy_lock:
136+
path = os.path.join(d1_common.util.abs_path('test_docs'), filename)
137+
if os.path.isfile(path):
138+
return path
139+
tidy_file_path = os.path.join(
140+
d1_common.util.abs_path('test_docs_tidy'), filename
141+
)
142+
if os.path.isfile(tidy_file_path):
143+
os.rename(tidy_file_path, path)
144+
logging.info('Moved from tidy: {} -> {}'.format(tidy_file_path, path))
134145
return path
135-
tidy_file_path = os.path.join(
136-
d1_common.util.abs_path('test_docs_tidy'), filename
137-
)
138-
if os.path.isfile(tidy_file_path):
139-
os.rename(tidy_file_path, path)
140-
logging.info('Moved from tidy: {} -> {}'.format(tidy_file_path, path))
141-
return path
142146

143147

144148
def load(filename, mode_str='rb'):
@@ -254,7 +258,7 @@ def _get_or_create_path(filename):
254258
See the test docs for usage.
255259
"""
256260
path = get_path(filename)
257-
logging.info('Sample path: {}'.format(path))
261+
logging.debug('Sample path: {}'.format(path))
258262
if not os.path.isfile(path):
259263
logging.info('Write new blank file: {}'.format(path))
260264
with open(path, 'w') as f:

0 commit comments

Comments
 (0)