blob: 7347229d897198df85db7aa207b0efba9c2932a8 [file] [log] [blame]
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +01001# osmo_gsm_tester: test class
2#
3# Copyright (C) 2017 by sysmocom - s.f.m.c. GmbH
4#
5# Author: Pau Espin Pedrol <pespin@sysmocom.de>
6#
7# This program is free software: you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as
9# 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
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program. If not, see <http://www.gnu.org/licenses/>.
19
20import os
21import sys
22import time
23import traceback
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +010024
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +020025from . import log
26from . import util
27from . import resource
28from .event_loop import MainLoop
29
30from .. import testenv
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +010031
32class Test(log.Origin):
Pau Espin Pedrol02e8a8d2020-03-05 17:22:40 +010033 UNKNOWN = 'UNKNOWN' # matches junit 'error'
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +010034 SKIP = 'skip'
35 PASS = 'pass'
36 FAIL = 'FAIL'
37
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +010038 def __init__(self, suite_run, test_basename):
39 self.basename = test_basename
40 super().__init__(log.C_TST, self.basename)
Pau Espin Pedrol58603672018-08-09 13:45:55 +020041 self._run_dir = None
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +010042 self.suite_run = suite_run
43 self.path = os.path.join(self.suite_run.definition.suite_dir, self.basename)
44 self.status = Test.UNKNOWN
45 self.start_timestamp = 0
46 self.duration = 0
47 self.fail_type = None
48 self.fail_message = None
Pau Espin Pedrolec285722020-06-11 17:57:43 +020049 self.log_targets = []
Pau Espin Pedrol644cb412020-03-04 16:14:31 +010050 self._report_stdout = None
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +010051
Pau Espin Pedrol30637302020-05-06 21:11:02 +020052 def module_name(self):
53 'Return test name without trailing .py'
54 assert self.basename.endswith('.py')
55 return self.basename[:-3]
56
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +010057 def get_run_dir(self):
58 if self._run_dir is None:
59 self._run_dir = util.Dir(self.suite_run.get_run_dir().new_dir(self._name))
60 return self._run_dir
61
62 def run(self):
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +020063 testenv_obj = None
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +010064 try:
Pau Espin Pedrolec285722020-06-11 17:57:43 +020065 self.log_targets = [log.FileLogTarget(self.get_run_dir().new_child(log.FILE_LOG)).set_all_levels(log.L_DBG).style_change(trace=True),
66 log.FileLogTarget(self.get_run_dir().new_child(log.FILE_LOG_BRIEF)).style_change(src=False, all_origins_on_levels=(log.L_ERR, log.L_TRACEBACK))]
Pau Espin Pedrola442cb82020-05-05 12:54:37 +020067 log.large_separator(self.suite_run.trial().name(), self.suite_run.name(), self.name(), sublevel=3)
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +010068 self.status = Test.UNKNOWN
69 self.start_timestamp = time.time()
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +020070 testenv_obj = testenv.setup(self.suite_run, self)
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +010071 with self.redirect_stdout():
72 util.run_python_file('%s.%s' % (self.suite_run.definition.name(), self.basename),
73 self.path)
74 if self.status == Test.UNKNOWN:
75 self.set_pass()
76 except Exception as e:
77 if hasattr(e, 'msg'):
78 msg = e.msg
79 else:
80 msg = str(e)
81 if isinstance(e, AssertionError):
82 # AssertionError lacks further information on what was
83 # asserted. Find the line where the code asserted:
84 msg += log.get_src_from_exc_info(sys.exc_info())
85 # add source file information to failure report
86 if hasattr(e, 'origins'):
87 msg += ' [%s]' % e.origins
88 tb_str = traceback.format_exc()
89 if isinstance(e, resource.NoResourceExn):
90 tb_str += self.suite_run.resource_status_str()
91 self.set_fail(type(e).__name__, msg, tb_str, log.get_src_from_exc_info())
92 except BaseException as e:
93 # when the program is aborted by a signal (like Ctrl-C), escalate to abort all.
94 self.err('TEST RUN ABORTED: %s' % type(e).__name__)
95 raise
Pau Espin Pedrol5eae2c52019-09-18 16:50:38 +020096 finally:
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +020097 if testenv_obj:
98 testenv_obj.stop()
Pau Espin Pedrolec285722020-06-11 17:57:43 +020099 for log_tgt in self.log_targets:
100 log_tgt.remove()
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +0100101
102 def name(self):
103 l = log.get_line_for_src(self.path)
104 if l is not None:
105 return '%s:%s' % (self._name, l)
106 return super().name()
107
Pau Espin Pedrol444129e2020-06-12 16:38:37 +0200108 def elapsed_time(self):
109 'time elapsed since test was started'
110 return time.time() - self.start_timestamp
111
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +0100112 def set_fail(self, fail_type, fail_message, tb_str=None, src=4):
113 self.status = Test.FAIL
Pau Espin Pedrol444129e2020-06-12 16:38:37 +0200114 self.duration = self.elapsed_time()
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +0100115 self.fail_type = fail_type
116 self.fail_message = fail_message
117
118 if tb_str is None:
119 # populate an exception-less call to set_fail() with traceback info
120 tb_str = ''.join(traceback.format_stack()[:-1])
121
122 self.fail_tb = tb_str
123 self.err('%s: %s' % (self.fail_type, self.fail_message), _src=src)
124 if self.fail_tb:
125 self.log(self.fail_tb, _level=log.L_TRACEBACK)
126 self.log('Test FAILED (%.1f sec)' % self.duration)
127
128 def set_pass(self):
129 self.status = Test.PASS
Pau Espin Pedrol444129e2020-06-12 16:38:37 +0200130 self.duration = self.elapsed_time()
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +0100131 self.log('Test passed (%.1f sec)' % self.duration)
132
133 def set_skip(self):
134 self.status = Test.SKIP
135 self.duration = 0
136
Pau Espin Pedrol644cb412020-03-04 16:14:31 +0100137 def set_report_stdout(self, text):
138 'Overwrite stdout text stored in report from inside a test'
139 self._report_stdout = text
140
141 def report_stdout(self):
142 # If test overwrote the text, provide it:
143 if self._report_stdout is not None:
144 return self._report_stdout
Pau Espin Pedrolec285722020-06-11 17:57:43 +0200145 # Otherwise vy default provide the entire test brief log:
146 if len(self.log_targets) == 2 and self.log_targets[1].log_file_path() is not None:
147 with open(self.log_targets[1].log_file_path(), 'r') as myfile:
Pau Espin Pedrol644cb412020-03-04 16:14:31 +0100148 return myfile.read()
149 else:
150 return 'test log file not available'
Pau Espin Pedrol5bbdab82020-02-24 18:19:10 +0100151
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +0100152# vim: expandtab tabstop=4 shiftwidth=4