Run pythonified tests in the containerized alternative architecture tests (#38)

Run pythonified tests in the containerized alternative architecture tests
This commit is contained in:
Douglas Stebila 2019-02-18 08:47:14 -05:00 committed by GitHub
commit dd15a07940
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 76 additions and 37 deletions

View File

@ -51,7 +51,8 @@ matrix:
script: script:
- docker run --rm --privileged multiarch/qemu-user-static:register --reset - docker run --rm --privileged multiarch/qemu-user-static:register --reset
- docker run --rm -v `pwd`:`pwd` -w `pwd` "pqclean/debian-unstable-ppc" /bin/bash -c "uname -a && - docker run --rm -v `pwd`:`pwd` -w `pwd` "pqclean/debian-unstable-ppc" /bin/bash -c "uname -a &&
make ${MAKETARGET}" make ${MAKETARGET} &&
cd test && python3 -m nose --rednose --verbose"
- name: "Run tests on qemu-arm32 (GCC)" - name: "Run tests on qemu-arm32 (GCC)"
os: linux os: linux
services: docker services: docker
@ -60,7 +61,8 @@ matrix:
script: script:
- docker run --rm --privileged multiarch/qemu-user-static:register --reset - docker run --rm --privileged multiarch/qemu-user-static:register --reset
- docker run --rm -v `pwd`:`pwd` -w `pwd` "pqclean/debian-buster-arm" /bin/bash -c "uname -a && - docker run --rm -v `pwd`:`pwd` -w `pwd` "pqclean/debian-buster-arm" /bin/bash -c "uname -a &&
make ${MAKETARGET}" make ${MAKETARGET} &&
cd test && python3 -m nose --rednose --verbose"
- name: "Run tests on qemu-aarch64 (GCC)" - name: "Run tests on qemu-aarch64 (GCC)"
os: linux os: linux
services: docker services: docker
@ -69,7 +71,8 @@ matrix:
script: script:
- docker run --rm --privileged multiarch/qemu-user-static:register --reset - docker run --rm --privileged multiarch/qemu-user-static:register --reset
- docker run --rm -v `pwd`:`pwd` -w `pwd` "pqclean/debian-buster-aarch64" /bin/bash -c "uname -a && - docker run --rm -v `pwd`:`pwd` -w `pwd` "pqclean/debian-buster-aarch64" /bin/bash -c "uname -a &&
make ${MAKETARGET}" make ${MAKETARGET} &&
cd test && python3 -m nose --rednose --verbose"
- name: "MacOS + Clang" - name: "MacOS + Clang"
os: osx os: osx
osx_image: xcode10.1 osx_image: xcode10.1

View File

@ -1,13 +1,19 @@
import subprocess import subprocess
def run_subprocess(command, working_dir, expected_returncode = 0):
"""Helper function to run a shell command and report success/failure depending on the exit status of the shell command.""" def run_subprocess(command, working_dir, expected_returncode=0):
# Note we need to capture stdout/stderr from the subprocess, then print it, which nose/unittest will then capture and buffer appropriately """
Helper function to run a shell command and report success/failure
depending on the exit status of the shell command.
"""
# Note we need to capture stdout/stderr from the subprocess,
# then print it, which nose/unittest will then capture and
# buffer appropriately
result = subprocess.run( result = subprocess.run(
command, command,
stdout = subprocess.PIPE, stdout=subprocess.PIPE,
stderr = subprocess.STDOUT, stderr=subprocess.STDOUT,
cwd = working_dir cwd=working_dir
) )
print(result.stdout.decode('utf-8')) print(result.stdout.decode('utf-8'))
assert(result.returncode == expected_returncode) assert(result.returncode == expected_returncode)

View File

@ -1,6 +1,7 @@
import os import os
import yaml import yaml
class Scheme: class Scheme:
def __init__(self): def __init__(self):
self.type = None self.type = None
@ -58,6 +59,7 @@ class Scheme:
print("Can't open {}: {}".format(metafile, e)) print("Can't open {}: {}".format(metafile, e))
return None return None
class Implementation: class Implementation:
def __init__(self, scheme, name): def __init__(self, scheme, name):
@ -83,22 +85,24 @@ class Implementation:
implementations.append(Implementation(scheme, d)) implementations.append(Implementation(scheme, d))
return implementations return implementations
class KEM(Scheme): class KEM(Scheme):
def __init__(self, name: str): def __init__(self, name: str):
self.type = 'kem' self.type = 'kem'
self.name = name; self.name = name
self.implementations = Implementation.all_implementations(self) self.implementations = Implementation.all_implementations(self)
@staticmethod @staticmethod
def all_kems() -> list: def all_kems() -> list:
return Scheme.all_schemes_of_type('kem') return Scheme.all_schemes_of_type('kem')
class Signature(Scheme): class Signature(Scheme):
def __init__(self, name: str): def __init__(self, name: str):
self.type = 'sign' self.type = 'sign'
self.name = name; self.name = name
self.implementations = Implementation.all_implementations(self) self.implementations = Implementation.all_implementations(self)
@staticmethod @staticmethod

View File

@ -1,18 +1,21 @@
""" """
Checks that the archive library can be successfully built for every scheme/implementation. Checks that the archive library can be successfully built for every
scheme/implementation.
""" """
import os
import pqclean import pqclean
import helpers import helpers
def test_compile_lib(): def test_compile_lib():
for scheme in pqclean.Scheme.all_schemes(): for scheme in pqclean.Scheme.all_schemes():
for implementation in scheme.implementations: for implementation in scheme.implementations:
yield check_compile_lib, scheme.name, implementation.name yield check_compile_lib, scheme.name, implementation.name
def check_compile_lib(scheme_name, implementation_name): def check_compile_lib(scheme_name, implementation_name):
implementation = pqclean.Implementation.by_name(scheme_name, implementation_name) implementation = pqclean.Implementation.by_name(
scheme_name, implementation_name)
helpers.run_subprocess( helpers.run_subprocess(
['make', 'clean'], ['make', 'clean'],
implementation.path() implementation.path()

View File

@ -1,20 +1,27 @@
""" """
Checks that the functional test program (functest) can be successfully built and executed for every scheme/implementation. Checks that the functional test program (functest) can be successfully built
and executed for every scheme/implementation.
""" """
import os import os
import pqclean import pqclean
import helpers import helpers
def test_functest(): def test_functest():
for scheme in pqclean.Scheme.all_schemes(): for scheme in pqclean.Scheme.all_schemes():
for implementation in scheme.implementations: for implementation in scheme.implementations:
yield check_functest, scheme.name, implementation.name yield check_functest, scheme.name, implementation.name
def check_functest(scheme_name, implementation_name): def check_functest(scheme_name, implementation_name):
implementation = pqclean.Implementation.by_name(scheme_name, implementation_name) implementation = pqclean.Implementation.by_name(
scheme_name, implementation_name)
helpers.run_subprocess( helpers.run_subprocess(
['make', 'TYPE=' + implementation.scheme.type, 'SCHEME=' + scheme_name, 'IMPLEMENTATION=' + implementation_name], ['make',
'TYPE={}'.format(implementation.scheme.type),
'SCHEME={}'.format(scheme_name),
'IMPLEMENTATION={}'.format(implementation_name)],
os.path.join('..', 'test') os.path.join('..', 'test')
) )
helpers.run_subprocess( helpers.run_subprocess(

View File

@ -1,17 +1,21 @@
""" """
Checks that a LICENSE or LICENSE.txt file is present for every implementation of the specified scheme. Checks that a LICENSE or LICENSE.txt file is present for every
implementation of the specified scheme.
""" """
import os import os
import pqclean import pqclean
def test_license(): def test_license():
for scheme in pqclean.Scheme.all_schemes(): for scheme in pqclean.Scheme.all_schemes():
for implementation in scheme.implementations: for implementation in scheme.implementations:
yield check_license, scheme.name, implementation.name yield check_license, scheme.name, implementation.name
def check_license(scheme_name, implementation_name): def check_license(scheme_name, implementation_name):
implementation = pqclean.Implementation.by_name(scheme_name, implementation_name) implementation = pqclean.Implementation.by_name(
scheme_name, implementation_name)
p1 = os.path.join(implementation.path(), 'LICENSE') p1 = os.path.join(implementation.path(), 'LICENSE')
p2 = os.path.join(implementation.path(), 'LICENSE.txt') p2 = os.path.join(implementation.path(), 'LICENSE.txt')
assert(os.path.isfile(p1) or os.path.isfile(p2)) assert(os.path.isfile(p1) or os.path.isfile(p2))

View File

@ -4,15 +4,14 @@ Verify the metadata specified in the META.yml files.
import copy import copy
import itertools import itertools
import os
import pqclean import pqclean
import yaml
import unittest
def test_metadata(): def test_metadata():
for scheme in pqclean.Scheme.all_schemes(): for scheme in pqclean.Scheme.all_schemes():
yield check_metadata, scheme.name yield check_metadata, scheme.name
def check_metadata(scheme_name): def check_metadata(scheme_name):
scheme = pqclean.Scheme.by_name(scheme_name) scheme = pqclean.Scheme.by_name(scheme_name)
metadata = scheme.metadata() metadata = scheme.metadata()
@ -22,17 +21,22 @@ def check_metadata(scheme_name):
if scheme.type == 'kem': if scheme.type == 'kem':
specification = itertools.chain(specification, KEM_FIELDS.items()) specification = itertools.chain(specification, KEM_FIELDS.items())
elif scheme.type == 'sign': elif scheme.type == 'sign':
specification = itertools.chain(specification, SIGNATURE_FIELDS.items()) specification = itertools.chain(specification,
SIGNATURE_FIELDS.items())
else: else:
assert(False) assert(False)
check_spec(copy.deepcopy(metadata), specification) check_spec(copy.deepcopy(metadata), specification)
implementation_names_in_yaml = set(i['name'] for i in metadata['implementations']) implementation_names_in_yaml = set(
i['name'] for i in metadata['implementations'])
implementations_on_disk = set(i.name for i in scheme.implementations) implementations_on_disk = set(i.name for i in scheme.implementations)
if implementation_names_in_yaml != implementations_on_disk: if implementation_names_in_yaml != implementations_on_disk:
raise AssertionError("Implementations in YAML file {} and implementations on disk {} do not match" raise AssertionError("Implementations in YAML file {} and "
.format(implementation_names_in_yaml, implementations_on_disk)) "implementations on disk {} do not match"
.format(implementation_names_in_yaml,
implementations_on_disk))
EXPECTED_FIELDS = { EXPECTED_FIELDS = {
'name': {'type': str}, 'name': {'type': str},
@ -62,6 +66,7 @@ SIGNATURE_FIELDS = {
'length-signature': {'type': int, 'min': 1}, 'length-signature': {'type': int, 'min': 1},
} }
def check_spec(metadata, spec): def check_spec(metadata, spec):
for field, props in spec: for field, props in spec:
if field not in metadata: if field not in metadata:
@ -75,7 +80,8 @@ def check_spec(metadata, spec):
# Done checking all specified fields, check if we have extras # Done checking all specified fields, check if we have extras
for field, value in metadata.items(): for field, value in metadata.items():
raise AssertionError("Unexpected item '{}' with value '{}'".format(field, value)) raise AssertionError(
"Unexpected item '{}' with value '{}'".format(field, value))
def check_element(field, element, props): def check_element(field, element, props):
@ -100,7 +106,8 @@ def check_element(field, element, props):
if 'max' in props: if 'max' in props:
if element > props['max']: if element > props['max']:
raise ValueError("Value of field '{}' is larger than maximum" raise ValueError("Value of field '{}' is larger than maximum"
" value {}".format(field, metafile, props['max'])) " value {}"
.format(field, props['max']))
if type_ == str: if type_ == str:
if 'length' in props: if 'length' in props:

View File

@ -1,21 +1,25 @@
""" """
Checks that the all exported symbols are properly namespaced, i.e., all start with "PQCLEAN_SCHEMENAME_". Checks that the all exported symbols are properly namespaced, i.e., all
start with "PQCLEAN_SCHEMENAME_".
""" """
import os
import pqclean import pqclean
import helpers import helpers
import sys import sys
import unittest import unittest
def test_symbol_namespace(): def test_symbol_namespace():
if sys.platform not in ['linux', 'darwin']: raise unittest.SkipTest() if sys.platform not in ['linux', 'darwin']:
raise unittest.SkipTest()
for scheme in pqclean.Scheme.all_schemes(): for scheme in pqclean.Scheme.all_schemes():
for implementation in scheme.implementations: for implementation in scheme.implementations:
yield check_symbol_namespace, scheme.name, implementation.name yield check_symbol_namespace, scheme.name, implementation.name
def check_symbol_namespace(scheme_name, implementation_name): def check_symbol_namespace(scheme_name, implementation_name):
implementation = pqclean.Implementation.by_name(scheme_name, implementation_name) implementation = pqclean.Implementation.by_name(
scheme_name, implementation_name)
helpers.run_subprocess( helpers.run_subprocess(
['make'], ['make'],
implementation.path() implementation.path()
@ -36,7 +40,8 @@ def check_symbol_namespace(scheme_name, implementation_name):
for symbolstr in symbols: for symbolstr in symbols:
*_, symtype, symbol = symbolstr.split() *_, symtype, symbol = symbolstr.split()
if symtype in 'TR': if symtype in 'TR':
if not symbol.startswith(namespace) and not symbol.startswith('_' + namespace): if (not symbol.startswith(namespace) and
not symbol.startswith('_' + namespace)):
non_namespaced.append(symbol) non_namespaced.append(symbol)
if non_namespaced: if non_namespaced: