diff --git a/.circleci/config.yml b/.circleci/config.yml index 17f13e49..7db6185c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -4,6 +4,11 @@ version: 2 machine: true steps: - checkout + - run: + name: Pull submodules + command: | + git submodule init + git submodule update - run: name: Install the emulation handlers command: docker run --rm --privileged multiarch/qemu-user-static:register --reset @@ -13,6 +18,7 @@ version: 2 docker run -e CI=true --rm -v `pwd`:`pwd` -w `pwd` "pqclean/ci-container:$ARCH" /bin/bash -c " uname -a && export CC=${CC} && + pip3 install -r requirements.txt && cd test && python3 -m nose --rednose --verbose" .native_job: &nativejob @@ -20,10 +26,16 @@ version: 2 - image: pqclean/ci-container:$ARCH steps: - checkout + - run: + name: Pull submodules + command: | + git submodule init + git submodule update - run: name: Run tests command: | export CC=${CC} + pip3 install -r requirements.txt && cd test && python3 -m nose --rednose --verbose diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..b547037a --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "test/pycparser"] + path = test/pycparser + url = https://github.com/eliben/pycparser.git diff --git a/.travis.yml b/.travis.yml index 66a4893b..6d7d3780 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,6 +23,9 @@ matrix: - gcc@8 before_install: - pip3 install -r requirements.txt + - brew link gcc + - export PATH="/usr/local/bin:$PATH" + - ln -s /usr/local/bin/gcc-8 /usr/local/bin/gcc - gcc --version script: - "cd test && python3 -m nose --rednose --verbose" diff --git a/README.md b/README.md index 6936d629..c055e9f4 100644 --- a/README.md +++ b/README.md @@ -139,10 +139,16 @@ To do this, make sure the following is installed: * Python 3.5+ * `nosetests` or `nose2` (either for Python 3) +You will also need to make sure the submodules are initialized by running: + +``` +git submodule update --init +``` + Run the Python-based tests by going into the `test` directory and running `nosetests -v` or `nose2 -B -v`, depending on what you installed. If you have the `rednose` plugin for `nosetests` installed, run `nosetests --rednose` to get colored output. -You may also run `python ` where `` is any of the files starting with `test_` in the `test/` folder. +You may also run `python3 ` where `` is any of the files starting with `test_` in the `test/` folder. [circleci-pqc]: https://circleci.com/gh/PQClean/PQClean/ [travis-pqc]: https://travis-ci.com/PQClean/PQClean/ diff --git a/requirements.txt b/requirements.txt index 01b892fa..b3f4ea5d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ PyYAML nose rednose +pycparser diff --git a/test/helpers.py b/test/helpers.py index 238e234c..e6c3adc1 100644 --- a/test/helpers.py +++ b/test/helpers.py @@ -86,7 +86,6 @@ def ensure_available(executable): """ path = shutil.which(executable) if path: - print("Found", path) return path # Installing clang-tidy on LLVM will be too much of a mess. diff --git a/test/pycparser b/test/pycparser new file mode 160000 index 00000000..e1a1d737 --- /dev/null +++ b/test/pycparser @@ -0,0 +1 @@ +Subproject commit e1a1d737be66308b633215fa26ac5ed30e890103 diff --git a/test/test_char.py b/test/test_char.py new file mode 100644 index 00000000..85bb47ac --- /dev/null +++ b/test/test_char.py @@ -0,0 +1,67 @@ + +""" +Checks that the implementation does not make use of the `char` type. +This is ambiguous; compilers can freely choose `signed` or `unsigned` char. +""" + +import pqclean +import pycparser +import os +import helpers + + +def test_char(): + for scheme in pqclean.Scheme.all_schemes(): + for implementation in scheme.implementations: + yield check_char, implementation + + +def walk_tree(ast): + if type(ast) is pycparser.c_ast.IdentifierType: + if ast.names == ['char']: + yield ast + + for (_, child) in ast.children(): + yield from walk_tree(child) # recursively yield prohibited nodes + + +@helpers.skip_windows() +def check_char(implementation): + errors = [] + for fname in os.listdir(implementation.path()): + if not fname.endswith(".c"): + continue + tdir, _ = os.path.split(os.path.realpath(__file__)) + ast = pycparser.parse_file( + os.path.join(implementation.path(), fname), + use_cpp=True, + cpp_path='cc', # not all platforms link cpp correctly; cc -E works + cpp_args=[ + '-E', + '-std=c99', + '-nostdinc', # pycparser cannot deal with e.g. __attribute__ + '-I{}'.format(os.path.join(tdir, "../common")), + # necessary to mock e.g. + '-I{}'.format( + os.path.join(tdir, 'pycparser/utils/fake_libc_include')), + ] + ) + for node in walk_tree(ast): + # flatten nodes to a string to easily enforce uniqueness + err = "\n at {c.file}:{c.line}:{c.column}".format(c=node.coord) + if err not in errors: + errors.append(err) + if errors: + raise AssertionError( + "Prohibited use of char without explicit signed/unsigned" + + "".join(errors) + ) + + +if __name__ == '__main__': + try: + import nose2 + nose2.main() + except ImportError: + import nose + nose.runmodule()