summaryrefslogtreecommitdiff
path: root/src/buildstream/testing/__init__.py
blob: 3474ded829f2e25310488ccf705cd97a384b1085 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#
#  Copyright (C) 2019 Codethink Limited
#  Copyright (C) 2019 Bloomberg Finance LP
#
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU Lesser General Public
#  License as published by the Free Software Foundation; either
#  version 2 of the License, or (at your option) any later version.
#
#  This library is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#  Lesser General Public License for more details.
#
#  You should have received a copy of the GNU Lesser General Public
#  License along with this library. If not, see <http://www.gnu.org/licenses/>.

"""
This package contains various utilities which make it easier to test plugins.
"""

import os
from collections import OrderedDict
from buildstream.exceptions import ErrorDomain, LoadErrorReason
from ._yaml import generate_project, generate_element, load_yaml
from .repo import Repo
from .runcli import cli, cli_integration, cli_remote_execution, Cli
from .integration import integration_cache
from ._cachekeys import check_cache_key_stability

__all__ = [
    "check_cache_key_stability",
    "create_repo",
    "register_repo_kind",
    "sourcetests_collection_hook",
]

# To make use of these test utilities it is necessary to have pytest
# available. However, we don't want to have a hard dependency on
# pytest.
try:
    import pytest
except ImportError:
    module_name = globals()["__name__"]
    msg = "Could not import pytest:\n" "To use the {} module, you must have pytest installed.".format(module_name)
    raise ImportError(msg)


# Of the form plugin_name -> (repo_class, plugin_package)
ALL_REPO_KINDS = OrderedDict()  # type: OrderedDict[Repo, str]


def create_repo(kind, directory, subdir="repo"):
    """Convenience method for creating a Repo

    Args:
        kind (str): The kind of repo to create (a source plugin basename). This
                    must have previously been registered using
                    `register_repo_kind`
        directory (str): The path where the repo will keep a cache

    Returns:
        (Repo): A new Repo object
    """
    try:
        constructor = ALL_REPO_KINDS[kind]
    except KeyError as e:
        raise AssertionError("Unsupported repo kind {}".format(kind)) from e

    return constructor[0](directory, subdir=subdir)


def register_repo_kind(kind, cls, plugin_package):
    """Register a new repo kind.

    Registering a repo kind will allow the use of the `create_repo`
    method for that kind and include that repo kind in ALL_REPO_KINDS

    In addition, repo_kinds registred prior to
    `sourcetests_collection_hook` being called will be automatically
    used to test the basic behaviour of their associated source
    plugins using the tests in `testing._sourcetests`.

    Args:
       kind (str): The kind of repo to create (a source plugin basename)
       cls (cls) : A class derived from Repo.
       plugin_package (str): The name of the python package containing the plugin

    """
    ALL_REPO_KINDS[kind] = (cls, plugin_package)


def sourcetests_collection_hook(session):
    """ Used to hook the templated source plugin tests into a pyest test suite.

    This should be called via the `pytest_sessionstart
    hook <https://docs.pytest.org/en/latest/reference.html#collection-hooks>`_.
    The tests in the _sourcetests package will be collected as part of
    whichever test package this hook is called from.

    Args:
        session (pytest.Session): The current pytest session
    """

    def should_collect_tests(config):
        args = config.args
        rootdir = config.rootdir
        # When no args are supplied, pytest defaults the arg list to
        # just include the session's root_dir. We want to collect
        # tests as part of the default collection
        if args == [str(rootdir)]:
            return True

        # If specific tests are passed, don't collect
        # everything. Pytest will handle this correctly without
        # modification.
        if len(args) > 1 or rootdir not in args:
            return False

        # If in doubt, collect them, this will be an easier bug to
        # spot and is less likely to result in bug not being found.
        return True

    from . import _sourcetests

    source_test_path = os.path.dirname(_sourcetests.__file__)
    # Add the location of the source tests to the session's
    # python_files config. Without this, pytest may filter out these
    # tests during collection.
    session.config.addinivalue_line("python_files", os.path.join(source_test_path, "*.py"))
    # If test invocation has specified specic tests, don't
    # automatically collect templated tests.
    if should_collect_tests(session.config):
        session.config.args.append(source_test_path)