blob: 5b9df767df21dd0c3383ef60a2e3813bebada894 [file] [log] [blame]
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +02001# osmo_gsm_tester: test suite
2#
3# Copyright (C) 2016-2017 by sysmocom - s.f.m.c. GmbH
4#
5# Author: Neels Hofmeyr <neels@hofmeyr.de>
6#
7# This program is free software: you can redistribute it and/or modify
Harald Welte27205342017-06-03 09:51:45 +02008# it under the terms of the GNU General Public License as
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +02009# published by the Free Software Foundation, either version 3 of the
10# License, or (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Harald Welte27205342017-06-03 09:51:45 +020015# GNU General Public License for more details.
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +020016#
Harald Welte27205342017-06-03 09:51:45 +020017# You should have received a copy of the GNU General Public License
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +020018# along with this program. If not, see <http://www.gnu.org/licenses/>.
19
20import os
Neels Hofmeyr3531a192017-03-28 14:30:28 +020021import sys
22import time
Pau Espin Pedrol0ffb4142017-05-15 18:24:35 +020023import traceback
Neels Hofmeyr2d1d5612017-05-22 20:02:41 +020024import pprint
Pau Espin Pedrol41dabb32017-11-09 14:38:49 +010025from . import config, log, template, util, resource, schema, event_loop
26from . import osmo_nitb, osmo_hlr, osmo_mgcpgw, osmo_mgw, osmo_msc, osmo_bsc, osmo_stp, modem, esme, sms
Pau Espin Pedroldfe38ad2017-11-09 13:57:39 +010027from . import testenv
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +020028
Neels Hofmeyr1ffc3fe2017-05-07 02:15:21 +020029class Timeout(Exception):
30 pass
31
Neels Hofmeyr3531a192017-03-28 14:30:28 +020032class SuiteDefinition(log.Origin):
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +020033 '''A test suite reserves resources for a number of tests.
34 Each test requires a specific number of modems, BTSs etc., which are
35 reserved beforehand by a test suite. This way several test suites can be
36 scheduled dynamically without resource conflicts arising halfway through
37 the tests.'''
38
39 CONF_FILENAME = 'suite.conf'
40
Neels Hofmeyr3531a192017-03-28 14:30:28 +020041 def __init__(self, suite_dir):
Neels Hofmeyr3531a192017-03-28 14:30:28 +020042 self.suite_dir = suite_dir
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +020043 super().__init__(log.C_CNF, os.path.basename(self.suite_dir))
Neels Hofmeyr3531a192017-03-28 14:30:28 +020044 self.read_conf()
45
46 def read_conf(self):
Neels Hofmeyr6ccda112017-06-06 19:41:17 +020047 self.dbg('reading %s' % SuiteDefinition.CONF_FILENAME)
48 if not os.path.isdir(self.suite_dir):
49 raise RuntimeError('No such directory: %r' % self.suite_dir)
50 self.conf = config.read(os.path.join(self.suite_dir,
51 SuiteDefinition.CONF_FILENAME),
Pau Espin Pedrol0b302792017-09-10 16:33:10 +020052 resource.CONF_SCHEMA)
Neels Hofmeyr6ccda112017-06-06 19:41:17 +020053 self.load_test_basenames()
Neels Hofmeyr3531a192017-03-28 14:30:28 +020054
Neels Hofmeyr6ccda112017-06-06 19:41:17 +020055 def load_test_basenames(self):
56 self.test_basenames = []
57 for basename in sorted(os.listdir(self.suite_dir)):
58 if not basename.endswith('.py'):
59 continue
60 self.test_basenames.append(basename)
Neels Hofmeyr3531a192017-03-28 14:30:28 +020061
Neels Hofmeyr3531a192017-03-28 14:30:28 +020062
Neels Hofmeyr3531a192017-03-28 14:30:28 +020063class Test(log.Origin):
Pau Espin Pedrol0ffb4142017-05-15 18:24:35 +020064 UNKNOWN = 'UNKNOWN'
Neels Hofmeyr6ccda112017-06-06 19:41:17 +020065 SKIP = 'skip'
66 PASS = 'pass'
Pau Espin Pedrol0ffb4142017-05-15 18:24:35 +020067 FAIL = 'FAIL'
Neels Hofmeyr3531a192017-03-28 14:30:28 +020068
Pau Espin Pedrold0912332017-06-14 13:27:08 +020069 _run_dir = None
70
Neels Hofmeyr6ccda112017-06-06 19:41:17 +020071 def __init__(self, suite_run, test_basename):
Neels Hofmeyr3531a192017-03-28 14:30:28 +020072 self.basename = test_basename
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +020073 super().__init__(log.C_TST, self.basename)
74 self.suite_run = suite_run
Neels Hofmeyr6ccda112017-06-06 19:41:17 +020075 self.path = os.path.join(self.suite_run.definition.suite_dir, self.basename)
Pau Espin Pedrol0ffb4142017-05-15 18:24:35 +020076 self.status = Test.UNKNOWN
77 self.start_timestamp = 0
78 self.duration = 0
79 self.fail_type = None
80 self.fail_message = None
Neels Hofmeyr3531a192017-03-28 14:30:28 +020081
Pau Espin Pedrold0912332017-06-14 13:27:08 +020082 def get_run_dir(self):
83 if self._run_dir is None:
84 self._run_dir = util.Dir(self.suite_run.get_run_dir().new_dir(self._name))
85 return self._run_dir
86
Neels Hofmeyr6ccda112017-06-06 19:41:17 +020087 def run(self):
Pau Espin Pedrol0ffb4142017-05-15 18:24:35 +020088 try:
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +020089 log.large_separator(self.suite_run.trial.name(), self.suite_run.name(), self.name(), sublevel=3)
90 self.status = Test.UNKNOWN
91 self.start_timestamp = time.time()
Pau Espin Pedroldfe38ad2017-11-09 13:57:39 +010092 testenv.setup(self.suite_run, self, sys.modules[__name__], event_loop, sms)
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +020093 with self.redirect_stdout():
94 util.run_python_file('%s.%s' % (self.suite_run.definition.name(), self.basename),
95 self.path)
96 if self.status == Test.UNKNOWN:
97 self.set_pass()
Pau Espin Pedrol0ffb4142017-05-15 18:24:35 +020098 except Exception as e:
Neels Hofmeyr6ccda112017-06-06 19:41:17 +020099 if hasattr(e, 'msg'):
100 msg = e.msg
Pau Espin Pedrol0ffb4142017-05-15 18:24:35 +0200101 else:
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200102 msg = str(e)
103 if isinstance(e, AssertionError):
104 # AssertionError lacks further information on what was
105 # asserted. Find the line where the code asserted:
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200106 msg += log.get_src_from_exc_info(sys.exc_info())
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200107 # add source file information to failure report
108 if hasattr(e, 'origins'):
109 msg += ' [%s]' % e.origins
110 tb_str = traceback.format_exc()
111 if isinstance(e, resource.NoResourceExn):
112 tb_str += self.suite_run.resource_status_str()
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200113 self.set_fail(type(e).__name__, msg, tb_str, log.get_src_from_exc_info())
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200114 except BaseException as e:
115 # when the program is aborted by a signal (like Ctrl-C), escalate to abort all.
116 self.err('TEST RUN ABORTED: %s' % type(e).__name__)
117 raise
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200118
119 def name(self):
120 l = log.get_line_for_src(self.path)
121 if l is not None:
122 return '%s:%s' % (self._name, l)
123 return super().name()
124
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200125 def set_fail(self, fail_type, fail_message, tb_str=None, src=4):
Pau Espin Pedrol0ffb4142017-05-15 18:24:35 +0200126 self.status = Test.FAIL
127 self.duration = time.time() - self.start_timestamp
128 self.fail_type = fail_type
129 self.fail_message = fail_message
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200130
131 if tb_str is None:
132 # populate an exception-less call to set_fail() with traceback info
133 tb_str = ''.join(traceback.format_stack()[:-1])
134
135 self.fail_tb = tb_str
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200136 self.err('%s: %s' % (self.fail_type, self.fail_message), _src=src)
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200137 if self.fail_tb:
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200138 self.log(self.fail_tb, _level=log.L_TRACEBACK)
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200139 self.log('Test FAILED (%.1f sec)' % self.duration)
Pau Espin Pedrol0ffb4142017-05-15 18:24:35 +0200140
141 def set_pass(self):
142 self.status = Test.PASS
143 self.duration = time.time() - self.start_timestamp
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200144 self.log('Test passed (%.1f sec)' % self.duration)
Pau Espin Pedrol0ffb4142017-05-15 18:24:35 +0200145
146 def set_skip(self):
147 self.status = Test.SKIP
148 self.duration = 0
149
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200150class SuiteRun(log.Origin):
Pau Espin Pedrol0ffb4142017-05-15 18:24:35 +0200151 UNKNOWN = 'UNKNOWN'
152 PASS = 'PASS'
153 FAIL = 'FAIL'
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200154
155 trial = None
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200156 status = None
Neels Hofmeyrf8e61862017-06-06 23:08:07 +0200157 start_timestamp = None
158 duration = None
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200159 resources_pool = None
160 reserved_resources = None
Neels Hofmeyr4d688c22017-05-29 04:13:58 +0200161 objects_to_clean_up = None
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200162 _resource_requirements = None
163 _config = None
164 _processes = None
Pau Espin Pedrold0912332017-06-14 13:27:08 +0200165 _run_dir = None
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200166
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200167 def __init__(self, trial, suite_scenario_str, suite_definition, scenarios=[]):
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200168 super().__init__(log.C_TST, suite_scenario_str)
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200169 self.trial = trial
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200170 self.definition = suite_definition
171 self.scenarios = scenarios
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200172 self.resources_pool = resource.ResourcesPool()
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200173 self.status = SuiteRun.UNKNOWN
174 self.load_tests()
175
176 def load_tests(self):
177 self.tests = []
178 for test_basename in self.definition.test_basenames:
179 self.tests.append(Test(self, test_basename))
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200180
Neels Hofmeyr4d688c22017-05-29 04:13:58 +0200181 def register_for_cleanup(self, *obj):
182 assert all([hasattr(o, 'cleanup') for o in obj])
183 self.objects_to_clean_up = self.objects_to_clean_up or []
184 self.objects_to_clean_up.extend(obj)
185
186 def objects_cleanup(self):
187 while self.objects_to_clean_up:
188 obj = self.objects_to_clean_up.pop()
Pau Espin Pedrol6100b622017-07-31 18:19:06 +0200189 try:
190 obj.cleanup()
191 except Exception:
192 log.log_exn()
Neels Hofmeyr4d688c22017-05-29 04:13:58 +0200193
Pau Espin Pedrol0ffb4142017-05-15 18:24:35 +0200194 def mark_start(self):
Pau Espin Pedrol0ffb4142017-05-15 18:24:35 +0200195 self.start_timestamp = time.time()
196 self.duration = 0
Pau Espin Pedrol0ffb4142017-05-15 18:24:35 +0200197 self.status = SuiteRun.UNKNOWN
198
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200199 def combined(self, conf_name):
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200200 log.dbg(combining=conf_name)
201 log.ctx(combining_scenarios=conf_name)
Pau Espin Pedrol0b302792017-09-10 16:33:10 +0200202 combination = config.replicate_times(self.definition.conf.get(conf_name, {}))
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200203 log.dbg(definition_conf=combination)
204 for scenario in self.scenarios:
205 log.ctx(combining_scenarios=conf_name, scenario=scenario.name())
Pau Espin Pedrol0b302792017-09-10 16:33:10 +0200206 c = config.replicate_times(scenario.get(conf_name, {}))
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200207 log.dbg(scenario=scenario.name(), conf=c)
208 if c is None:
209 continue
210 config.combine(combination, c)
211 return combination
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200212
Pau Espin Pedrold0912332017-06-14 13:27:08 +0200213 def get_run_dir(self):
214 if self._run_dir is None:
215 self._run_dir = util.Dir(self.trial.get_run_dir().new_dir(self.name()))
216 return self._run_dir
217
218 def get_test_run_dir(self):
219 if self.current_test:
220 return self.current_test.get_run_dir()
221 return self.get_run_dir()
222
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200223 def resource_requirements(self):
224 if self._resource_requirements is None:
225 self._resource_requirements = self.combined('resources')
226 return self._resource_requirements
227
228 def config(self):
229 if self._config is None:
230 self._config = self.combined('config')
231 return self._config
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200232
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200233 def reserve_resources(self):
234 if self.reserved_resources:
235 raise RuntimeError('Attempt to reserve resources twice for a SuiteRun')
Neels Hofmeyr7e2e8f12017-05-14 03:37:13 +0200236 self.log('reserving resources in', self.resources_pool.state_dir, '...')
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200237 self.reserved_resources = self.resources_pool.reserve(self, self.resource_requirements())
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200238
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200239 def run_tests(self, names=None):
Pau Espin Pedrol469316f2017-05-17 14:51:31 +0200240 try:
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200241 log.large_separator(self.trial.name(), self.name(), sublevel=2)
242 self.mark_start()
243 event_loop.register_poll_func(self.poll)
244 if not self.reserved_resources:
245 self.reserve_resources()
246 for test in self.tests:
247 if names and not test.name() in names:
248 test.set_skip()
249 continue
Pau Espin Pedrold0912332017-06-14 13:27:08 +0200250 self.current_test = test
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200251 test.run()
Pau Espin Pedrol1dd29552017-06-13 18:07:57 +0200252 self.stop_processes()
253 self.objects_cleanup()
254 self.reserved_resources.put_all()
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200255 except Exception:
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200256 log.log_exn()
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200257 except BaseException as e:
258 # when the program is aborted by a signal (like Ctrl-C), escalate to abort all.
259 self.err('SUITE RUN ABORTED: %s' % type(e).__name__)
260 raise
Pau Espin Pedrol469316f2017-05-17 14:51:31 +0200261 finally:
262 # if sys.exit() called from signal handler (e.g. SIGINT), SystemExit
263 # base exception is raised. Make sure to stop processes in this
264 # finally section. Resources are automatically freed with 'atexit'.
265 self.stop_processes()
Neels Hofmeyr4d688c22017-05-29 04:13:58 +0200266 self.objects_cleanup()
Neels Hofmeyred4e5282017-05-29 02:53:54 +0200267 self.free_resources()
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200268 event_loop.unregister_poll_func(self.poll)
269 self.duration = time.time() - self.start_timestamp
270
271 passed, skipped, failed = self.count_test_results()
272 # if no tests ran, count it as failure
273 if passed and not failed:
274 self.status = SuiteRun.PASS
275 else:
276 self.status = SuiteRun.FAIL
277
278 log.large_separator(self.trial.name(), self.name(), self.status, sublevel=2, space_above=False)
279
280 def passed(self):
281 return self.status == SuiteRun.PASS
282
283 def count_test_results(self):
284 passed = 0
285 skipped = 0
286 failed = 0
287 for test in self.tests:
288 if test.status == Test.PASS:
289 passed += 1
290 elif test.status == Test.FAIL:
291 failed += 1
292 else:
293 skipped += 1
294 return (passed, skipped, failed)
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200295
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200296 def remember_to_stop(self, process):
297 if self._processes is None:
298 self._processes = []
Pau Espin Pedrolecf10792017-05-08 16:56:38 +0200299 self._processes.insert(0, process)
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200300
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200301 def stop_processes(self):
Pau Espin Pedrol1dd29552017-06-13 18:07:57 +0200302 while self._processes:
303 self._processes.pop().terminate()
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200304
Neels Hofmeyred4e5282017-05-29 02:53:54 +0200305 def free_resources(self):
306 if self.reserved_resources is None:
307 return
308 self.reserved_resources.free()
309
Neels Hofmeyrb902b292017-06-06 21:52:03 +0200310 def ip_address(self, specifics=None):
311 return self.reserved_resources.get(resource.R_IP_ADDRESS, specifics=specifics)
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200312
Neels Hofmeyr76d81032017-05-18 18:35:32 +0200313 def nitb(self, ip_address=None):
314 if ip_address is None:
315 ip_address = self.ip_address()
316 return osmo_nitb.OsmoNitb(self, ip_address)
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200317
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200318 def hlr(self, ip_address=None):
319 if ip_address is None:
320 ip_address = self.ip_address()
321 return osmo_hlr.OsmoHlr(self, ip_address)
322
323 def mgcpgw(self, ip_address=None, bts_ip=None):
324 if ip_address is None:
325 ip_address = self.ip_address()
326 return osmo_mgcpgw.OsmoMgcpgw(self, ip_address, bts_ip)
327
Pau Espin Pedrol386b78d2017-11-09 13:02:09 +0100328 def mgw(self, ip_address=None):
329 if ip_address is None:
330 ip_address = self.ip_address()
331 return osmo_mgw.OsmoMgw(self, ip_address)
332
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200333 def msc(self, hlr, mgcpgw, ip_address=None):
334 if ip_address is None:
335 ip_address = self.ip_address()
336 return osmo_msc.OsmoMsc(self, hlr, mgcpgw, ip_address)
337
Pau Espin Pedrol386b78d2017-11-09 13:02:09 +0100338 def bsc(self, msc, mgw, ip_address=None):
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200339 if ip_address is None:
340 ip_address = self.ip_address()
Pau Espin Pedrol386b78d2017-11-09 13:02:09 +0100341 return osmo_bsc.OsmoBsc(self, msc, mgw, ip_address)
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200342
Neels Hofmeyr38b051c2017-06-13 16:26:06 +0200343 def stp(self, ip_address=None):
344 if ip_address is None:
345 ip_address = self.ip_address()
346 return osmo_stp.OsmoStp(self, ip_address)
347
Neels Hofmeyrb902b292017-06-06 21:52:03 +0200348 def bts(self, specifics=None):
Pau Espin Pedrol15aae982017-09-08 13:55:54 +0200349 bts = bts_obj(self, self.reserved_resources.get(resource.R_BTS, specifics=specifics))
Pau Espin Pedrol5e0c2512017-11-06 18:40:23 +0100350 bts.set_lac(self.lac())
Pau Espin Pedrol4ccce7c2017-11-07 11:13:20 +0100351 bts.set_cellid(self.cellid())
Pau Espin Pedrol15aae982017-09-08 13:55:54 +0200352 self.register_for_cleanup(bts)
353 return bts
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200354
Neels Hofmeyrb902b292017-06-06 21:52:03 +0200355 def modem(self, specifics=None):
356 conf = self.reserved_resources.get(resource.R_MODEM, specifics=specifics)
Neels Hofmeyr4d688c22017-05-29 04:13:58 +0200357 self.dbg('create Modem object', conf=conf)
Pau Espin Pedrol6cdd2fd2017-11-07 11:57:42 +0100358 ms = modem.Modem(conf)
359 self.register_for_cleanup(ms)
360 return ms
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200361
Neels Hofmeyrf2d279c2017-05-06 15:05:02 +0200362 def modems(self, count):
363 l = []
364 for i in range(count):
365 l.append(self.modem())
366 return l
367
Pau Espin Pedrol2d16f6f2017-05-30 15:33:57 +0200368 def esme(self):
369 esme_obj = esme.Esme(self.msisdn())
Pau Espin Pedrolac9c1bb2017-08-10 10:59:40 +0200370 self.register_for_cleanup(esme_obj)
Pau Espin Pedrol2d16f6f2017-05-30 15:33:57 +0200371 return esme_obj
372
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200373 def msisdn(self):
Pau Espin Pedrol2d16f6f2017-05-30 15:33:57 +0200374 msisdn = self.resources_pool.next_msisdn(self)
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200375 self.log('using MSISDN', msisdn)
376 return msisdn
377
Pau Espin Pedrol5e0c2512017-11-06 18:40:23 +0100378 def lac(self):
379 lac = self.resources_pool.next_lac(self)
380 self.log('using LAC', lac)
381 return lac
382
Pau Espin Pedrol4ccce7c2017-11-07 11:13:20 +0100383 def cellid(self):
384 cellid = self.resources_pool.next_cellid(self)
385 self.log('using CellId', cellid)
386 return cellid
387
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200388 def poll(self):
389 if self._processes:
390 for process in self._processes:
Neels Hofmeyr5356d0a2017-04-10 03:45:30 +0200391 if process.terminated():
Neels Hofmeyr85eb3242017-04-09 22:01:16 +0200392 process.log_stdout_tail()
393 process.log_stderr_tail()
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200394 log.ctx(process)
Pau Espin Pedrol800a6972017-07-03 18:34:09 +0200395 raise log.Error('Process ended prematurely: %s' % process.name())
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200396
397 def prompt(self, *msgs, **msg_details):
398 'ask for user interaction. Do not use in tests that should run automatically!'
399 if msg_details:
400 msgs = list(msgs)
401 msgs.append('{%s}' %
402 (', '.join(['%s=%r' % (k,v)
403 for k,v in sorted(msg_details.items())])))
404 msg = ' '.join(msgs) or 'Hit Enter to continue'
405 self.log('prompt:', msg)
Neels Hofmeyracf0c932017-05-06 16:05:33 +0200406 sys.__stdout__.write('\n\n--- PROMPT ---\n')
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200407 sys.__stdout__.write(msg)
Neels Hofmeyracf0c932017-05-06 16:05:33 +0200408 sys.__stdout__.write('\n')
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200409 sys.__stdout__.flush()
Pau Espin Pedrol90c23cc2017-07-03 13:12:37 +0200410 entered = util.input_polling('> ', event_loop.poll)
Neels Hofmeyracf0c932017-05-06 16:05:33 +0200411 self.log('prompt entered:', repr(entered))
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200412 return entered
413
Neels Hofmeyr2d1d5612017-05-22 20:02:41 +0200414 def resource_status_str(self):
415 return '\n'.join(('',
416 'SUITE RUN: %s' % self.origin_id(),
417 'ASKED FOR:', pprint.pformat(self._resource_requirements),
418 'RESERVED COUNT:', pprint.pformat(self.reserved_resources.counts()),
419 'RESOURCES STATE:', repr(self.reserved_resources)))
420
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200421loaded_suite_definitions = {}
422
423def load(suite_name):
424 global loaded_suite_definitions
425
426 suite = loaded_suite_definitions.get(suite_name)
427 if suite is not None:
428 return suite
429
430 suites_dir = config.get_suites_dir()
431 suite_dir = suites_dir.child(suite_name)
432 if not suites_dir.exists(suite_name):
433 raise RuntimeError('Suite not found: %r in %r' % (suite_name, suites_dir))
434 if not suites_dir.isdir(suite_name):
435 raise RuntimeError('Suite name found, but not a directory: %r' % (suite_dir))
436
437 suite_def = SuiteDefinition(suite_dir)
438 loaded_suite_definitions[suite_name] = suite_def
439 return suite_def
440
441def parse_suite_scenario_str(suite_scenario_str):
442 tokens = suite_scenario_str.split(':')
443 if len(tokens) > 2:
444 raise RuntimeError('invalid combination string: %r' % suite_scenario_str)
445
446 suite_name = tokens[0]
447 if len(tokens) <= 1:
448 scenario_names = []
449 else:
450 scenario_names = tokens[1].split('+')
451
452 return suite_name, scenario_names
453
454def load_suite_scenario_str(suite_scenario_str):
455 suite_name, scenario_names = parse_suite_scenario_str(suite_scenario_str)
456 suite = load(suite_name)
Pau Espin Pedrol0b302792017-09-10 16:33:10 +0200457 scenarios = [config.get_scenario(scenario_name, resource.CONF_SCHEMA) for scenario_name in scenario_names]
Your Name44af3412017-04-13 03:11:59 +0200458 return (suite_scenario_str, suite, scenarios)
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200459
460def bts_obj(suite_run, conf):
461 bts_type = conf.get('type')
Neels Hofmeyrd28d1a72017-06-14 02:59:55 +0200462 log.dbg('create BTS object', type=bts_type)
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200463 bts_class = resource.KNOWN_BTS_TYPES.get(bts_type)
464 if bts_class is None:
465 raise RuntimeError('No such BTS type is defined: %r' % bts_type)
466 return bts_class(suite_run, conf)
467
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200468# vim: expandtab tabstop=4 shiftwidth=4