diff options
Diffstat (limited to 'testing')
-rw-r--r-- | testing/runner.py | 34 | ||||
-rw-r--r-- | testing/testing.pyqtc | 9 | ||||
-rw-r--r-- | testing/wheel_tester.py | 295 |
3 files changed, 327 insertions, 11 deletions
diff --git a/testing/runner.py b/testing/runner.py index 3c99df71c..baa29408c 100644 --- a/testing/runner.py +++ b/testing/runner.py @@ -88,21 +88,14 @@ class TestRunner(object): os.environ['PATH'] = clang_bin_dir + os.pathsep + path print("Adding %s as detected by %s to PATH" % (clang_bin_dir, clang_dir[1])) - def _find_ctest(self): + def _find_ctest_in_file(self, file_name): """ - Find ctest in the Makefile - - We no longer use make, but the ctest command directly. - It is convenient to look for the ctest program using the Makefile. - This serves us two purposes: - - - there is no dependency of the PATH variable, - - each project is checked whether ctest was configured. + Helper for _find_ctest() that finds the ctest binary in a build + system file (ninja, Makefile). """ - make_path = os.path.join(self.test_dir, "Makefile") look_for = "--force-new-ctest-process" line = None - with open(make_path) as makefile: + with open(file_name) as makefile: for line in makefile: if look_for in line: break @@ -121,6 +114,25 @@ class TestRunner(object): ctest = re.search(r'(\S+|"([^"]+)")\s+' + look_for, line).groups() return ctest[1] or ctest[0] + def _find_ctest(self): + """ + Find ctest in a build system file (ninja, Makefile) + + We no longer use make, but the ctest command directly. + It is convenient to look for the ctest program using the Makefile. + This serves us two purposes: + + - there is no dependency of the PATH variable, + - each project is checked whether ctest was configured. + """ + candidate_files = ["Makefile", "build.ninja"] + for candidate in candidate_files: + path = os.path.join(self.test_dir, candidate) + if os.path.exists(path): + return self._find_ctest_in_file(path) + raise RuntimeError('Cannot find any of the build system files {}.'.format( + ', '.join(candidate_files))) + def _setup(self): self.ctestCommand = self._find_ctest() diff --git a/testing/testing.pyqtc b/testing/testing.pyqtc new file mode 100644 index 000000000..5a89e69b8 --- /dev/null +++ b/testing/testing.pyqtc @@ -0,0 +1,9 @@ +../testrunner.py +blacklist.py +buildlog.py +command.py +helper.py +__init__.py +parser.py +runner.py +wheel_tester.py diff --git a/testing/wheel_tester.py b/testing/wheel_tester.py new file mode 100644 index 000000000..60fd7a38a --- /dev/null +++ b/testing/wheel_tester.py @@ -0,0 +1,295 @@ +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of Qt for Python. +## +## $QT_BEGIN_LICENSE:LGPL$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU Lesser General Public License Usage +## Alternatively, this file may be used under the terms of the GNU Lesser +## General Public License version 3 as published by the Free Software +## Foundation and appearing in the file LICENSE.LGPL3 included in the +## packaging of this file. Please review the following information to +## ensure the GNU Lesser General Public License version 3 requirements +## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 2.0 or (at your option) the GNU General +## Public license version 3 or any later version approved by the KDE Free +## Qt Foundation. The licenses are as published by the Free Software +## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-2.0.html and +## https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +""" +This script is used by Coin (coin_test_instructions.py specifically) to +test installation of generated wheels, and test building of the +"buildable" examples samplebinding and scriptableapplication. + +It can also be invoked regularly from the command line via +python testing/wheel_tester.py --qmake=some-value --cmake=some-value + +The qmake and cmake arguments can also be omitted, and they will be +looked up in your PATH. + +Make sure that some generated wheels already exist in the dist/ +directory (e.g. setup.py bdist_wheel was already executed). +""" + +import os, sys + +try: + this_file = __file__ +except NameError: + this_file = sys.argv[0] +this_file = os.path.abspath(this_file) +this_dir = os.path.dirname(this_file) +setup_script_dir = os.path.abspath(os.path.join(this_dir, '..')) +sys.path.append(setup_script_dir) + +from build_scripts.options import OPTION_QMAKE +from build_scripts.options import OPTION_CMAKE + +from build_scripts.utils import find_files_using_glob +from build_scripts.utils import find_glob_in_path +from build_scripts.utils import run_process +from build_scripts.utils import rmtree +import distutils.log as log + +log.set_verbosity(1) + + +def find_executable_qmake(): + return find_executable('qmake', OPTION_QMAKE) + + +def find_executable_cmake(): + return find_executable('cmake', OPTION_CMAKE) + + +def find_executable(executable, command_line_value): + value = command_line_value + option_str = '--{}'.format(executable) + + if value: + log.info("{} option given: {}".format(option_str, value)) + if not os.path.exists(value): + raise RuntimeError("No executable exists at: {}".format(value)) + else: + log.info("No {} option given, trying to find {} in PATH.".format(option_str, executable)) + paths = find_glob_in_path(executable) + log.info("{} executables found in PATH: {}".format(executable, paths)) + if not paths: + raise RuntimeError( + "No {} option was specified and no {} was found " + "in PATH.".format(option_str, executable)) + else: + value = paths[0] + log.info("Using {} found in PATH: {}".format(executable, value)) + log.info("") + return value + + +QMAKE_PATH = find_executable_qmake() +CMAKE_PATH = find_executable_cmake() + + +def get_wheels_dir(): + return os.path.join(setup_script_dir, "dist") + + +def get_examples_dir(): + return os.path.join(setup_script_dir, "examples") + + +def package_prefix_names(): + return ["shiboken2", "shiboken2_generator", "PySide2"] + + +def clean_egg_info(): + # After a successful bdist_wheel build, some .egg-info directories + # are left over, which confuse pip when invoking it via + # python -m pip, making pip think that the packages are already + # installed in the root source directory. + # Clean up the .egg-info directories to fix this, it should be + # safe to do so. + paths = find_files_using_glob(setup_script_dir, "*.egg-info") + for p in paths: + log.info("Removing {}".format(p)) + rmtree(p) + + +def install_wheel(wheel_path): + log.info("Installing wheel: {}".format(wheel_path)) + exit_code = run_process([sys.executable, "-m", "pip", "install", wheel_path]) + log.info("") + if exit_code: + raise RuntimeError("Error while installing wheel {}".format(wheel_path)) + + +def try_install_wheels(wheels_dir, py_version): + clean_egg_info() + all_wheels_pattern = "*.whl" + all_wheels = find_files_using_glob(wheels_dir, all_wheels_pattern) + + if len(all_wheels) > 1: + log.info("Found the following wheels in {}: ".format(wheels_dir)) + for wheel in all_wheels: + log.info(wheel) + else: + log.info("No wheels found in {}".format(wheels_dir)) + log.info("") + + for p in package_prefix_names(): + pattern = "{}-*cp{}*.whl".format(p, py_version) + files = find_files_using_glob(wheels_dir, pattern) + if files and len(files) == 1: + wheel_path = files[0] + install_wheel(wheel_path) + elif len(files) > 1: + raise RuntimeError("More than one wheel found for specific package and version.") + else: + raise RuntimeError("No wheels compatible with Python {} found " + "for testing.".format(py_version)) + + +def is_unix(): + if sys.platform.startswith("linux") or sys.platform == "darwin": + return True + return False + + +def generate_build_cmake(): + args = [CMAKE_PATH] + if is_unix(): + args.extend(["-G", "Unix Makefiles"]) + else: + args.extend(["-G", "NMake Makefiles"]) + args.append("-DCMAKE_BUILD_TYPE=Release") + args.append("-Dpython_interpreter={}".format(sys.executable)) + + # Specify prefix path so find_package(Qt5) works. + qmake_dir = os.path.abspath(os.path.join(os.path.dirname(QMAKE_PATH), "..")) + args.append("-DCMAKE_PREFIX_PATH={}".format(qmake_dir)) + + args.append("..") + + exit_code = run_process(args) + if exit_code: + raise RuntimeError("Failure while running cmake.") + log.info("") + + +def generate_build_qmake(): + exit_code = run_process([QMAKE_PATH, "..", "python_interpreter={}".format(sys.executable)]) + if exit_code: + raise RuntimeError("Failure while running qmake.") + log.info("") + + +def run_make(): + args = [] + if is_unix(): + executable = "make" + else: + executable = "nmake" + args.append(executable) + + exit_code = run_process(args) + if exit_code: + raise RuntimeError("Failure while running {}.".format(executable)) + log.info("") + + +def run_make_install(): + args = [] + if is_unix(): + executable = "make" + else: + executable = "nmake" + args.append(executable) + args.append("install") + + exit_code = run_process(args) + if exit_code: + raise RuntimeError("Failed while running {} install.".format(executable)) + log.info("") + + +def execute_script(script_path): + args = [sys.executable, script_path] + exit_code = run_process(args) + if exit_code: + raise RuntimeError("Failure while executing script: {}".format(script_path)) + log.info("") + + +def prepare_build_folder(src_path, build_folder_name): + build_path = os.path.join(src_path, build_folder_name) + + # The script can be called for both Python 2 and Python 3 wheels, so + # preparing a build folder should clean any previous existing build. + if os.path.exists(build_path): + log.info("Removing {}".format(build_path)) + rmtree(build_path) + + log.info("Creating {}".format(build_path)) + os.makedirs(build_path) + os.chdir(build_path) + + +def try_build_examples(): + examples_dir = get_examples_dir() + + log.info("Attempting to build and run samplebinding using cmake.") + src_path = os.path.join(examples_dir, "samplebinding") + prepare_build_folder(src_path, "cmake") + generate_build_cmake() + run_make() + run_make_install() + execute_script(os.path.join(src_path, "main.py")) + + log.info("Attempting to build scriptableapplication using cmake.") + src_path = os.path.join(examples_dir, "scriptableapplication") + prepare_build_folder(src_path, "cmake") + generate_build_cmake() + run_make() + + log.info("Attempting to build scriptableapplication using qmake.") + src_path = os.path.join(examples_dir, "scriptableapplication") + prepare_build_folder(src_path, "qmake") + generate_build_qmake() + run_make() + + +def run_wheel_tests(): + wheels_dir = get_wheels_dir() + py_version = sys.version_info[0] + + log.info("Attempting to install wheels.\n") + try_install_wheels(wheels_dir, py_version) + + log.info("Attempting to build examples.\n") + try_build_examples() + + log.info("All tests passed!") + + +if __name__ == "__main__": + run_wheel_tests() |