* Add support for specifying architecture and feature limits * cpuinfo not supported on ppc * Detect 32-bit python interpreter on 64-bit CPU * Fix bugs in isolated tests * Also support restricting operating systemmaster
@@ -2,3 +2,4 @@ PyYAML | |||||
pytest | pytest | ||||
pytest-xdist | pytest-xdist | ||||
pycparser | pycparser | ||||
py-cpuinfo |
@@ -69,7 +69,7 @@ $(DEST_DIR)/testvectors_$(SCHEME)_$(IMPLEMENTATION): build-scheme crypto_$(TYPE) | |||||
mkdir -p $(DEST_DIR) | mkdir -p $(DEST_DIR) | ||||
$(CC) $(CFLAGS) -DPQCLEAN_NAMESPACE=PQCLEAN_$(SCHEME_UPPERCASE)_$(IMPLEMENTATION_UPPERCASE) -I$(SCHEME_DIR) crypto_$(TYPE)/testvectors.c $(COMMON_FILES) $(TEST_COMMON_DIR)/notrandombytes.c -o $@ -L$(SCHEME_DIR) -l$(SCHEME)_$(IMPLEMENTATION) | $(CC) $(CFLAGS) -DPQCLEAN_NAMESPACE=PQCLEAN_$(SCHEME_UPPERCASE)_$(IMPLEMENTATION_UPPERCASE) -I$(SCHEME_DIR) crypto_$(TYPE)/testvectors.c $(COMMON_FILES) $(TEST_COMMON_DIR)/notrandombytes.c -o $@ -L$(SCHEME_DIR) -l$(SCHEME)_$(IMPLEMENTATION) | ||||
$(DEST_DIR)/printparams_$(SCHEME)_$(IMPLEMENTATION): build-scheme crypto_$(TYPE)/printparams.c | |||||
$(DEST_DIR)/printparams_$(SCHEME)_$(IMPLEMENTATION): crypto_$(TYPE)/printparams.c | |||||
mkdir -p $(DEST_DIR) | mkdir -p $(DEST_DIR) | ||||
$(CC) $(CFLAGS) -DPQCLEAN_NAMESPACE=PQCLEAN_$(SCHEME_UPPERCASE)_$(IMPLEMENTATION_UPPERCASE) -I$(SCHEME_DIR) crypto_$(TYPE)/printparams.c -o $@ | $(CC) $(CFLAGS) -DPQCLEAN_NAMESPACE=PQCLEAN_$(SCHEME_UPPERCASE)_$(IMPLEMENTATION_UPPERCASE) -I$(SCHEME_DIR) crypto_$(TYPE)/printparams.c -o $@ | ||||
@@ -1,7 +1,9 @@ | |||||
import glob | import glob | ||||
import os | import os | ||||
from typing import Optional | |||||
import yaml | import yaml | ||||
import platform | |||||
class Scheme: | class Scheme: | ||||
@@ -37,6 +39,11 @@ class Scheme: | |||||
implementations.extend(scheme.implementations) | implementations.extend(scheme.implementations) | ||||
return implementations | return implementations | ||||
@staticmethod | |||||
def all_supported_implementations(): | |||||
return [impl for impl in Scheme.all_implementations() | |||||
if impl.supported_on_current_platform()] | |||||
@staticmethod | @staticmethod | ||||
def all_schemes_of_type(type: str) -> list: | def all_schemes_of_type(type: str) -> list: | ||||
schemes = [] | schemes = [] | ||||
@@ -111,10 +118,57 @@ class Implementation: | |||||
implementations.append(Implementation(scheme, d)) | implementations.append(Implementation(scheme, d)) | ||||
return implementations | return implementations | ||||
@staticmethod | |||||
def all_supported_implementations(scheme: Scheme) -> list: | |||||
return [impl for impl in Implementation.all_implementations(scheme) | |||||
if impl.supported_on_current_platform()] | |||||
def namespace_prefix(self): | def namespace_prefix(self): | ||||
return '{}{}_'.format(self.scheme.namespace_prefix(), | return '{}{}_'.format(self.scheme.namespace_prefix(), | ||||
self.name.upper()).replace('-', '') | self.name.upper()).replace('-', '') | ||||
def supported_on_os(self, os: Optional[str] = None) -> bool: | |||||
"""Check if we support the OS | |||||
If no OS is specified, then we run on the current OS | |||||
""" | |||||
if os is None: | |||||
os = platform.system() | |||||
for platform_ in self.metadata().get('supported_platforms', []): | |||||
if 'operating_systems' in platform_: | |||||
if os not in platform_['operating_systems']: | |||||
return False | |||||
return True | |||||
def supported_on_current_platform(self) -> bool: | |||||
if 'supported_platforms' not in self.metadata(): | |||||
return True | |||||
if platform.machine() == 'ppc': | |||||
return False | |||||
if not self.supported_on_os(): | |||||
return False | |||||
if not hasattr(Implementation, 'CPUINFO'): | |||||
import cpuinfo | |||||
Implementation.CPUINFO = cpuinfo.get_cpu_info() | |||||
CPUINFO = Implementation.CPUINFO | |||||
for platform_ in self.metadata()['supported_platforms']: | |||||
if platform_['architecture'] == CPUINFO['arch'].lower(): | |||||
# Detect actually running on emulated i386 | |||||
if (platform_['architecture'] == 'x86_64' and | |||||
platform.architecture()[0] == '32bit'): | |||||
continue | |||||
if all([flag in CPUINFO['flags'] | |||||
for flag in platform_['required_flags']]): | |||||
return True | |||||
return False | |||||
def __str__(self): | def __str__(self): | ||||
return "{} implementation of {}".format(self.name, self.scheme.name) | return "{} implementation of {}".format(self.name, self.scheme.name) | ||||
@@ -11,8 +11,8 @@ import pqclean | |||||
@pytest.mark.parametrize( | @pytest.mark.parametrize( | ||||
'implementation,test_dir,impl_dir, init, destr', | 'implementation,test_dir,impl_dir, init, destr', | ||||
[(impl, *helpers.isolate_test_files(impl.path(), 'test_functest_')) | [(impl, *helpers.isolate_test_files(impl.path(), 'test_functest_')) | ||||
for impl in pqclean.Scheme.all_implementations()], | |||||
ids=[str(impl) for impl in pqclean.Scheme.all_implementations()], | |||||
for impl in pqclean.Scheme.all_supported_implementations()], | |||||
ids=[str(impl) for impl in pqclean.Scheme.all_supported_implementations()], | |||||
) | ) | ||||
@helpers.filtered_test | @helpers.filtered_test | ||||
def test_compile_lib(implementation, test_dir, impl_dir, init, destr): | def test_compile_lib(implementation, test_dir, impl_dir, init, destr): | ||||
@@ -11,8 +11,8 @@ import pqclean | |||||
@pytest.mark.parametrize( | @pytest.mark.parametrize( | ||||
'implementation,test_dir,impl_path, init, destr', | 'implementation,test_dir,impl_path, init, destr', | ||||
[(impl, *helpers.isolate_test_files(impl.path(), 'test_functest_')) | [(impl, *helpers.isolate_test_files(impl.path(), 'test_functest_')) | ||||
for impl in pqclean.Scheme.all_implementations()], | |||||
ids=[str(impl) for impl in pqclean.Scheme.all_implementations()], | |||||
for impl in pqclean.Scheme.all_supported_implementations()], | |||||
ids=[str(impl) for impl in pqclean.Scheme.all_supported_implementations()], | |||||
) | ) | ||||
@helpers.skip_windows() | @helpers.skip_windows() | ||||
@helpers.filtered_test | @helpers.filtered_test | ||||
@@ -16,8 +16,8 @@ import pqclean | |||||
@pytest.mark.parametrize( | @pytest.mark.parametrize( | ||||
'implementation,test_dir,impl_path, init, destr', | 'implementation,test_dir,impl_path, init, destr', | ||||
[(impl, *helpers.isolate_test_files(impl.path(), 'test_functest_')) | [(impl, *helpers.isolate_test_files(impl.path(), 'test_functest_')) | ||||
for impl in pqclean.Scheme.all_implementations()], | |||||
ids=[str(impl) for impl in pqclean.Scheme.all_implementations()], | |||||
for impl in pqclean.Scheme.all_supported_implementations()], | |||||
ids=[str(impl) for impl in pqclean.Scheme.all_supported_implementations()], | |||||
) | ) | ||||
@helpers.filtered_test | @helpers.filtered_test | ||||
def test_functest(implementation, impl_path, test_dir, | def test_functest(implementation, impl_path, test_dir, | ||||
@@ -45,8 +45,8 @@ def test_functest(implementation, impl_path, test_dir, | |||||
'implementation,test_dir,impl_path, init, destr', | 'implementation,test_dir,impl_path, init, destr', | ||||
[(impl, | [(impl, | ||||
*helpers.isolate_test_files(impl.path(), 'test_functest_sanitizers_')) | *helpers.isolate_test_files(impl.path(), 'test_functest_sanitizers_')) | ||||
for impl in pqclean.Scheme.all_implementations()], | |||||
ids=[str(impl) for impl in pqclean.Scheme.all_implementations()], | |||||
for impl in pqclean.Scheme.all_supported_implementations()], | |||||
ids=[str(impl) for impl in pqclean.Scheme.all_supported_implementations()], | |||||
) | ) | ||||
@helpers.skip_windows() | @helpers.skip_windows() | ||||
@helpers.filtered_test | @helpers.filtered_test | ||||
@@ -13,7 +13,7 @@ additional_flags = [] #['-fix-errors'] | |||||
@pytest.mark.parametrize( | @pytest.mark.parametrize( | ||||
'implementation', | 'implementation', | ||||
pqclean.Scheme.all_implementations(), | |||||
pqclean.Scheme.all_supported_implementations(), | |||||
ids=str, | ids=str, | ||||
) | ) | ||||
@helpers.skip_windows() | @helpers.skip_windows() | ||||
@@ -17,8 +17,8 @@ import pqclean | |||||
'implementation,test_dir,impl_path, init, destr', | 'implementation,test_dir,impl_path, init, destr', | ||||
[(impl, | [(impl, | ||||
*helpers.isolate_test_files(impl.path(), 'test_makefile_deps_')) | *helpers.isolate_test_files(impl.path(), 'test_makefile_deps_')) | ||||
for impl in pqclean.Scheme.all_implementations()], | |||||
ids=[str(impl) for impl in pqclean.Scheme.all_implementations()], | |||||
for impl in pqclean.Scheme.all_supported_implementations()], | |||||
ids=[str(impl) for impl in pqclean.Scheme.all_supported_implementations()], | |||||
) | ) | ||||
@helpers.filtered_test | @helpers.filtered_test | ||||
def test_makefile_dependencies(implementation, impl_path, test_dir, | def test_makefile_dependencies(implementation, impl_path, test_dir, | ||||
@@ -17,10 +17,23 @@ import pqclean | |||||
ids=str, | ids=str, | ||||
) | ) | ||||
@helpers.filtered_test | @helpers.filtered_test | ||||
def test_makefiles_present(implementation): | |||||
def test_makefile_present(implementation): | |||||
p1 = os.path.join(implementation.path(), 'Makefile') | p1 = os.path.join(implementation.path(), 'Makefile') | ||||
assert os.path.isfile(p1) | |||||
@pytest.mark.parametrize( | |||||
'implementation', | |||||
pqclean.Scheme.all_implementations(), | |||||
ids=str, | |||||
) | |||||
@helpers.filtered_test | |||||
def test_microsoft_nmakefile_present(implementation): | |||||
p2 = os.path.join(implementation.path(), 'Makefile.Microsoft_nmake') | p2 = os.path.join(implementation.path(), 'Makefile.Microsoft_nmake') | ||||
assert(os.path.isfile(p1) and os.path.isfile(p2)) | |||||
if implementation.supported_on_os(os='Windows'): | |||||
assert os.path.isfile(p2) | |||||
else: | |||||
assert not os.path.isfile(p2), "Should not have an NMake file" | |||||
if __name__ == '__main__': | if __name__ == '__main__': | ||||
@@ -61,6 +61,31 @@ EXPECTED_FIELDS = { | |||||
'spec': { | 'spec': { | ||||
'name': {'type': str}, | 'name': {'type': str}, | ||||
'version': {'type': str}, | 'version': {'type': str}, | ||||
'supported_platforms': { | |||||
'type': list, | |||||
'optional': True, | |||||
'elements': { | |||||
'type': dict, | |||||
'spec': { | |||||
'architecture': { | |||||
'type': str, | |||||
'values': ['x86', 'x86_64', 'aarch64']}, | |||||
'required_flags': { | |||||
'type': list, | |||||
'optional': True, | |||||
'elements': {'type': str}, | |||||
}, | |||||
'operating_systems': { | |||||
'type': list, | |||||
'optional': True, | |||||
'elements': { | |||||
'type': str, | |||||
'values': ['Linux', 'Windows', 'Darwin'], | |||||
}, | |||||
}, | |||||
}, | |||||
}, | |||||
}, | |||||
}, | }, | ||||
}, | }, | ||||
}, | }, | ||||
@@ -9,7 +9,7 @@ import pqclean | |||||
@pytest.mark.parametrize( | @pytest.mark.parametrize( | ||||
'implementation,test_dir,impl_path, init, destr', | 'implementation,test_dir,impl_path, init, destr', | ||||
[(impl, *helpers.isolate_test_files(impl.path(), 'test_functest_')) | |||||
[(impl, *helpers.isolate_test_files(impl.path(), 'test_printparams_')) | |||||
for impl in pqclean.Scheme.all_implementations()], | for impl in pqclean.Scheme.all_implementations()], | ||||
ids=[str(impl) for impl in pqclean.Scheme.all_implementations()], | ids=[str(impl) for impl in pqclean.Scheme.all_implementations()], | ||||
) | ) | ||||
@@ -20,8 +20,8 @@ import pqclean | |||||
@pytest.mark.parametrize( | @pytest.mark.parametrize( | ||||
'implementation,test_dir,impl_path, init, destr', | 'implementation,test_dir,impl_path, init, destr', | ||||
[(impl, *helpers.isolate_test_files(impl.path(), 'test_functest_')) | [(impl, *helpers.isolate_test_files(impl.path(), 'test_functest_')) | ||||
for impl in pqclean.Scheme.all_implementations()], | |||||
ids=[str(impl) for impl in pqclean.Scheme.all_implementations()], | |||||
for impl in pqclean.Scheme.all_supported_implementations()], | |||||
ids=[str(impl) for impl in pqclean.Scheme.all_supported_implementations()], | |||||
) | ) | ||||
@helpers.filtered_test | @helpers.filtered_test | ||||
def test_nistkat(implementation, impl_path, test_dir, init, destr): | def test_nistkat(implementation, impl_path, test_dir, init, destr): | ||||
@@ -16,8 +16,8 @@ import pqclean | |||||
'implementation,test_dir,impl_path,init,destr', | 'implementation,test_dir,impl_path,init,destr', | ||||
[(impl, | [(impl, | ||||
*helpers.isolate_test_files(impl.path(), 'test_symbol_ns_')) | *helpers.isolate_test_files(impl.path(), 'test_symbol_ns_')) | ||||
for impl in pqclean.Scheme.all_implementations()], | |||||
ids=[str(impl) for impl in pqclean.Scheme.all_implementations()], | |||||
for impl in pqclean.Scheme.all_supported_implementations()], | |||||
ids=[str(impl) for impl in pqclean.Scheme.all_supported_implementations()], | |||||
) | ) | ||||
@helpers.filtered_test | @helpers.filtered_test | ||||
def test_symbol_namespaces(implementation, impl_path, test_dir, init, destr): | def test_symbol_namespaces(implementation, impl_path, test_dir, init, destr): | ||||
@@ -5,6 +5,7 @@ the one provided in the META file for every scheme/implementation. | |||||
import hashlib | import hashlib | ||||
import os | import os | ||||
import unittest | |||||
import pytest | import pytest | ||||
@@ -22,6 +23,8 @@ import pqclean | |||||
) | ) | ||||
@helpers.filtered_test | @helpers.filtered_test | ||||
def test_testvectors(implementation, impl_path, test_dir, init, destr): | def test_testvectors(implementation, impl_path, test_dir, init, destr): | ||||
if not implementation.supported_on_current_platform(): | |||||
raise unittest.SkipTest("Not supported on current platform") | |||||
init() | init() | ||||
dest_dir = os.path.join(test_dir, 'bin') | dest_dir = os.path.join(test_dir, 'bin') | ||||
helpers.make('testvectors', | helpers.make('testvectors', | ||||
@@ -27,8 +27,8 @@ def valgrind_supports_exit_early(): | |||||
@pytest.mark.parametrize( | @pytest.mark.parametrize( | ||||
'implementation,test_dir,impl_path, init, destr', | 'implementation,test_dir,impl_path, init, destr', | ||||
[(impl, *helpers.isolate_test_files(impl.path(), 'test_functest_')) | [(impl, *helpers.isolate_test_files(impl.path(), 'test_functest_')) | ||||
for impl in pqclean.Scheme.all_implementations()], | |||||
ids=[str(impl) for impl in pqclean.Scheme.all_implementations()], | |||||
for impl in pqclean.Scheme.all_supported_implementations()], | |||||
ids=[str(impl) for impl in pqclean.Scheme.all_supported_implementations()], | |||||
) | ) | ||||
@helpers.slow_test | @helpers.slow_test | ||||
@helpers.filtered_test | @helpers.filtered_test | ||||