-
Notifications
You must be signed in to change notification settings - Fork 1.1k
PYTHON-2033 Unified Test Format #519
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
PYTHON-2033 Unified Test Format #519
Conversation
6fc38e3
to
d694867
Compare
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.
Looks good so far!
test/utils.py
Outdated
@@ -948,3 +956,118 @@ def assertion_context(msg): | |||
except AssertionError as exc: | |||
msg = '%s (%s)' % (exc, msg) | |||
py3compat.reraise(type(exc), msg, sys.exc_info()[2]) | |||
|
|||
|
|||
class SpecParserUtil(object): |
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.
Is this class needed?
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.
Removed.
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.
SpecParserUtil is still here.
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.
Sorry, removed.
Implementation of |
c6ad26c
to
18e4056
Compare
pymongo/helpers.py
Outdated
@@ -224,7 +224,6 @@ def _check_write_command_response(result): | |||
write_errors = result.get("writeErrors") | |||
if write_errors: | |||
_raise_last_write_error(write_errors) | |||
|
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.
We can remove this change now.
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.
Done.
# process test-level runOnRequirements | ||
run_on_spec = spec.get('runOnRequirements', []) | ||
if not self.should_run_on(run_on_spec): | ||
raise unittest.SkipTest('runOnRequirements not satisfied') |
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.
Shouldn't runOnRequirements happen first, ie before processing createEntities
and initialData
?
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.
good point. Also moved up the processing of skipTest.
test/utils.py
Outdated
@@ -948,3 +956,118 @@ def assertion_context(msg): | |||
except AssertionError as exc: | |||
msg = '%s (%s)' % (exc, msg) | |||
py3compat.reraise(type(exc), msg, sys.exc_info()[2]) | |||
|
|||
|
|||
class SpecParserUtil(object): |
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.
SpecParserUtil is still here.
test/unified_format.py
Outdated
self._listeners[spec['id']] = listener | ||
kwargs['event_listeners'] = [listener] | ||
if client_context.is_mongos and spec.get('useMultipleMongoses'): | ||
kwargs['host'] = client_context.mongos_seeds() |
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.
Change this to kwargs['h']
to fix the host
errors. rs_or_single_client
accepts a h
argument, not host
.
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.
Done.
In 1a79309 I made one more change to how we were skipping entire classes when runOn wasn't satisfied. Previously, we got this extremely unhelpful message when
With my change, this is now more informative:
The other change tweaks how we deal with unsupported schema versions - this was a bug. |
test/unified_format.py
Outdated
if mixin_class is None: | ||
print('Ignoring test file %s with ' | ||
'unsupported schemaVersion %s' % | ||
(fpath, schema_version)) |
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.
Shouldn't we raise some kind of error here so that we actually notice when this happens?
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.
If we do that, we cannot get the test suite green with the valid-fail
tests: https://github.com/mongodb/specifications/tree/master/source/unified-test-format/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.
Shouldn't the valid-fail
tests catch said error to assert that the tests actually do fail?
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.
Like if we copy/paste this test file https://github.com/mongodb/specifications/blob/master/source/unified-test-format/tests/valid-fail/schemaVersion-unsupported.yml into the valid-pass
directory, and then run the test suite, it should fail. What's the behavior in that scenario right now?
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 would think if we expect something to fail, we would mark it as an expected failure and have the test suite still pass. This is what we are doing at the moment with the valid-fail
tests (see the expected_failures
argument):
globals().update(generate_test_classes(
os.path.join(_TEST_PATH, 'valid-fail'),
module=__name__,
class_name_prefix='UnifiedTestFormat',
expected_failures=[
'foo',
'FindOneAndReplace returnDocument invalid enum value',
'FindOneAndUpdate returnDocument invalid enum value'
]))
IIRC the spec is not prescriptive for this. We need to decide what the correct behavior should 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.
I don't think we should need to list out the expected_failures
. I think it's clear that if a test is in the valid-fail
directory it should fail and any test in valid-pass
should pass. Is that the question? Or is the question what type of error is expected?
I'd like to avoid squashing errors and instead raise them.
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.
Updated the code to raise errors. The unsupported schema test file now causes a failure during test class generation as follows:
(pymongo_cp37) ➜ mongo-python-driver git:(PYTHON-2033/unified-test-format) ✗ python setup.py test -s test.test_unified_format
running test
running egg_info
writing pymongo.egg-info/PKG-INFO
writing dependency_links to pymongo.egg-info/dependency_links.txt
writing requirements to pymongo.egg-info/requires.txt
writing top-level names to pymongo.egg-info/top_level.txt
reading manifest file 'pymongo.egg-info/SOURCES.txt'
reading manifest template 'MANIFEST.in'
writing manifest file 'pymongo.egg-info/SOURCES.txt'
running build_ext
Traceback (most recent call last):
File "setup.py", line 431, in <module>
**extra_opts
File "/Users/pmital/.pyenv/versions/3.7.5/envs/pymongo_cp37/lib/python3.7/site-packages/setuptools/__init__.py", line 145, in setup
return distutils.core.setup(**attrs)
File "/Users/pmital/.pyenv/versions/3.7.5/lib/python3.7/distutils/core.py", line 148, in setup
dist.run_commands()
File "/Users/pmital/.pyenv/versions/3.7.5/lib/python3.7/distutils/dist.py", line 966, in run_commands
self.run_command(cmd)
File "/Users/pmital/.pyenv/versions/3.7.5/lib/python3.7/distutils/dist.py", line 985, in run_command
cmd_obj.run()
File "setup.py", line 123, in run
self.test_suite)
File "/Users/pmital/.pyenv/versions/3.7.5/lib/python3.7/unittest/loader.py", line 154, in loadTestsFromName
module = __import__(module_name)
File "/Users/pmital/Developer/mongo-python-driver/test/test_unified_format.py", line 46, in <module>
'FindOneAndUpdate returnDocument invalid enum value'
File "/Users/pmital/Developer/mongo-python-driver/test/unified_format.py", line 989, in generate_test_classes
fpath, schema_version))
ValueError: test file '/Users/pmital/Developer/mongo-python-driver/test/unified-test-format/valid-fail/schemaVersion-unsupported.json' has unsupported schemaVersion '0.1.0.0'
Note that with this change I have had to comment out the code that generates the valid-fail
tests since leaving them would break CI (as they fail when run).
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 issue to consider is that there is now no way to run the other tests in the valid-fail
directory without removing the schemaVersion-unsupported.json
file. Previously, we continued to create test classes even after encountering a failure. Since we are now eagerly surfacing these errors to the user, this is no longer possible.
I don't think it will be a problem in practice (actual spec tests will never cause such a failure, and I think we'd be OK seeing a build failure if they did), but I thought I should point it out nonetheless.
test/unified_format.py
Outdated
(mixin_class, test_base_class_factory(scenario_def),), | ||
{'__module__': module}) | ||
except (AttributeError, KeyError, TypeError): | ||
print("Ignoring invalid test file '%s'\n" |
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.
Shouldn't we raise some kind of error here so that we actually notice when this happens?
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.
Same answer as above.
test/test_unified_format.py
Outdated
# globals().update(generate_test_classes( | ||
# os.path.join(_TEST_PATH, 'valid-fail'), | ||
# module=__name__, | ||
# class_name_prefix='UnifiedTestFormat')) |
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.
Perhaps we can uncomment this by adding a flag expect_failure=True
. When expect_failure=True
we can ignore errors that happen at test construction time, if the test actually does get constructed then we should assert that test fails at runtime. What do you think?
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 is too similar to expected_failures
. How about a bypass_test_generation_errors
kwarg instead that allows us to ignore/bypass errors raised during test creation?
1207c2c
to
735badf
Compare
The remaining failures seem to be unrelated. |
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.
Disable test commands variant is failing:
[2020/12/21 20:08:32.700] ERROR [0.230s]: test_Aggregate_succeeds_after_InterruptedAtShutdown (test_unified_format.TestUnifiedTestFormatValidPassPocRetryableReads)
[2020/12/21 20:08:32.700] ----------------------------------------------------------------------
[2020/12/21 20:08:32.700] Traceback (most recent call last):
[2020/12/21 20:08:32.700] File "/data/mci/7bb1f7d1f5cfc16d0e9aeb8306ed5b7a/src/test/unified_format.py", line 923, in test_case
[2020/12/21 20:08:32.700] self.run_scenario(spec)
[2020/12/21 20:08:32.700] File "/data/mci/7bb1f7d1f5cfc16d0e9aeb8306ed5b7a/src/test/unified_format.py", line 907, in run_scenario
[2020/12/21 20:08:32.700] self.run_operations(spec['operations'])
[2020/12/21 20:08:32.700] File "/data/mci/7bb1f7d1f5cfc16d0e9aeb8306ed5b7a/src/test/unified_format.py", line 848, in run_operations
[2020/12/21 20:08:32.700] self.run_special_operation(op)
[2020/12/21 20:08:32.700] File "/data/mci/7bb1f7d1f5cfc16d0e9aeb8306ed5b7a/src/test/unified_format.py", line 840, in run_special_operation
[2020/12/21 20:08:32.700] method(spec['arguments'])
[2020/12/21 20:08:32.700] File "/data/mci/7bb1f7d1f5cfc16d0e9aeb8306ed5b7a/src/test/unified_format.py", line 756, in _testOperation_failPoint
[2020/12/21 20:08:32.700] command_args=spec['failPoint'])
[2020/12/21 20:08:32.700] File "/data/mci/7bb1f7d1f5cfc16d0e9aeb8306ed5b7a/src/test/unified_format.py", line 748, in __set_fail_point
[2020/12/21 20:08:32.700] client.admin.command(cmd_on)
[2020/12/21 20:08:32.700] File "/data/mci/7bb1f7d1f5cfc16d0e9aeb8306ed5b7a/src/pymongo/database.py", line 743, in command
[2020/12/21 20:08:32.700] codec_options, session=session, **kwargs)
[2020/12/21 20:08:32.700] File "/data/mci/7bb1f7d1f5cfc16d0e9aeb8306ed5b7a/src/pymongo/database.py", line 640, in _command
[2020/12/21 20:08:32.700] client=self.__client)
[2020/12/21 20:08:32.700] File "/data/mci/7bb1f7d1f5cfc16d0e9aeb8306ed5b7a/src/pymongo/pool.py", line 719, in command
[2020/12/21 20:08:32.700] exhaust_allowed=exhaust_allowed)
[2020/12/21 20:08:32.700] File "/data/mci/7bb1f7d1f5cfc16d0e9aeb8306ed5b7a/src/pymongo/network.py", line 161, in command
[2020/12/21 20:08:32.700] parse_write_concern_error=parse_write_concern_error)
[2020/12/21 20:08:32.700] File "/data/mci/7bb1f7d1f5cfc16d0e9aeb8306ed5b7a/src/pymongo/helpers.py", line 164, in _check_command_response
[2020/12/21 20:08:32.700] raise OperationFailure(errmsg, code, response, max_wire_version)
[2020/12/21 20:08:32.700] OperationFailure: no such command: 'configureFailPoint', full error: {u'codeName': u'CommandNotFound', u'code': 59, u'ok': 0.0, u'errmsg': u"no such command: 'configureFailPoint'"}
Probably missing a @require_test_commands
for tests that use failPoints.
test/unified_format.py
Outdated
@@ -544,6 +544,19 @@ def setUpClass(cls): | |||
raise unittest.SkipTest( | |||
'%s runOnRequirements not satisfied' % (cls.__name__,)) | |||
|
|||
# add any special-casing for skipping tests here | |||
if client_context.storage_engine == 'mmapv1': | |||
if cls.TEST_SPEC['description'].find('retryable-writes') != -1: |
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.
Nit: 'retryable-writes' in cls.TEST_SPEC['description']
instead of find() != -1
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.
Done
test/unified_format.py
Outdated
if cls.TEST_SPEC['description'].find( | ||
'transactions-convenient-api') != -1: | ||
raise unittest.SkipTest( | ||
"MMAPv1 does not support document-level locking") |
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.
For these would it make more sense to skip any test that uses startTransaction()/withTransaction()/watch() on mmap rather than using strings (which can change)?
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.
Done
…pecify module in which dynamically created classes live
7182e75
to
855c752
Compare
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.
LGTM
(cherry picked from commit 6b01235)
PYTHON-2033
To Do:
$$matchesEntity
operatorImplement- this will be implemented along with GridFS since it is not used by any tests other than GridFS.$$matchesHexBytes
operatorexpectError.errorContains
expectError.errorCode
expectError.errorCodeName
targetedFailPoint
operationuriOptions
anduseMultipleMongoses
toMongoClient
entity creation$$type
operator