diff --git a/runbot/common.py b/runbot/common.py
index 328d078e..e774a193 100644
--- a/runbot/common.py
+++ b/runbot/common.py
@@ -70,8 +70,8 @@ def rfind(filename, pattern):
if os.path.isfile(filename):
regexp = re.compile(pattern, re.M)
with file_open(filename, 'r') as f:
- if regexp.findall(f.read()):
- return True
+ result = regexp.findall(f.read())
+ return result or False
return False
diff --git a/runbot/models/build_config.py b/runbot/models/build_config.py
index d7826114..c7922111 100644
--- a/runbot/models/build_config.py
+++ b/runbot/models/build_config.py
@@ -23,7 +23,6 @@ _SAFE_OPCODES |= set(to_opcodes(['LOAD_DEREF', 'STORE_DEREF', 'LOAD_CLOSURE']))
_logger = logging.getLogger(__name__)
-_re_error = r'^(?:\d{4}-\d\d-\d\d \d\d:\d\d:\d\d,\d{3} \d+ (?:ERROR|CRITICAL) )|(?:Traceback \(most recent call last\):)$'
_re_warning = r'^\d{4}-\d\d-\d\d \d\d:\d\d:\d\d,\d{3} \d+ WARNING '
PYTHON_DEFAULT = "# type python code here\n\n\n\n\n\n"
@@ -926,17 +925,19 @@ class ConfigStep(models.Model):
log_time = self._get_log_last_write(build)
if log_time:
build.job_end = log_time
- if self.job_type == 'python' and self.python_result_code and self.python_result_code != PYTHON_DEFAULT:
- build.write(self._make_python_results(build))
- elif self.job_type in ['install_odoo', 'python']:
+ if self.job_type == 'python':
+ if self.python_result_code and self.python_result_code != PYTHON_DEFAULT:
+ self._make_python_results(build)
+ elif self.test_enable or self.test_tags:
+ self._make_odoo_results(build)
+ elif self.job_type == 'install_odoo':
if self.coverage:
build.write(self._make_coverage_results(build))
- if self.test_enable or self.test_tags:
- build.write(self._make_tests_results(build))
+ self._make_odoo_results(build)
elif self.job_type == 'test_upgrade':
- build.write(self._make_upgrade_results(build))
+ self._make_upgrade_results(build)
elif self.job_type == 'restore':
- build.write(self._make_restore_results(build))
+ self._make_restore_results(build)
def _make_python_results(self, build):
eval_ctx = self._make_python_ctx(build)
@@ -945,7 +946,7 @@ class ConfigStep(models.Model):
# todo check return_value or write in try except. Example: local result setted to wrong value
if not isinstance(return_value, dict):
raise RunbotException('python_result_code must set return_value to a dict values on build')
- return return_value
+ build.write(return_value) # old style support
def _make_coverage_results(self, build):
build_values = {}
@@ -965,22 +966,18 @@ class ConfigStep(models.Model):
return build_values
def _make_upgrade_results(self, build):
- build_values = {}
build._log('upgrade', 'Getting results for build %s' % build.dest)
if build.local_result != 'ko':
checkers = [
self._check_log,
- self._check_module_loaded,
self._check_error,
+ self._check_module_loaded,
self._check_module_states,
self._check_build_ended,
self._check_warning,
]
- local_result = self._get_checkers_result(build, checkers)
- build_values['local_result'] = build._get_worst_result([build.local_result, local_result])
-
- return build_values
+ build.local_result = self._get_checkers_result(build, checkers)
def _check_module_states(self, build):
if not build._is_file('logs/modules_states.txt'):
@@ -1009,9 +1006,18 @@ class ConfigStep(models.Model):
def _check_error(self, build, regex=None):
log_path = build._path('logs', '%s.txt' % self.name)
- regex = regex or _re_error
- if rfind(log_path, regex):
- build._log('_make_tests_results', 'Error or traceback found in logs', level="ERROR")
+ re_error = regex or r'^(?:\d{4}-\d\d-\d\d \d\d:\d\d:\d\d,\d{3} \d+ (?:ERROR|CRITICAL) .*)$'
+
+ if result := rfind(log_path, re_error):
+ build._log('_make_tests_results', 'Error found in logs:\n%s' % '\n'.join(result), level="ERROR")
+ return 'ko'
+
+ re_traceback = r'^(?:Traceback \(most recent call last\):)$'
+ if result := rfind(log_path, re_traceback):
+ # find Traceback, all following indented lines and one last non indented line
+ complete_traceback = rfind(log_path, r'^(?:Traceback \(most recent call last\):(?:\n .*)*(?:\n.*)?)')[:10000]
+ complete_traceback = complete_traceback or result
+ build._log('_make_tests_results', 'Traceback found in logs:\n%s' % '\n'.join(complete_traceback), level="ERROR")
return 'ko'
return 'ok'
@@ -1049,34 +1055,28 @@ class ConfigStep(models.Model):
return result
return 'ok'
- def _make_tests_results(self, build):
- build_values = {}
+ def _make_odoo_results(self, build):
build._log('run', 'Getting results for build %s' % build.dest)
if build.local_result != 'ko':
checkers = [
self._check_log,
- self._check_module_loaded,
self._check_error,
- self._check_build_ended
+ self._check_module_loaded,
+ self._check_build_ended,
]
if build.local_result != 'warn':
checkers.append(self._check_warning)
- local_result = self._get_checkers_result(build, checkers)
- build_values['local_result'] = build._get_worst_result([build.local_result, local_result])
- return build_values
+ build.local_result = self._get_checkers_result(build, checkers)
def _make_restore_results(self, build):
- build_values = {}
if build.local_result != 'warn':
checkers = [
self._check_log,
self._check_restore_ended
]
- local_result = self._get_checkers_result(build, checkers)
- build_values['local_result'] = build._get_worst_result([build.local_result, local_result])
- return build_values
+ build.local_result = self._get_checkers_result(build, checkers)
def _make_stats(self, build):
if not self.make_stats: # TODO garbage collect non sticky stat
diff --git a/runbot/models/repo.py b/runbot/models/repo.py
index 6fa8608d..781b9518 100644
--- a/runbot/models/repo.py
+++ b/runbot/models/repo.py
@@ -90,7 +90,7 @@ class Trigger(models.Model):
report_view_id = fields.Many2one('ir.ui.view',
help="custom view to render result",
- string='Docker Template',
+ string='Report view',
domain=[('type', '=', 'qweb')],
context={'default_type': 'qweb', 'default_arch_base': ''},
)
diff --git a/runbot/tests/test_build_config_step.py b/runbot/tests/test_build_config_step.py
index 819afd01..812dec6a 100644
--- a/runbot/tests/test_build_config_step.py
+++ b/runbot/tests/test_build_config_step.py
@@ -618,102 +618,128 @@ class TestMakeResult(RunbotCase):
super(TestMakeResult, self).setUp()
self.ConfigStep = self.env['runbot.build.config.step']
self.Config = self.env['runbot.build.config']
+ self.patchers['getmtime'].return_value = 7200
+ self.logs = []
+ def _log(build, func, message, level='INFO', log_type='runbot', path='runbot'):
+ self.logs.append((level, message))
- @patch('odoo.addons.runbot.models.build_config.os.path.getmtime')
- @patch('odoo.addons.runbot.models.build.BuildResult._log')
- def test_make_result(self, mock_log, mock_getmtime):
+ self.start_patcher('log_patcher', 'odoo.addons.runbot.models.build.BuildResult._log', new=_log)
+
+ self.build = self.Build.create({
+ 'params_id': self.base_params.id,
+ })
+ self.config_step = self.ConfigStep.create({
+ 'name': 'all',
+ 'job_type': 'install_odoo',
+ 'test_tags': '/module,:class.method',
+ })
+
+ def test_make_result_ok(self):
file_content = """
Loading stuff
odoo.stuff.modules.loading: Modules loaded.
Some post install stuff
Initiating shutdown
"""
- logs = []
-
- def _log(func, message, level='INFO', log_type='runbot', path='runbot'):
- logs.append((level, message))
-
- mock_log.side_effect = _log
- mock_getmtime.return_value = 7200
-
- config_step = self.ConfigStep.create({
- 'name': 'all',
- 'job_type': 'install_odoo',
- 'test_tags': '/module,:class.method',
- })
- build = self.Build.create({
- 'params_id': self.base_params.id,
- })
- logs = []
with patch('builtins.open', mock_open(read_data=file_content)):
- config_step._make_results(build)
- self.assertEqual(str(build.job_end), '1970-01-01 02:00:00')
- self.assertEqual(logs, [('INFO', 'Getting results for build %s' % build.dest)])
- self.assertEqual(build.local_result, 'ok')
- # no shutdown
- build = self.Build.create({
- 'params_id': self.base_params.id,
- })
- logs = []
+ self.config_step._make_results(self.build)
+ self.assertEqual(str(self.build.job_end), '1970-01-01 02:00:00')
+ self.assertEqual(self.logs, [('INFO', 'Getting results for build %s' % self.build.dest)])
+ self.assertEqual(self.build.local_result, 'ok')
+
+ def test_make_result_no_shutdown(self):
file_content = """
Loading stuff
odoo.stuff.modules.loading: Modules loaded.
Some post install stuff
"""
with patch('builtins.open', mock_open(read_data=file_content)):
- config_step._make_results(build)
- self.assertEqual(str(build.job_end), '1970-01-01 02:00:00')
- self.assertEqual(build.local_result, 'ko')
- self.assertEqual(logs, [
- ('INFO', 'Getting results for build %s' % build.dest),
- ('ERROR', 'No "Initiating shutdown" found in logs, maybe because of cpu limit.')
+ self.config_step._make_results(self.build)
+ self.assertEqual(str(self.build.job_end), '1970-01-01 02:00:00')
+ self.assertEqual(self.build.local_result, 'ko')
+ self.assertEqual(self.logs, [
+ ('INFO', 'Getting results for build %s' % self.build.dest),
+ ('ERROR', 'No "Initiating shutdown" found in logs, maybe because of cpu limit.'),
])
- # no loaded
- build = self.Build.create({
- 'params_id': self.base_params.id,
- })
- logs = []
+
+ def test_make_result_no_loaded(self):
file_content = """
Loading stuff
"""
with patch('builtins.open', mock_open(read_data=file_content)):
- config_step._make_results(build)
- self.assertEqual(str(build.job_end), '1970-01-01 02:00:00')
- self.assertEqual(build.local_result, 'ko')
- self.assertEqual(logs, [
- ('INFO', 'Getting results for build %s' % build.dest),
- ('ERROR', 'Modules loaded not found in logs')
+ self.config_step._make_results(self.build)
+ self.assertEqual(str(self.build.job_end), '1970-01-01 02:00:00')
+ self.assertEqual(self.build.local_result, 'ko')
+ self.assertEqual(self.logs, [
+ ('INFO', 'Getting results for build %s' % self.build.dest),
+ ('ERROR', 'Modules loaded not found in logs'),
])
- # traceback
- build = self.Build.create({
- 'params_id': self.base_params.id,
- })
- logs = []
+ def test_make_result_traceback(self):
+ file_content = """
+Loading stuff
+Traceback (most recent call last):
+ File "/data/build/odoo/odoo-bin", line 5, in
+ import odoo
+ File "/data/build/odoo/odoo/__init__.py", line 134, in
+ from . import modules
+ File "/data/build/odoo/odoo/modules/__init__.py", line 8, in
+ from . import db, graph, loading, migration, module, registry, neutralize
+ File "/data/build/odoo/odoo/modules/graph.py", line 11, in
+ import odoo.tools as tools
+ File "/data/build/odoo/odoo/tools/__init__.py", line 25, in
+ from .mail import *
+ File "/data/build/odoo/odoo/tools/mail.py", line 32, in
+ safe_attrs = clean.defs.safe_attrs | frozenset(
+AttributeError: module 'lxml.html.clean' has no attribute 'defs'
+2024-05-14 09:54:22,692 17 INFO dbname path.to.test: aaa
+"""
+ with patch('builtins.open', mock_open(read_data=file_content)):
+ self.config_step._make_results(self.build)
+ self.assertEqual(str(self.build.job_end), '1970-01-01 02:00:00')
+ self.assertEqual(self.build.local_result, 'ko')
+ expected = """Traceback found in logs:
+Traceback (most recent call last):
+ File "/data/build/odoo/odoo-bin", line 5, in
+ import odoo
+ File "/data/build/odoo/odoo/__init__.py", line 134, in
+ from . import modules
+ File "/data/build/odoo/odoo/modules/__init__.py", line 8, in
+ from . import db, graph, loading, migration, module, registry, neutralize
+ File "/data/build/odoo/odoo/modules/graph.py", line 11, in
+ import odoo.tools as tools
+ File "/data/build/odoo/odoo/tools/__init__.py", line 25, in
+ from .mail import *
+ File "/data/build/odoo/odoo/tools/mail.py", line 32, in
+ safe_attrs = clean.defs.safe_attrs | frozenset(
+AttributeError: module 'lxml.html.clean' has no attribute 'defs'"""
+ self.assertEqual(self.logs, [
+ ('INFO', 'Getting results for build %s' % self.build.dest),
+ ('ERROR', expected),
+ ])
+
+ def test_make_result_error(self):
file_content = """
Loading stuff
odoo.stuff.modules.loading: Modules loaded.
Some post install stuff
-2019-12-17 17:34:37,692 17 ERROR dbname path.to.test: FAIL: TestClass.test_
-Traceback (most recent call last):
-File "x.py", line a, in test_
- ....
+2024-05-14 09:54:22,692 17 ERROR dbname path.to.test: FAIL: TestClass.test_
+Some log
+2024-05-14 09:54:22,692 17 ERROR dbname path.to.test: FAIL: TestClass.test2_
Initiating shutdown
"""
with patch('builtins.open', mock_open(read_data=file_content)):
- config_step._make_results(build)
- self.assertEqual(str(build.job_end), '1970-01-01 02:00:00')
- self.assertEqual(build.local_result, 'ko')
- self.assertEqual(logs, [
- ('INFO', 'Getting results for build %s' % build.dest),
- ('ERROR', 'Error or traceback found in logs')
+ self.config_step._make_results(self.build)
+ self.assertEqual(str(self.build.job_end), '1970-01-01 02:00:00')
+ self.assertEqual(self.build.local_result, 'ko')
+ self.assertEqual(self.logs, [
+ ('INFO', 'Getting results for build %s' % self.build.dest),
+ ('ERROR', """Error found in logs:
+2024-05-14 09:54:22,692 17 ERROR dbname path.to.test: FAIL: TestClass.test_
+2024-05-14 09:54:22,692 17 ERROR dbname path.to.test: FAIL: TestClass.test2_"""),
])
- # warning in logs
- build = self.Build.create({
- 'params_id': self.base_params.id,
- })
- logs = []
+ def test_make_result_warning(self):
file_content = """
Loading stuff
odoo.stuff.modules.loading: Modules loaded.
@@ -722,30 +748,26 @@ Some post install stuff
Initiating shutdown
"""
with patch('builtins.open', mock_open(read_data=file_content)):
- config_step._make_results(build)
- self.assertEqual(str(build.job_end), '1970-01-01 02:00:00')
- self.assertEqual(build.local_result, 'warn')
- self.assertEqual(logs, [
- ('INFO', 'Getting results for build %s' % build.dest),
+ self.config_step._make_results(self.build)
+ self.assertEqual(str(self.build.job_end), '1970-01-01 02:00:00')
+ self.assertEqual(self.build.local_result, 'warn')
+ self.assertEqual(self.logs, [
+ ('INFO', 'Getting results for build %s' % self.build.dest),
('WARNING', 'Warning found in logs')
])
# no log file
- logs = []
+ self.logs = []
self.patchers['isfile'].return_value = False
- config_step._make_results(build)
+ self.config_step._make_results(self.build)
- self.assertEqual(build.local_result, 'ko')
- self.assertEqual(logs, [
- ('INFO', 'Getting results for build %s' % build.dest),
+ self.assertEqual(self.build.local_result, 'ko')
+ self.assertEqual(self.logs, [
+ ('INFO', 'Getting results for build %s' % self.build.dest),
('ERROR', 'Log file not found at the end of test job')
])
- # no error but build was already in warn
- build = self.Build.create({
- 'params_id': self.base_params.id,
- })
- logs = []
+ def test_make_result_already_warn(self):
file_content = """
Loading stuff
odoo.stuff.modules.loading: Modules loaded.
@@ -753,17 +775,18 @@ Some post install stuff
Initiating shutdown
"""
self.patchers['isfile'].return_value = True
- build.local_result = 'warn'
+ self.build.local_result = 'warn'
with patch('builtins.open', mock_open(read_data=file_content)):
- config_step._make_results(build)
- self.assertEqual(logs, [
- ('INFO', 'Getting results for build %s' % build.dest)
+ self.config_step._make_results(self.build)
+ self.assertEqual(self.logs, [
+ ('INFO', 'Getting results for build %s' % self.build.dest)
])
- self.assertEqual(str(build.job_end), '1970-01-01 02:00:00')
- self.assertEqual(build.local_result, 'warn')
+ self.assertEqual(str(self.build.job_end), '1970-01-01 02:00:00')
+ self.assertEqual(self.build.local_result, 'warn')
- @patch('odoo.addons.runbot.models.build_config.ConfigStep._make_tests_results')
- def test_make_python_result(self, mock_make_tests_results):
+
+ @patch('odoo.addons.runbot.models.build_config.ConfigStep._make_odoo_results')
+ def test_make_python_result(self, mock_make_odoo_results):
config_step = self.ConfigStep.create({
'name': 'all',
'job_type': 'python',
@@ -785,7 +808,10 @@ Initiating shutdown
# no result defined
config_step.python_result_code = ""
- mock_make_tests_results.return_value = {'local_result': 'warn'}
+ def make_warn(build):
+ build.local_result = "warn"
+
+ mock_make_odoo_results.side_effect = make_warn
config_step._make_results(build)
self.assertEqual(build.local_result, 'warn')