/usr/lib/python3/dist-packages/plainbox/impl/integration_tests.py is in python3-plainbox 0.5.3-2.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
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 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 | # This file is part of Checkbox.
#
# Copyright 2012 Canonical Ltd.
# Written by:
# Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
#
# Checkbox is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3,
# as published by the Free Software Foundation.
#
# Checkbox 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
"""
:mod:`plainbox.impl.integration_tests` -- integration tests
===========================================================
.. warning::
THIS MODULE DOES NOT HAVE STABLE PUBLIC API
"""
from tempfile import TemporaryDirectory
import json
import os
import shutil
import tempfile
from pkg_resources import resource_filename, resource_isdir, resource_listdir
from plainbox.impl.box import main
from plainbox.testing_utils.cwd import TestCwd
from plainbox.testing_utils.io import TestIO
from plainbox.testing_utils.testcases import TestCaseWithParameters
from plainbox.testing_utils.resource import ResourceCache
from plainbox.vendor.mock import patch, Mock
class IntegrationTests(TestCaseWithParameters):
"""
Test cases for checking execution and outcome of checkbox jobs.
Each test case is parametrized by the job id and execution "profile".
The profile is simply a string that somehow characterizes where this test
is applicable.
"""
# XXX: we cannot use weak resource cache here because test parameters
# iterate over methods first and then over actual scenarios so our cache
# would constantly loose data. This might be fixable with a different
# implementation of test parameters but that's not a low hanging fruit.
cache = ResourceCache(weak=False)
parameter_names = ('scenario_pathname',)
@patch.dict('sys.modules', {'concurrent': Mock()})
def setUp(self):
# session data are kept in XDG_CACHE_HOME/plainbox/.session
# To avoid resuming a real session, we have to select a temporary
# location instead
self._sandbox = tempfile.mkdtemp()
self._env = os.environ
os.environ['XDG_CACHE_HOME'] = self._sandbox
# Load the expected results and keep them in memory
self.scenario_data = self.cache.get(
key=('scenario_data', self.parameters.scenario_pathname),
operation=lambda: load_scenario_data(
self.parameters.scenario_pathname))
# Skip tests that are not applicable for the current system
self.skip_if_incompatible()
# Execute the job and remember the results.
(self.job_id, self.job_outcome, self.job_execution_duration,
self.job_return_code, self.job_stdout,
self.job_stderr) = self.cache.get(
key=('job-run-artifacts', self.parameters.scenario_pathname),
operation=lambda: execute_job(self.scenario_data['job_name']))
def test_job_outcome(self):
# Check that results match expected values
self.assertEqual(self.job_outcome,
self.scenario_data['result']['result_map'] \
[self.job_id]['outcome'])
def test_job_return_code(self):
# Check the return code for correctness
self.assertEqual(self.job_return_code,
self.scenario_data.get("return_code", 0))
def skip_if_incompatible(self):
"""
Skip a job if it is incompatible with the current environment
"""
if self.scenario_data.get('profile') != 'default':
self.skipTest("not applicable for current profile")
@classmethod
def _discover_test_scenarios(cls, package='plainbox',
dirname="/test-data/integration-tests/",
extension=".json"):
"""
Discover test scenarios.
Generates special absolute pathnames to scenario files. All those paths
are really relative to the plainbox package. Those pathnames are
suitable for pkg_resources.resource_ functions.
All reference data should be dropped to
``plainbox/test-data/integration-tests/`` as a json file
"""
for name in resource_listdir(package, dirname):
resource_pathname = os.path.join(dirname, name)
if resource_isdir(package, resource_pathname):
for item in cls._discover_test_scenarios(package,
resource_pathname,
extension):
yield item
elif resource_pathname.endswith(extension):
yield resource_pathname
@classmethod
def get_parameter_values(cls):
"""
Implementation detail of TestCaseWithParameters
Creates subsequent tuples for each job that has reference data
"""
for scenario_pathname in cls._discover_test_scenarios():
yield (scenario_pathname,)
def tearDown(self):
shutil.rmtree(self._sandbox)
os.environ = self._env
def load_scenario_data(scenario_pathname):
"""
Load and return scenario data.
Data is loaded from a .json file located in the plainbox package
directory. Individual files are named after the jobs they describe.
"""
pathname = resource_filename("plainbox", scenario_pathname)
with open(pathname, encoding='UTF-8') as stream:
return json.load(stream)
def execute_job(job_id):
"""
Execute the specified job.
The job is invoked using a high-level interface from box so the test will
actually execute the same way as the UI would execute it. It will
create/tear-down appropriate session objects as well.
Returns (result, return_code) where result is the deserialized JSON saved
at the end of the job.
"""
# Create a scratch directory so that we can save results there. The
# shared directory is also used for running tests as some test jobs
# leave junk around the current directory.
with TemporaryDirectory() as scratch_dir:
# Save results to results.json in the scratch directory
pathname = os.path.join(scratch_dir, 'results.json')
# Redirect all standard IO so that the test is silent.
# Run the script, having relocated to the scratch directory
with TestIO() as io, TestCwd(scratch_dir):
try:
main(['run', '-i', job_id,
'--output-format=json', '-o', pathname])
except SystemExit as exc:
# Capture SystemExit that is always raised by main() so that we
# can observe the return code as well.
job_return_code = exc.args[0]
else:
job_return_code = None
# Load the actual results and keep them in memory
with open(pathname, encoding='UTF-8') as stream:
job_result = json.load(stream)
job_outcome = job_result['result_map'][job_id]['outcome']
job_execution_duration = job_result['result_map'][job_id] \
['execution_duration']
# [ At this time TestIO and TemporaryDirectory are gone ]
return (job_id, job_outcome, job_execution_duration,
job_return_code, io.stdout, io.stderr)
|