Skip to content

Use Python's unittest for validating bundles and configurations? #98

@wking

Description

@wking

Currently we're using Go, which means it's hard to build a single validator that can process multiple versions of the spec, because you'd have to use tedious interface{} manipulation or load separate packages with structures for each supported version.

We're also not using a test suite, which means it's hard to test multiple aspects of the configuration and report on several errors (the current code just dies on the first error).

By using Python's unittest, we get more convenient handling of generic JSON and an established test framework that doesn't need a lot of boilerplate. I've started working up this approach here if folks want to kick the tires. Examples of potentially tricky things and how this approach lets us handle them easily:

  • Skipping particular tests if the configuration has an unrecognized version (wking/oci-runtime-config-validator@c607380f).
  • Applying the Windows-specific mount-nesting restriction (here).
  • Applying the 0.5.0-specific (and previous, but I haven't bothered supporting them) relative root.path requirement (here).

Example compact output with the current tip (wking/oci-runtime-config-validator@cd0facf0a. I still haven't finished process, later entries in config.md or anything from the platform-specific files):

$ BUNDLE=~/src/opencontainers/my-app python3 -m unittest
...s.............
----------------------------------------------------------------------
Ran 17 tests in 0.006s

OK (skipped=1)

and verbose output:

$ BUNDLE=~/src/opencontainers/my-app python3 -m unittest -v
test_configuration (test.test_bundle.TestBundle)
config.json MUST reside in the root of the bundle directory. ... ok
test_root (test.test_bundle.TestBundle)
The bundle directory MUST contain the root filesystem. ... ok
test_destination (test.test_mounts.TestMounts)
destination (string, required). ... ok
test_destination_nesting (test.test_mounts.TestMounts)
Mount destinations MUST not be nested within another mount. ... skipped 'the destination-nesting restriction only applies to Windows for specification version 1.0.0-rc1.'
test_options (test.test_mounts.TestMounts)
options (list of strings, optional). ... ok
test_source (test.test_mounts.TestMounts)
source (string, required). ... ok
test_type (test.test_mounts.TestMounts)
type (string, required). ... ok
test_args (test.test_process.TestProcess)
args (array of strings, required). ... ok
test_cwd (test.test_process.TestProcess)
cwd (string, required). ... ok
test_env (test.test_process.TestProcess)
env (array of strings, optional). ... ok
test_process (test.test_process.TestProcess)
process (object, required). ... ok
test_terminal (test.test_process.TestProcess)
terminal (bool, optional). ... ok
test_path (test.test_root.TestRoot)
path (string, required). ... ok
test_readonly (test.test_root.TestRoot)
readonly (bool, optional). ... ok
test_syntax (test.test_syntax.TestSyntax)
All configuration JSON MUST be encoded in UTF-8. ... ok
test_recognized_version (test.test_version.TestVersion)
Check for a recognized configuration version. ... ok
test_semantic_version (test.test_version.TestVersion)
ociVersion (string, required) MUST be in SemVer v2.0.0 format. ... ok

----------------------------------------------------------------------
Ran 17 tests in 0.007s

OK (skipped=1)

And with an unrecognized version:

$ BUNDLE=~/src/opencontainers/my-app python3 -m unittest
.sssssssssssss.F.
======================================================================
FAIL: test_recognized_version (test.test_version.TestVersion)
Check for a recognized configuration version.
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/wking/src/opencontainers/oci-runtime-config-validator/test/test_version.py", line 39, in test_recognized_version
    .format(util.VERSION))
AssertionError: '1.0.0-rc2' not found in ['1.0.0-rc1', '0.5.0'] : Unrecognized configuration version.  Either your configuration does not match an OCI specification or the test suite has not been taught to process the version you are using.

----------------------------------------------------------------------
Ran 17 tests in 0.002s

FAILED (failures=1, skipped=13)
$ BUNDLE=~/src/opencontainers/my-app python3 -m unittest -v
test_configuration (test.test_bundle.TestBundle)
config.json MUST reside in the root of the bundle directory. ... ok
test_root (test.test_bundle.TestBundle)
The bundle directory MUST contain the root filesystem. ... skipped 'cannot validate an unrecognized version'
test_destination (test.test_mounts.TestMounts)
destination (string, required). ... skipped 'cannot validate an unrecognized version'
test_destination_nesting (test.test_mounts.TestMounts)
Mount destinations MUST not be nested within another mount. ... skipped 'cannot validate an unrecognized version'
test_options (test.test_mounts.TestMounts)
options (list of strings, optional). ... skipped 'cannot validate an unrecognized version'
test_source (test.test_mounts.TestMounts)
source (string, required). ... skipped 'cannot validate an unrecognized version'
test_type (test.test_mounts.TestMounts)
type (string, required). ... skipped 'cannot validate an unrecognized version'
test_args (test.test_process.TestProcess)
args (array of strings, required). ... skipped 'cannot validate an unrecognized version'
test_cwd (test.test_process.TestProcess)
cwd (string, required). ... skipped 'cannot validate an unrecognized version'
test_env (test.test_process.TestProcess)
env (array of strings, optional). ... skipped 'cannot validate an unrecognized version'
test_process (test.test_process.TestProcess)
process (object, required). ... skipped 'cannot validate an unrecognized version'
test_terminal (test.test_process.TestProcess)
terminal (bool, optional). ... skipped 'cannot validate an unrecognized version'
test_path (test.test_root.TestRoot)
path (string, required). ... skipped 'cannot validate an unrecognized version'
test_readonly (test.test_root.TestRoot)
readonly (bool, optional). ... skipped 'cannot validate an unrecognized version'
test_syntax (test.test_syntax.TestSyntax)
All configuration JSON MUST be encoded in UTF-8. ... ok
test_recognized_version (test.test_version.TestVersion)
Check for a recognized configuration version. ... FAIL
test_semantic_version (test.test_version.TestVersion)
ociVersion (string, required) MUST be in SemVer v2.0.0 format. ... ok

======================================================================
FAIL: test_recognized_version (test.test_version.TestVersion)
Check for a recognized configuration version.
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/wking/src/opencontainers/oci-runtime-config-validator/test/test_version.py", line 39, in test_recognized_version
    .format(util.VERSION))
AssertionError: '1.0.0-rc2' not found in ['1.0.0-rc1', '0.5.0'] : Unrecognized configuration version.  Either your configuration does not match an OCI specification or the test suite has not been taught to process the version you are using.

----------------------------------------------------------------------
Ran 17 tests in 0.003s

FAILED (failures=1, skipped=13)

This approach would also likely work fine in other languages and test frameworks that make it easy to handle generic JSON. I'm familiar with Python, but if the OCI community prefers a different language, I'm game to try. So:

  1. Does this sound like a useful direction? If so,
  2. Is it worth pulling into ocitools? Go is a reasonable language for compiled binaries like runtimetest (certainly a better choice for that than Python), and multi-language repositories can get awkward. The validation tasks (configuration validation and runtime validation) seem to decouple well, so I'd rather not stuff both into a single repository. But I can live with a single repo if it's the maintainer preference ;).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions