From 73f720a55c8d2c3b3f867e293ff5cb6fb93281a7 Mon Sep 17 00:00:00 2001 From: Christophe Monniez Date: Sun, 10 Nov 2019 17:16:41 +0100 Subject: [PATCH] [IMP] runbot: refactor tests A lot of things have to be mocked during runbot tests, as a consequence, a lot of patch decorators accumulate in a big stack uppon some tests methods. Also, a lot of mocks are used multiple times among tests. With this commit, a new RunbotClass is added that comes with patches ready to be started. A start_patcher helper method is available to start a patch and add the appropriate stop in a cleanup. Also, when a build is created in the tests, the _get_params method is always called, resulting in an annoying git warning. With this commit, a create_build method is added on the test class, that way the _get_params is always mocked when a build is created. --- runbot/common.py | 6 +- runbot/container.py | 2 +- runbot/models/build.py | 8 +- runbot/models/build_config.py | 3 +- runbot/models/repo.py | 6 +- runbot/tests/__init__.py | 1 + runbot/tests/common.py | 55 ++++++++ runbot/tests/test_branch.py | 7 +- runbot/tests/test_build.py | 171 ++++++++++--------------- runbot/tests/test_build_config_step.py | 94 +++++++------- runbot/tests/test_build_error.py | 38 +++--- runbot/tests/test_cron.py | 31 ++--- runbot/tests/test_event.py | 11 +- runbot/tests/test_frontend.py | 4 +- runbot/tests/test_repo.py | 57 ++++----- runbot/tests/test_schedule.py | 10 +- 16 files changed, 251 insertions(+), 253 deletions(-) create mode 100644 runbot/tests/common.py diff --git a/runbot/common.py b/runbot/common.py index 5b2c2ae5..6c5e6812 100644 --- a/runbot/common.py +++ b/runbot/common.py @@ -63,10 +63,14 @@ def now(): def grep(filename, string): if os.path.isfile(filename): - return open(filename).read().find(string) != -1 + return find(filename, string) != -1 return False +def find(filename, string): + return open(filename).read().find(string) + + def uniq_list(l): return OrderedDict.fromkeys(l).keys() diff --git a/runbot/container.py b/runbot/container.py index b3ab86a4..cb09ab15 100644 --- a/runbot/container.py +++ b/runbot/container.py @@ -14,7 +14,7 @@ import datetime import io import json import logging -import os +from .common import os import shutil import subprocess import time diff --git a/runbot/models/build.py b/runbot/models/build.py index 69cb8bb5..98760c5e 100644 --- a/runbot/models/build.py +++ b/runbot/models/build.py @@ -2,14 +2,13 @@ import fnmatch import glob import logging -import os import pwd import re import shutil import subprocess import time import datetime -from ..common import dt2time, fqdn, now, grep, uniq_list, local_pgadmin_cursor, s2human, Commit, dest_reg +from ..common import dt2time, fqdn, now, grep, uniq_list, local_pgadmin_cursor, s2human, Commit, dest_reg, os from ..container import docker_build, docker_stop, docker_is_running, Command from odoo.addons.runbot.models.repo import RunbotException from odoo import models, fields, api @@ -382,10 +381,7 @@ class runbot_build(models.Model): def _get_params(self): message = False - try: - message = self.repo_id._git(['show', '-s', self.name]) - except CalledProcessError: - pass # todo remove this try catch and make correct patch for _git + message = self.repo_id._git(['show', '-s', self.name]) params = defaultdict(lambda: defaultdict(str)) if message: regex = re.compile(r'^[\t ]*Runbot-dependency: ([A-Za-z0-9\-_]+/[A-Za-z0-9\-_]+):([0-9A-Fa-f\-]*) *(#.*)?$', re.M) # dep:repo:hash #comment diff --git a/runbot/models/build_config.py b/runbot/models/build_config.py index c20f97b5..6590545e 100644 --- a/runbot/models/build_config.py +++ b/runbot/models/build_config.py @@ -1,11 +1,10 @@ import base64 import glob import logging -import os import re import shlex import time -from ..common import now, grep, time2str, rfind, Commit, s2human +from ..common import now, grep, time2str, rfind, Commit, s2human, os from ..container import docker_run, docker_get_gateway_ip, Command from odoo import models, fields, api from odoo.exceptions import UserError, ValidationError diff --git a/runbot/models/repo.py b/runbot/models/repo.py index 8f2ce150..c6781f9d 100644 --- a/runbot/models/repo.py +++ b/runbot/models/repo.py @@ -3,7 +3,6 @@ import datetime import dateutil import json import logging -import os import random import re import requests @@ -18,7 +17,7 @@ from odoo.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT from odoo import models, fields, api, registry from odoo.modules.module import get_module_resource from odoo.tools import config -from ..common import fqdn, dt2time, Commit, dest_reg +from ..common import fqdn, dt2time, Commit, dest_reg, os from ..container import docker_ps, docker_stop from psycopg2.extensions import TransactionRollbackError _logger = logging.getLogger(__name__) @@ -538,6 +537,7 @@ class runbot_repo(models.Model): """This method have to be called from a dedicated cron on a runbot in charge of orchestration. """ + if hostname != fqdn(): return 'Not for me' @@ -558,8 +558,10 @@ class runbot_repo(models.Model): """ This method have to be called from a dedicated cron created on each runbot instance. """ + if hostname != fqdn(): return 'Not for me' + host = self.env['runbot.host']._get_current() host.set_psql_conn_count() host.last_start_loop = fields.Datetime.now() diff --git a/runbot/tests/__init__.py b/runbot/tests/__init__.py index 90852177..ce252503 100644 --- a/runbot/tests/__init__.py +++ b/runbot/tests/__init__.py @@ -1,3 +1,4 @@ +from . import common from . import test_repo from . import test_build_error from . import test_branch diff --git a/runbot/tests/common.py b/runbot/tests/common.py new file mode 100644 index 00000000..85c966df --- /dev/null +++ b/runbot/tests/common.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +from odoo.tests.common import TransactionCase +from unittest.mock import patch + +class Dummy(): + ... + + +class RunbotCase(TransactionCase): + + def setUp(self): + super(RunbotCase, self).setUp() + + self.Build = self.env['runbot.build'] + self.Repo = self.env['runbot.repo'] + self.Branch = self.env['runbot.branch'] + + self.patchers = {} + + def git_side_effect(cmd): + if cmd[:2] == ['show', '-s'] or cmd[:3] == ['show', '--pretty="%H -- %s"', '-s']: + return 'commit message for %s' % cmd[-1] + if cmd[:2] == ['cat-file', '-e']: + return True + else: + print('Unsupported mock command %s' % cmd) + + self.start_patcher('git_patcher', 'odoo.addons.runbot.models.repo.runbot_repo._git', side_effect=git_side_effect) + self.start_patcher('fqdn_patcher', 'odoo.addons.runbot.common.socket.getfqdn', 'host.runbot.com') + self.start_patcher('find_patcher', 'odoo.addons.runbot.common.find', 0) + self.start_patcher('github_patcher', 'odoo.addons.runbot.models.repo.runbot_repo._github', {}) + self.start_patcher('is_on_remote_patcher', 'odoo.addons.runbot.models.branch.runbot_branch._is_on_remote', True) + self.start_patcher('repo_root_patcher', 'odoo.addons.runbot.models.repo.runbot_repo._root', '/tmp/runbot_test/static') + self.start_patcher('makedirs', 'odoo.addons.runbot.common.os.makedirs', True) + self.start_patcher('mkdir', 'odoo.addons.runbot.common.os.mkdir', True) + self.start_patcher('local_pgadmin_cursor', 'odoo.addons.runbot.common.local_pgadmin_cursor', False) # avoid to create databases + self.start_patcher('isdir', 'odoo.addons.runbot.common.os.path.isdir', True) + self.start_patcher('isfile', 'odoo.addons.runbot.common.os.path.isfile', True) + self.start_patcher('docker_run', 'odoo.addons.runbot.models.build_config.docker_run') + self.start_patcher('docker_ps', 'odoo.addons.runbot.models.repo.docker_ps', []) + self.start_patcher('docker_stop', 'odoo.addons.runbot.models.repo.docker_stop') + + def start_patcher(self, patcher_name, patcher_path, return_value=Dummy, side_effect=Dummy): + patcher = patch(patcher_path) + if not hasattr(patcher, 'is_local'): + res = patcher.start() + self.addCleanup(patcher.stop) + self.patchers[patcher_name] = res + if side_effect != Dummy: + res.side_effect = side_effect + elif return_value != Dummy: + res.return_value = return_value + + def create_build(self, vals): + return self.Build.create(vals) diff --git a/runbot/tests/test_branch.py b/runbot/tests/test_branch.py index 4440b04f..91d7b862 100644 --- a/runbot/tests/test_branch.py +++ b/runbot/tests/test_branch.py @@ -1,8 +1,9 @@ # -*- coding: utf-8 -*- from unittest.mock import patch from odoo.tests import common +from .common import RunbotCase -class Test_Branch(common.TransactionCase): +class Test_Branch(RunbotCase): def setUp(self): super(Test_Branch, self).setUp() @@ -24,8 +25,8 @@ class Test_Branch(common.TransactionCase): self.assertEqual(branch.branch_url, 'https://example.com/foo/bar/tree/master') self.assertEqual(branch.config_id, self.env.ref('runbot.runbot_build_config_default')) - @patch('odoo.addons.runbot.models.repo.runbot_repo._github') - def test_pull_request(self, mock_github): + def test_pull_request(self): + mock_github = self.patchers['github_patcher'] mock_github.return_value = { 'head' : {'label': 'foo-dev:bar_branch'}, 'base' : {'ref': 'master'}, diff --git a/runbot/tests/test_build.py b/runbot/tests/test_build.py index bbeafb48..546c2aed 100644 --- a/runbot/tests/test_build.py +++ b/runbot/tests/test_build.py @@ -1,7 +1,9 @@ # -*- coding: utf-8 -*- +from collections import defaultdict from unittest.mock import patch from odoo.tests import common +from .common import RunbotCase def rev_parse(repo, branch_name): """ @@ -12,13 +14,11 @@ def rev_parse(repo, branch_name): head_hash = 'rp_%s_%s_head' % (repo.name.split(':')[1], branch_name.split('/')[-1]) return head_hash -class Test_Build(common.TransactionCase): +class Test_Build(RunbotCase): def setUp(self): super(Test_Build, self).setUp() - self.Repo = self.env['runbot.repo'] self.repo = self.Repo.create({'name': 'bla@example.com:foo/bar', 'server_files': 'server.py', 'addons_paths': 'addons,core/addons'}) - self.Branch = self.env['runbot.branch'] self.branch = self.Branch.create({ 'repo_id': self.repo.id, 'name': 'refs/heads/master' @@ -31,11 +31,9 @@ class Test_Build(common.TransactionCase): 'repo_id': self.repo.id, 'name': 'refs/heads/11.0' }) - self.Build = self.env['runbot.build'] - @patch('odoo.addons.runbot.models.build.fqdn') - def test_base_fields(self, mock_fqdn): - build = self.Build.create({ + def test_base_fields(self): + build = self.create_build({ 'branch_id': self.branch.id, 'name': 'd0d0caca0000ffffffffffffffffffffffffffff', 'port': '1234', @@ -47,14 +45,14 @@ class Test_Build(common.TransactionCase): self.assertEqual(build.dest, '%05d-master-deadbe' % build.id) # Test domain compute with fqdn and ir.config_parameter - mock_fqdn.return_value = 'runbot98.nowhere.org' + self.patchers['fqdn_patcher'].return_value = 'runbot98.nowhere.org' self.env['ir.config_parameter'].sudo().set_param('runbot.runbot_domain', False) self.assertEqual(build.domain, 'runbot98.nowhere.org:1234') self.env['ir.config_parameter'].set_param('runbot.runbot_domain', 'runbot99.example.org') build._compute_domain() self.assertEqual(build.domain, 'runbot99.example.org:1234') - other = self.Build.create({ + other = self.create_build({ 'branch_id': self.branch.id, 'name': 'd0d0caca0000ffffffffffffffffffffffffffff', 'port': '5678', @@ -71,11 +69,9 @@ class Test_Build(common.TransactionCase): builds.write({'local_state': 'duplicate'}) @patch('odoo.addons.runbot.models.build.runbot_build._get_repo_available_modules') - @patch('odoo.addons.runbot.models.build.runbot_build._get_params') - @patch('odoo.addons.runbot.models.build.fqdn') - def test_filter_modules(self, mock_fqdn, mock_get_params, mock_get_repo_mods): + def test_filter_modules(self, mock_get_repo_mods): """ test module filtering """ - build = self.Build.create({ + build = self.create_build({ 'branch_id': self.branch.id, 'name': 'd0d0caca0000ffffffffffffffffffffffffffff', 'port': '1234', @@ -99,15 +95,11 @@ class Test_Build(common.TransactionCase): modules_to_test = build._get_modules_to_test(self, modules_patterns='*, -hw_*, hw_explicit') self.assertEqual(modules_to_test, sorted(['good_module', 'bad_module', 'other_good', 'l10n_be', 'hwgood', 'hw_explicit', 'other_mod_1', 'other_mod_2'])) - @patch('odoo.addons.runbot.models.build.os.path.isfile') - @patch('odoo.addons.runbot.models.build.os.mkdir') - @patch('odoo.addons.runbot.models.build.grep') - def test_build_cmd_log_db(self, mock_grep, mock_mkdir, mock_is_file): + def test_build_cmd_log_db(self, ): """ test that the logdb connection URI is taken from the .odoorc file """ - mock_is_file.return_value = True uri = 'postgres://someone:pass@somewhere.com/db' self.env['ir.config_parameter'].sudo().set_param("runbot.runbot_logdb_uri", uri) - build = self.Build.create({ + build = self.create_build({ 'branch_id': self.branch.id, 'name': 'd0d0caca0000ffffffffffffffffffffffffffff', 'port': '1234', @@ -115,15 +107,9 @@ class Test_Build(common.TransactionCase): cmd = build._cmd(py_version=3) self.assertIn('log-db = %s' % uri, cmd.get_config()) - @patch('odoo.addons.runbot.models.build.os.path.isdir') - @patch('odoo.addons.runbot.models.build.os.path.isfile') - @patch('odoo.addons.runbot.models.build.os.mkdir') - @patch('odoo.addons.runbot.models.build.grep') - def test_build_cmd_server_path_no_dep(self, mock_grep, mock_mkdir, mock_is_file, mock_is_dir): + def test_build_cmd_server_path_no_dep(self): """ test that the server path and addons path """ - mock_is_file.return_value = True - mock_is_dir.return_value = True - build = self.Build.create({ + build = self.create_build({ 'branch_id': self.branch.id, 'name': 'd0d0caca0000ffffffffffffffffffffffffffff', 'port': '1234', @@ -135,19 +121,15 @@ class Test_Build(common.TransactionCase): addons_path_pos = cmd.index('--addons-path') + 1 self.assertEqual(cmd[addons_path_pos], 'bar/addons,bar/core/addons') - @patch('odoo.addons.runbot.models.branch.runbot_branch._is_on_remote') - @patch('odoo.addons.runbot.models.build.os.path.isdir') - @patch('odoo.addons.runbot.models.build.os.path.isfile') - @patch('odoo.addons.runbot.models.build.os.mkdir') - @patch('odoo.addons.runbot.models.build.grep') - def test_build_cmd_server_path_with_dep(self, mock_grep, mock_mkdir, mock_is_file, mock_is_dir, mock_is_on_remote): + def test_build_cmd_server_path_with_dep(self): """ test that the server path and addons path """ def is_file(file): self.assertIn(file, [ '/tmp/runbot_test/static/sources/bar-ent/d0d0caca0000ffffffffffffffffffffffffffff/requirements.txt', '/tmp/runbot_test/static/sources/bar/dfdfcfcf0000ffffffffffffffffffffffffffff/requirements.txt', - '/tmp/runbot_test/static/sources/bar/dfdfcfcf0000ffffffffffffffffffffffffffff/server.py' + '/tmp/runbot_test/static/sources/bar/dfdfcfcf0000ffffffffffffffffffffffffffff/server.py', + '/tmp/runbot_test/static/sources/bar/dfdfcfcf0000ffffffffffffffffffffffffffff/openerp/tools/config.py' ]) if file == '/tmp/runbot_test/static/sources/bar-ent/d0d0caca0000ffffffffffffffffffffffffffff/requirements.txt': return False @@ -162,9 +144,9 @@ class Test_Build(common.TransactionCase): self.assertTrue(any([path in file for path in paths])) # checking that addons path existence check looks ok return True - mock_is_file.side_effect = is_file - mock_is_dir.side_effect = is_dir - mock_is_on_remote.return_value = True + self.patchers['isfile'].side_effect = is_file + self.patchers['isdir'].side_effect = is_dir + repo_ent = self.env['runbot.repo'].create({ 'name': 'bla@example.com:foo/bar-ent', 'server_files': '', @@ -181,7 +163,7 @@ class Test_Build(common.TransactionCase): return 'dfdfcfcf0000ffffffffffffffffffffffffffff' with patch('odoo.addons.runbot.models.repo.runbot_repo._git_rev_parse', new=rev_parse): - build = self.Build.create({ + build = self.create_build({ 'branch_id': enterprise_branch.id, 'name': 'd0d0caca0000ffffffffffffffffffffffffffff', 'port': '1234', @@ -193,26 +175,21 @@ class Test_Build(common.TransactionCase): self.assertEqual('bar/server.py', cmd[1]) self.assertEqual('python3', cmd[0]) - @patch('odoo.addons.runbot.models.branch.runbot_branch._is_on_remote') - @patch('odoo.addons.runbot.models.build.os.path.isdir') - @patch('odoo.addons.runbot.models.build.os.path.isfile') - @patch('odoo.addons.runbot.models.build.os.mkdir') - @patch('odoo.addons.runbot.models.build.grep') - def test_build_cmd_server_path_with_dep_collision(self, mock_grep, mock_mkdir, mock_is_file, mock_is_dir, mock_is_on_remote): + def test_build_cmd_server_path_with_dep_collision(self): """ test that the server path and addons path """ def is_file(file): self.assertIn(file, [ '/tmp/runbot_test/static/sources/bar/dfdfcfcf0000ffffffffffffffffffffffffffff/requirements.txt', '/tmp/runbot_test/static/sources/bar/d0d0caca0000ffffffffffffffffffffffffffff/requirements.txt', - '/tmp/runbot_test/static/sources/bar/dfdfcfcf0000ffffffffffffffffffffffffffff/server.py']) + '/tmp/runbot_test/static/sources/bar/dfdfcfcf0000ffffffffffffffffffffffffffff/server.py', + '/tmp/runbot_test/static/sources/bar/dfdfcfcf0000ffffffffffffffffffffffffffff/openerp/tools/config.py' + ]) if file == '/tmp/runbot_test/static/sources/bar/dfdfcfcf0000ffffffffffffffffffffffffffff/requirements.txt': return False return True - mock_is_file.side_effect = is_file - mock_is_dir.return_value = True - mock_is_on_remote.return_value = True + self.patchers['isfile'].side_effect = is_file repo_ent = self.env['runbot.repo'].create({ 'name': 'bla@example.com:foo-ent/bar', 'server_files': '', @@ -229,7 +206,7 @@ class Test_Build(common.TransactionCase): return 'dfdfcfcf0000ffffffffffffffffffffffffffff' with patch('odoo.addons.runbot.models.repo.runbot_repo._git_rev_parse', new=rev_parse): - build = self.Build.create({ + build = self.create_build({ 'branch_id': enterprise_branch.id, 'name': 'd0d0caca0000ffffffffffffffffffffffffffff', 'port': '1234', @@ -243,7 +220,7 @@ class Test_Build(common.TransactionCase): def test_build_config_from_branch_default(self): """test build config_id is computed from branch default config_id""" - build = self.Build.create({ + build = self.create_build({ 'branch_id': self.branch.id, 'name': 'd0d0caca0000ffffffffffffffffffffffffffff', }) @@ -252,7 +229,7 @@ class Test_Build(common.TransactionCase): def test_build_config_from_branch_testing(self): """test build config_id is computed from branch""" self.branch.config_id = self.env.ref('runbot.runbot_build_config_default_no_run') - build = self.Build.create({ + build = self.create_build({ 'branch_id': self.branch.id, 'name': 'd0d0caca0000ffffffffffffffffffffffffffff', }) @@ -261,7 +238,7 @@ class Test_Build(common.TransactionCase): def test_build_from_branch_no_build(self): """test build is not even created when branch no_build is True""" self.branch.no_build = True - build = self.Build.create({ + build = self.create_build({ 'branch_id': self.branch.id, 'name': 'd0d0caca0000ffffffffffffffffffffffffffff', }) @@ -270,7 +247,7 @@ class Test_Build(common.TransactionCase): def test_build_config_can_be_set(self): """test build config_id can be set to something different than the one on the branch""" self.branch.config_id = self.env.ref('runbot.runbot_build_config_default') - build = self.Build.create({ + build = self.create_build({ 'branch_id': self.branch.id, 'name': 'd0d0caca0000ffffffffffffffffffffffffffff', 'config_id': self.env.ref('runbot.runbot_build_config_default_no_run').id @@ -280,7 +257,7 @@ class Test_Build(common.TransactionCase): @patch('odoo.addons.runbot.models.build._logger') def test_build_skip(self, mock_logger): """test build is skipped""" - build = self.Build.create({ + build = self.create_build({ 'branch_id': self.branch.id, 'name': 'd0d0caca0000ffffffffffffffffffffffffffff', 'port': '1234', @@ -289,7 +266,7 @@ class Test_Build(common.TransactionCase): self.assertEqual(build.local_state, 'done') self.assertEqual(build.local_result, 'skipped') - other_build = self.Build.create({ + other_build = self.create_build({ 'branch_id': self.branch.id, 'name': 'deadbeef0000ffffffffffffffffffffffffffff', 'port': '1234', @@ -302,13 +279,12 @@ class Test_Build(common.TransactionCase): def test_ask_kill_duplicate(self): """ Test that the _ask_kill method works on duplicate""" - #mock_is_on_remote.return_value = True - build1 = self.Build.create({ + build1 = self.create_build({ 'branch_id': self.branch_10.id, 'name': 'd0d0caca0000ffffffffffffffffffffffffffff', }) - build2 = self.Build.create({ + build2 = self.create_build({ 'branch_id': self.branch_10.id, 'name': 'd0d0caca0000ffffffffffffffffffffffffffff', }) @@ -320,30 +296,30 @@ class Test_Build(common.TransactionCase): self.assertEqual(build1.local_result, 'skipped', 'A killed pending duplicate build should mark the real build as skipped') def test_children(self): - build1 = self.Build.create({ + build1 = self.create_build({ 'branch_id': self.branch_10.id, 'name': 'd0d0caca0000ffffffffffffffffffffffffffff', }) - build1_1 = self.Build.create({ + build1_1 = self.create_build({ 'branch_id': self.branch_10.id, 'name': 'd0d0caca0000ffffffffffffffffffffffffffff', 'parent_id': build1.id, 'hidden': True, 'extra_params': '2', # avoid duplicate }) - build1_2 = self.Build.create({ + build1_2 = self.create_build({ 'branch_id': self.branch_10.id, 'name': 'd0d0caca0000ffffffffffffffffffffffffffff', 'parent_id': build1.id, 'extra_params': '3', }) - build1_1_1 = self.Build.create({ + build1_1_1 = self.create_build({ 'branch_id': self.branch_10.id, 'name': 'd0d0caca0000ffffffffffffffffffffffffffff', 'parent_id': build1_1.id, 'extra_params': '4', }) - build1_1_2 = self.Build.create({ + build1_1_2 = self.create_build({ 'branch_id': self.branch_10.id, 'name': 'd0d0caca0000ffffffffffffffffffffffffffff', 'parent_id': build1_1.id, @@ -408,17 +384,17 @@ class Test_Build(common.TransactionCase): assert_state(0, 0, 0, 'done', build1_1_2) def test_duplicate_childrens(self): - build_old = self.Build.create({ + build_old = self.create_build({ 'branch_id': self.branch_10.id, 'name': 'd0d0caca0000ffffffffffffffffffffffffffff', 'extra_params': '0', }) - build_parent = self.Build.create({ + build_parent = self.create_build({ 'branch_id': self.branch_10.id, 'name': 'd0d0caca0000ffffffffffffffffffffffffffff', 'extra_params': '1', }) - build_child = self.Build.create({ + build_child = self.create_build({ 'branch_id': self.branch_10.id, 'name': 'd0d0caca0000ffffffffffffffffffffffffffff', 'parent_id': build_parent.id, @@ -431,7 +407,8 @@ class Test_Build(common.TransactionCase): self.assertEqual(build_parent.nb_testing, 0) self.assertEqual(build_parent.global_state, 'done') -class TestClosestBranch(common.TransactionCase): + +class TestClosestBranch(RunbotCase): def branch_description(self, branch): branch_type = 'pull' if 'pull' in branch.name else 'branch' @@ -453,7 +430,7 @@ class TestClosestBranch(common.TransactionCase): } for b1, b2 in [(branch1, branch2), (branch2, branch1)]: hash = '%s%s' % (b1.name, b2.name) - build1 = self.Build.create({ + build1 = self.create_build({ 'branch_id': b1.id, 'name': hash, }) @@ -461,7 +438,7 @@ class TestClosestBranch(common.TransactionCase): if b1_closest: self.assertClosest(b1, closest[b1]) - build2 = self.Build.create({ + build2 = self.create_build({ 'branch_id': b2.id, 'name': hash, }) @@ -533,10 +510,10 @@ class TestClosestBranch(common.TransactionCase): self.Build = self.env['runbot.build'] - @patch('odoo.addons.runbot.models.repo.runbot_repo._github') - def test_pr_is_duplicate(self, mock_github): + def test_pr_is_duplicate(self): """ test PR is a duplicate of a dev branch build """ + mock_github = self.patchers['github_patcher'] mock_github.return_value = { 'head': {'label': 'odoo-dev:10.0-fix-thing-moc'}, 'base': {'ref': '10.0'}, @@ -553,10 +530,8 @@ class TestClosestBranch(common.TransactionCase): }) self.assertDuplicate(dev_branch, pr) - @patch('odoo.addons.runbot.models.branch.runbot_branch._is_on_remote') - def test_closest_branch_01(self, mock_is_on_remote): + def test_closest_branch_01(self): """ test find a matching branch in a target repo based on branch name """ - mock_is_on_remote.return_value = True self.Branch.create({ 'repo_id': self.community_dev_repo.id, @@ -569,10 +544,10 @@ class TestClosestBranch(common.TransactionCase): self.assertEqual((addons_branch, 'exact'), addons_branch._get_closest_branch(self.enterprise_dev_repo.id)) - @patch('odoo.addons.runbot.models.repo.runbot_repo._github') - def test_closest_branch_02(self, mock_github): - + def test_closest_branch_02(self): """ test find two matching PR having the same head name """ + + mock_github = self.patchers['github_patcher'] mock_github.return_value = { # "head label" is the repo:branch where the PR comes from # "base ref" is the target of the PR @@ -601,13 +576,11 @@ class TestClosestBranch(common.TransactionCase): }) self.assertEqual((community_branch, 'exact PR'), enterprise_pr._get_closest_branch(self.community_repo.id)) - @patch('odoo.addons.runbot.models.repo.runbot_repo._github') - @patch('odoo.addons.runbot.models.branch.runbot_branch._is_on_remote') - def test_closest_branch_02_improved(self, mock_is_on_remote, mock_github): + def test_closest_branch_02_improved(self): """ test that a PR in enterprise with a matching PR in Community uses the matching one""" - mock_is_on_remote.return_value = True + mock_github = self.patchers['github_patcher'] com_dev_branch = self.Branch.create({ 'repo_id': self.community_dev_repo.id, @@ -656,22 +629,18 @@ class TestClosestBranch(common.TransactionCase): (com_dev_branch, 'exact PR') ) - @patch('odoo.addons.runbot.models.branch.runbot_branch._is_on_remote') - def test_closest_branch_03(self, mock_is_on_remote): + def test_closest_branch_03(self): """ test find a branch based on dashed prefix""" - mock_is_on_remote.return_value = True addons_branch = self.Branch.create({ 'repo_id': self.enterprise_dev_repo.id, 'name': 'refs/heads/10.0-fix-blah-blah-moc' }) self.assertEqual((self.branch_odoo_10, 'prefix'), addons_branch._get_closest_branch(self.community_repo.id)) - @patch('odoo.addons.runbot.models.repo.runbot_repo._github') - @patch('odoo.addons.runbot.models.branch.runbot_branch._is_on_remote') - def test_closest_branch_03_05(self, mock_is_on_remote, mock_github): + def test_closest_branch_03_05(self): """ test that a PR in enterprise without a matching PR in Community and no branch in community""" - mock_is_on_remote.return_value = True + mock_github = self.patchers['github_patcher'] # comm_repo = self.repo # self.repo.write({'token': 1}) @@ -715,12 +684,10 @@ class TestClosestBranch(common.TransactionCase): (com_branch, 'prefix'), ) - @patch('odoo.addons.runbot.models.repo.runbot_repo._github') - @patch('odoo.addons.runbot.models.branch.runbot_branch._is_on_remote') - def test_closest_branch_04(self, mock_is_on_remote, mock_github): + def test_closest_branch_04(self): """ test that a PR in enterprise without a matching PR in Community uses the corresponding exact branch in community""" - mock_is_on_remote.return_value = True + mock_github = self.patchers['github_patcher'] com_dev_branch = self.Branch.create({ 'repo_id': self.community_dev_repo.id, @@ -753,9 +720,9 @@ class TestClosestBranch(common.TransactionCase): (com_dev_branch, 'no PR') ) - @patch('odoo.addons.runbot.models.repo.runbot_repo._github') - def test_closest_branch_05(self, mock_github): + def test_closest_branch_05(self): """ test last resort value """ + mock_github = self.patchers['github_patcher'] mock_github.return_value = { 'head': {'label': 'foo-dev:bar_branch'}, 'base': {'ref': '10.0'}, @@ -786,10 +753,8 @@ class TestClosestBranch(common.TransactionCase): }) self.assertEqual((self.branch_odoo_master, 'default'), addons_branch._get_closest_branch(self.community_repo.id)) - @patch('odoo.addons.runbot.models.branch.runbot_branch._is_on_remote') - def test_no_duplicate_update(self, mock_is_on_remote): + def test_no_duplicate_update(self): """push a dev branch in enterprise with same head as sticky, but with a matching branch in community""" - mock_is_on_remote.return_value = True community_sticky_branch = self.Branch.create({ 'repo_id': self.community_repo.id, 'name': 'refs/heads/saas-12.2', @@ -811,7 +776,7 @@ class TestClosestBranch(common.TransactionCase): # we shouldn't have duplicate since community_dev_branch exists with patch('odoo.addons.runbot.models.repo.runbot_repo._git_rev_parse', new=rev_parse): # lets create an old enterprise build - self.Build.create({ + self.create_build({ 'branch_id': enterprise_sticky_branch.id, 'name': 'd0d0caca0000ffffffffffffffffffffffffffff', }) @@ -822,9 +787,9 @@ class TestClosestBranch(common.TransactionCase): (community_dev_branch, 'exact'), ) - @patch('odoo.addons.runbot.models.repo.runbot_repo._github') - def test_external_pr_closest_branch(self, mock_github): + def test_external_pr_closest_branch(self): """ test last resort value target_name""" + mock_github = self.patchers['github_patcher'] mock_github.return_value = { 'head': {'label': 'external_repo:11.0-fix'}, 'base': {'ref': '11.0'}, @@ -838,9 +803,9 @@ class TestClosestBranch(common.TransactionCase): closest_branch = enterprise_pr._get_closest_branch(dependency_repo.id) self.assertEqual(enterprise_pr._get_closest_branch(dependency_repo.id), (self.branch_odoo_11, 'pr_target')) - @patch('odoo.addons.runbot.models.repo.runbot_repo._github') - def test_external_pr_with_comunity_pr_closest_branch(self, mock_github): + def test_external_pr_with_comunity_pr_closest_branch(self): """ test matching external pr """ + mock_github = self.patchers['github_patcher'] mock_github.return_value = { 'head': {'label': 'external_dev_repo:11.0-fix'}, 'base': {'ref': '11.0'}, @@ -860,7 +825,7 @@ class TestClosestBranch(common.TransactionCase): 'name': 'refs/pull/123' }) with patch('odoo.addons.runbot.models.repo.runbot_repo._git_rev_parse', new=rev_parse): - build = self.Build.create({ + build = self.create_build({ 'branch_id': enterprise_pr.id, 'name': 'd0d0caca0000ffffffffffffffffffffffffffff', }) diff --git a/runbot/tests/test_build_config_step.py b/runbot/tests/test_build_config_step.py index 9c2a0a22..f3b414cf 100644 --- a/runbot/tests/test_build_config_step.py +++ b/runbot/tests/test_build_config_step.py @@ -1,15 +1,13 @@ # -*- coding: utf-8 -*- from unittest.mock import patch from odoo.tests import common +from .common import RunbotCase - -class TestBuildConfigStep(common.TransactionCase): +class TestBuildConfigStep(RunbotCase): def setUp(self): super(TestBuildConfigStep, self).setUp() - self.Repo = self.env['runbot.repo'] - self.repo = self.Repo.create({'name': 'bla@example.com:foo/bar'}) - self.Branch = self.env['runbot.branch'] + self.repo = self.Repo.create({'name': 'bla@example.com:foo/bar', 'server_files': 'server.py'}) self.branch = self.Branch.create({ 'repo_id': self.repo.id, 'name': 'refs/heads/master' @@ -31,6 +29,9 @@ class TestBuildConfigStep(common.TransactionCase): 'name': 'd0d0caca0000ffffffffffffffffffffffffffff', 'port': '1234', }) + self.start_patcher('_local_pg_createdb', 'odoo.addons.runbot.models.build.runbot_build._local_pg_createdb', True) + self.start_patcher('_get_py_version', 'odoo.addons.runbot.models.build.runbot_build._get_py_version', 3) + def test_config_step_create_results(self): """ Test child builds are taken into account""" @@ -81,50 +82,48 @@ class TestBuildConfigStep(common.TransactionCase): self.assertFalse(self.parent_build.global_result) - - @patch('odoo.addons.runbot.models.build.runbot_build._local_pg_createdb') - @patch('odoo.addons.runbot.models.build.runbot_build._get_server_info') - @patch('odoo.addons.runbot.models.build.runbot_build._get_addons_path') - @patch('odoo.addons.runbot.models.build.runbot_build._get_py_version') - @patch('odoo.addons.runbot.models.build.runbot_build._server') @patch('odoo.addons.runbot.models.build.runbot_build._checkout') - @patch('odoo.addons.runbot.models.build_config.docker_run') - def test_coverage(self, mock_docker_run, mock_checkout, mock_server, mock_get_py_version, mock_get_addons_path, mock_get_server_info, mock_local_pg_createdb): + def test_coverage(self, mock_checkout): config_step = self.ConfigStep.create({ 'name': 'coverage', 'job_type': 'install_odoo', 'coverage': True }) - mock_checkout.return_value = {} - mock_server.return_value = 'bar' - mock_get_py_version.return_value = '3' - mock_get_addons_path.return_value = ['bar/addons'] - mock_get_server_info.return_value = (self.parent_build._get_all_commit()[0], 'server.py') - mock_local_pg_createdb.return_value = True - - def docker_run(cmd, log_path, build_dir, *args, **kwargs): - cmds = cmd.split(' && ') - self.assertEqual(cmds[0], 'sudo pip3 install -r bar/requirements.txt') - self.assertEqual(cmds[1].split(' bar/server.py')[0], 'python3 -m coverage run --branch --source /data/build --omit *__manifest__.py') - self.assertEqual(cmds[2].split(' ; ')[0], 'python3 -m coverage html -d /data/build/coverage --ignore-errors') - self.assertEqual(cmds[2].split(' ; ')[1], 'pg_dump %s-coverage > /data/build/logs/%s-coverage//dump.sql' % (self.parent_build.dest, self.parent_build.dest)) + def docker_run(cmd, log_path, *args, **kwargs): + cmds = cmd.build().split(' && ') + dest = self.parent_build.dest + self.assertEqual(cmd.pres, [['sudo', 'pip3', 'install', '-r', 'bar/requirements.txt']]) + self.assertEqual(cmd.cmd[:10], ['python3', '-m', 'coverage', 'run', '--branch', '--source', '/data/build', '--omit', '*__manifest__.py', 'bar/server.py']) + #['bar/server.py', '--addons-path', 'bar', '--no-xmlrpcs', '--no-netrpc', '-d', '08732-master-d0d0ca-coverage', '--test-enable', '--stop-after-init', '--log-level=test', '--max-cron-threads=0'] + self.assertEqual(cmd.posts, [['python3', '-m', 'coverage', 'html', '-d', '/data/build/coverage', '--ignore-errors']]) self.assertEqual(log_path, 'dev/null/logpath') - mock_docker_run.side_effect = docker_run - + self.patchers['docker_run'].side_effect = docker_run + + config_step._run_odoo_install(self.parent_build, 'dev/null/logpath') + + @patch('odoo.addons.runbot.models.build.runbot_build._checkout') + def test_dump(self, mock_checkout): + config_step = self.ConfigStep.create({ + 'name': 'all', + 'job_type': 'install_odoo', + }) + def docker_run(cmd, log_path, *args, **kwargs): + dest = self.parent_build.dest + self.assertEqual(cmd.cmd[:2], ['python3', 'bar/server.py']) + self.assertEqual(cmd.finals[0], ['pg_dump', '%s-all' % dest, '>', '/data/build/logs/%s-all//dump.sql' % dest]) + self.assertEqual(cmd.finals[1], ['cp', '-r', '/data/build/datadir/filestore/%s-all' % dest, '/data/build/logs/%s-all//filestore/' % dest]) + self.assertEqual(cmd.finals[2], ['cd', '/data/build/logs/%s-all/' % dest, '&&', 'zip', '-rmq9', '/data/build/logs/%s-all.zip' % dest, '*']) + self.assertEqual(log_path, 'dev/null/logpath') + + self.patchers['docker_run'].side_effect = docker_run + config_step._run_odoo_install(self.parent_build, 'dev/null/logpath') - @patch('odoo.addons.runbot.models.build.runbot_build._local_pg_createdb') - @patch('odoo.addons.runbot.models.build.runbot_build._get_server_info') - @patch('odoo.addons.runbot.models.build.runbot_build._get_addons_path') - @patch('odoo.addons.runbot.models.build.runbot_build._get_py_version') - @patch('odoo.addons.runbot.models.build.runbot_build._server') @patch('odoo.addons.runbot.models.build.runbot_build._checkout') - @patch('odoo.addons.runbot.models.build_config.docker_run') - @patch('odoo.addons.runbot.models.build_config.grep') - def test_install_tags(self, mock_grep, mock_docker_run, mock_checkout, mock_server, mock_get_py_version, mock_get_addons_path, mock_get_server_info, mock_local_pg_createdb): + def test_install_tags(self, mock_checkout): config_step = self.ConfigStep.create({ 'name': 'all', 'job_type': 'install_odoo', @@ -137,29 +136,22 @@ class TestBuildConfigStep(common.TransactionCase): 'test_tags': ':otherclass.othertest' }) - mock_checkout.return_value = {} - mock_server.return_value = 'bar' - mock_get_py_version.return_value = '3' - mock_get_addons_path.return_value = ['bar/addons'] - mock_get_server_info.return_value = (self.parent_build._get_all_commit()[0], 'server.py') - mock_local_pg_createdb.return_value = True - mock_grep.return_value = True - - def docker_run(cmd, log_path, build_dir, *args, **kwargs): - cmds = cmd.split(' && ') + def docker_run(cmd, *args, **kwargs): + cmds = cmd.build().split(' && ') self.assertEqual(cmds[1].split(' bar/server.py')[0], 'python3') tags = cmds[1].split('--test-tags ')[1].split(' ')[0] self.assertEqual(tags, '/module,:class.method') - mock_docker_run.side_effect = docker_run + self.patchers['docker_run'].side_effect = docker_run config_step._run_odoo_install(self.parent_build, 'dev/null/logpath') - config_step.enable_auto_tags =True - def docker_run(cmd, log_path, build_dir, *args, **kwargs): - cmds = cmd.split(' && ') + config_step.enable_auto_tags = True + + def docker_run2(cmd, *args, **kwargs): + cmds = cmd.build().split(' && ') self.assertEqual(cmds[1].split(' bar/server.py')[0], 'python3') tags = cmds[1].split('--test-tags ')[1].split(' ')[0] self.assertEqual(tags, '/module,:class.method,-:otherclass.othertest') - mock_docker_run.side_effect = docker_run + self.patchers['docker_run'].side_effect = docker_run2 config_step._run_odoo_install(self.parent_build, 'dev/null/logpath') diff --git a/runbot/tests/test_build_error.py b/runbot/tests/test_build_error.py index 2691e492..ff2059b1 100644 --- a/runbot/tests/test_build_error.py +++ b/runbot/tests/test_build_error.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from unittest.mock import patch from odoo.tests import common +from .common import RunbotCase RTE_ERROR = """FAIL: TestUiTranslate.test_admin_tour_rte_translator Traceback (most recent call last): @@ -14,9 +15,10 @@ AssertionError: The test code "odoo.startTour('rte_translator')" failed Tour rte_translator failed at step click language dropdown (trigger: .js_language_selector .dropdown-toggle) """ -class TestBuildError(common.TransactionCase): - def create_build(self, vals): +class TestBuildError(RunbotCase): + + def create_test_build(self, vals): create_vals = { 'branch_id': self.branch.id, 'name': 'deadbeaf0000ffffffffffffffffffffffffffff', @@ -24,11 +26,10 @@ class TestBuildError(common.TransactionCase): 'local_result': 'ok' } create_vals.update(vals) - return self.Build.create(create_vals) - + return self.create_build(create_vals) - @patch('odoo.addons.runbot.models.build.runbot_build._get_params') - def setUp(self, mock_get_params): + + def setUp(self): super(TestBuildError, self).setUp() repo = self.env['runbot.repo'].create({'name': 'bla@example.com:foo/bar'}) self.branch = self.env['runbot.branch'].create({ @@ -36,14 +37,12 @@ class TestBuildError(common.TransactionCase): 'name': 'refs/heads/master' }) - self.Build = self.env['runbot.build'] self.BuildError = self.env['runbot.build.error'] - @patch('odoo.addons.runbot.models.build.runbot_build._get_params') - def test_build_scan(self, mock_get_params): + def test_build_scan(self): IrLog = self.env['ir.logging'] - ko_build = self.create_build({'local_result': 'ko'}) - ok_build = self.create_build({'local_result': 'ok'}) + ko_build = self.create_test_build({'local_result': 'ko'}) + ok_build = self.create_test_build({'local_result': 'ok'}) log = {'message': RTE_ERROR, 'build_id': ko_build.id, @@ -66,24 +65,24 @@ class TestBuildError(common.TransactionCase): self.assertFalse(self.BuildError.search([('build_ids','in', [ok_build.id])]), 'A successful build should not associated to a runbot.build.error') # Test that build with same error is added to the errors - ko_build_same_error = self.create_build({'local_result': 'ko'}) + ko_build_same_error = self.create_test_build({'local_result': 'ko'}) log.update({'build_id': ko_build_same_error.id}) IrLog.create(log) ko_build_same_error._parse_logs() self.assertIn(ko_build_same_error, build_error.build_ids, 'The parsed build should be added to the existing runbot.build.error') - + # Test that line numbers does not interfere with error recognition - ko_build_diff_number = self.create_build({'local_result': 'ko'}) + ko_build_diff_number = self.create_test_build({'local_result': 'ko'}) rte_diff_numbers = RTE_ERROR.replace('89','100').replace('1062','1000').replace('1046', '4610') log.update({'build_id': ko_build_diff_number.id, 'message': rte_diff_numbers}) IrLog.create(log) ko_build_diff_number._parse_logs() self.assertIn(ko_build_diff_number, build_error.build_ids, 'The parsed build with different line numbers in error should be added to the runbot.build.error') - + # Test that when an error re-appears after the bug has been fixed, # a new build error is created, with the old one linked build_error.active = False - ko_build_new = self.create_build({'local_result': 'ko'}) + ko_build_new = self.create_test_build({'local_result': 'ko'}) log.update({'build_id': ko_build_new.id}) IrLog.create(log) ko_build_new._parse_logs() @@ -92,10 +91,9 @@ class TestBuildError(common.TransactionCase): self.assertIn(ko_build_new, new_build_error.build_ids, 'The parsed build with a re-apearing error should generate a new runbot.build.error') self.assertIn(build_error, new_build_error.error_history_ids, 'The old error should appear in history') - @patch('odoo.addons.runbot.models.build.runbot_build._get_params') - def test_build_error_links(self, mock_get_params): - build_a = self.create_build({'local_result': 'ko'}) - build_b = self.create_build({'local_result': 'ko'}) + def test_build_error_links(self): + build_a = self.create_test_build({'local_result': 'ko'}) + build_b = self.create_test_build({'local_result': 'ko'}) error_a = self.env['runbot.build.error'].create({ 'content': 'foo', diff --git a/runbot/tests/test_cron.py b/runbot/tests/test_cron.py index 283b2a98..e3edcf26 100644 --- a/runbot/tests/test_cron.py +++ b/runbot/tests/test_cron.py @@ -1,13 +1,14 @@ # -*- coding: utf-8 -*- from unittest.mock import patch from odoo.tests import common +from .common import RunbotCase -class Test_Cron(common.TransactionCase): +class Test_Cron(RunbotCase): def setUp(self): super(Test_Cron, self).setUp() - self.Repo = self.env['runbot.repo'] + self.start_patcher('_get_cron_period', 'odoo.addons.runbot.models.repo.runbot_repo._get_cron_period', 2) @patch('odoo.addons.runbot.models.repo.config.get') def test_cron_period(self, mock_config_get): @@ -19,53 +20,43 @@ class Test_Cron(common.TransactionCase): for i in range(200): self.assertLess(period, 400) - @patch('odoo.addons.runbot.models.repo.fqdn') - def test_crons_returns(self, mock_fqdn): + def test_crons_returns(self): """ test that cron_fetch_and_schedule and _cron_fetch_and_build return directly when called on wrong host """ - mock_fqdn.return_value = 'runboty.foo.com' + ret = self.Repo._cron_fetch_and_schedule('runbotx.foo.com') self.assertEqual(ret, 'Not for me') ret = self.Repo._cron_fetch_and_build('runbotx.foo.com') self.assertEqual(ret, 'Not for me') - @patch('odoo.addons.runbot.models.repo.runbot_repo._get_cron_period') @patch('odoo.addons.runbot.models.repo.runbot_repo._create_pending_builds') @patch('odoo.addons.runbot.models.repo.runbot_repo._update') - @patch('odoo.addons.runbot.models.repo.fqdn') - def test_cron_schedule(self, mock_fqdn, mock_update, mock_create, mock_cron_period): + def test_cron_schedule(self, mock_update, mock_create): """ test that cron_fetch_and_schedule do its work """ - mock_fqdn.return_value = 'runbotx.foo.com' - mock_cron_period.return_value = 2 self.env['ir.config_parameter'].sudo().set_param('runbot.runbot_update_frequency', 1) self.Repo.create({'name': '/path/somewhere/disabled.git', 'mode': 'disabled'}) # create a disabled self.Repo.search([]).write({'mode': 'disabled'}) # disable all depo, in case we have existing ones local_repo = self.Repo.create({'name': '/path/somewhere/rep.git'}) # create active repo - ret = self.Repo._cron_fetch_and_schedule('runbotx.foo.com') + ret = self.Repo._cron_fetch_and_schedule('host.runbot.com') self.assertEqual(None, ret) mock_update.assert_called_with(force=False) mock_create.assert_called_with() - @patch('odoo.addons.runbot.models.host.fqdn') - @patch('odoo.addons.runbot.models.repo.runbot_repo._get_cron_period') @patch('odoo.addons.runbot.models.repo.runbot_repo._reload_nginx') @patch('odoo.addons.runbot.models.repo.runbot_repo._scheduler') - @patch('odoo.addons.runbot.models.repo.fqdn') - def test_cron_build(self, mock_fqdn, mock_scheduler, mock_reload, mock_cron_period, mock_host_fqdn): + def test_cron_build(self, mock_scheduler, mock_reload): """ test that cron_fetch_and_build do its work """ - hostname = 'runbotx.foo.com' - mock_fqdn.return_value = mock_host_fqdn.return_value = hostname - mock_cron_period.return_value = 2 + hostname = 'host.runbot.com' self.env['ir.config_parameter'].sudo().set_param('runbot.runbot_update_frequency', 1) self.Repo.create({'name': '/path/somewhere/disabled.git', 'mode': 'disabled'}) # create a disabled self.Repo.search([]).write({'mode': 'disabled'}) # disable all depo, in case we have existing ones local_repo = self.Repo.create({'name': '/path/somewhere/rep.git'}) # create active repo - ret = self.Repo._cron_fetch_and_build('runbotx.foo.com') + ret = self.Repo._cron_fetch_and_build(hostname) self.assertEqual(None, ret) mock_scheduler.assert_called() self.assertTrue(mock_reload.called) - host = self.env['runbot.host'].search([('name', '=', 'runbotx.foo.com')]) + host = self.env['runbot.host'].search([('name', '=', hostname)]) self.assertEqual(host.name, hostname, 'A new host should have been created') self.assertGreater(host.psql_conn_count, 0, 'A least one connection should exist on the current psql instance') diff --git a/runbot/tests/test_event.py b/runbot/tests/test_event.py index 9001b9e9..c53aad2a 100644 --- a/runbot/tests/test_event.py +++ b/runbot/tests/test_event.py @@ -1,15 +1,14 @@ # -*- coding: utf-8 -*- from unittest.mock import patch from odoo.tests import common +from .common import RunbotCase -class TestIrLogging(common.TransactionCase): +class TestIrLogging(RunbotCase): def setUp(self): super(TestIrLogging, self).setUp() - self.Repo = self.env['runbot.repo'] self.repo = self.Repo.create({'name': 'bla@example.com:foo/bar', 'server_files': 'server.py', 'addons_paths': 'addons,core/addons'}) - self.Branch = self.env['runbot.branch'] self.branch = self.Branch.create({ 'repo_id': self.repo.id, 'name': 'refs/heads/master' @@ -26,10 +25,8 @@ class TestIrLogging(common.TransactionCase): VALUES (NOW() at time zone 'UTC', %s, %s, %s, %s, %s, %s, %s, %s) """, val) - @patch('odoo.addons.runbot.models.build.runbot_build._get_params') - @patch('odoo.addons.runbot.models.build.fqdn') - def test_ir_logging(self, mock_fqdn, mock_get_params): - build = self.Build.create({ + def test_ir_logging(self): + build = self.create_build({ 'branch_id': self.branch.id, 'name': 'd0d0caca0000ffffffffffffffffffffffffffff', 'port': '1234', diff --git a/runbot/tests/test_frontend.py b/runbot/tests/test_frontend.py index 45d1fa65..5c81f706 100644 --- a/runbot/tests/test_frontend.py +++ b/runbot/tests/test_frontend.py @@ -1,12 +1,14 @@ # -*- coding: utf-8 -*- +from collections import defaultdict from itertools import cycle from unittest.mock import patch from werkzeug.wrappers import Response from odoo.tests import common from odoo.addons.runbot.controllers import frontend +from .common import RunbotCase -class Test_Frontend(common.HttpCase): +class Test_Frontend(RunbotCase): def setUp(self): super(Test_Frontend, self).setUp() diff --git a/runbot/tests/test_repo.py b/runbot/tests/test_repo.py index 91d44276..a0031e75 100644 --- a/runbot/tests/test_repo.py +++ b/runbot/tests/test_repo.py @@ -2,22 +2,22 @@ import datetime from unittest import skip from unittest.mock import patch, Mock -from odoo.tests import common +from odoo.tests import common, TransactionCase import logging import odoo import time -import datetime +from .common import RunbotCase _logger = logging.getLogger(__name__) -class Test_Repo(common.TransactionCase): +class Test_Repo(RunbotCase): def setUp(self): super(Test_Repo, self).setUp() - self.Repo = self.env['runbot.repo'] self.commit_list = [] + self.mock_root = self.patchers['repo_root_patcher'] def mock_git_helper(self): """Helper that returns a mock for repo._git()""" @@ -26,9 +26,8 @@ class Test_Repo(common.TransactionCase): return '\n'.join(['\0'.join(commit_fields) for commit_fields in self.commit_list]) return mock_git - @patch('odoo.addons.runbot.models.repo.runbot_repo._root') - def test_base_fields(self, mock_root): - mock_root.return_value = '/tmp/static' + def test_base_fields(self): + self.mock_root.return_value = '/tmp/static' repo = self.Repo.create({'name': 'bla@example.com:foo/bar'}) self.assertEqual(repo.path, '/tmp/static/repo/bla_example.com_foo_bar') @@ -41,13 +40,12 @@ class Test_Repo(common.TransactionCase): local_repo = self.Repo.create({'name': '/path/somewhere/rep.git'}) self.assertEqual(local_repo.short_name, 'somewhere/rep') - @patch('odoo.addons.runbot.models.repo.runbot_repo._root') @patch('odoo.addons.runbot.models.repo.runbot_repo._get_fetch_head_time') - def test_repo_create_pending_builds(self, mock_fetch_head_time, mock_root): + def test_repo_create_pending_builds(self, mock_fetch_head_time): """ Test that when finding new refs in a repo, the missing branches are created and new builds are created in pending state """ - mock_root.return_value = '/tmp/static' + self.mock_root.return_value = '/tmp/static' repo = self.Repo.create({'name': 'bla@example.com:foo/bar'}) # create another repo and branch to ensure there is no mismatch @@ -152,8 +150,7 @@ class Test_Repo(common.TransactionCase): @skip('This test is for performances. It needs a lot of real branches in DB to mean something') - @patch('odoo.addons.runbot.models.repo.runbot_repo._root') - def test_repo_perf_find_new_commits(self, mock_root): + def test_repo_perf_find_new_commits(self): mock_root.return_value = '/tmp/static' repo = self.env['runbot.repo'].search([('name', '=', 'blabla')]) @@ -179,9 +176,11 @@ class Test_Repo(common.TransactionCase): _logger.info('Create pending builds took: %ssec', (time.time() - inserted_time)) + +class Test_Github(TransactionCase): def test_github(self): """ Test different github responses or failures""" - repo = self.Repo.create({'name': 'bla@example.com:foo/foo'}) + repo = self.env['runbot.repo'].create({'name': 'bla@example.com:foo/foo'}) self.assertEqual(repo._github('/repos/:owner/:repo/statuses/abcdef', dict(), ignore_errors=True), None, 'A repo without token should return None') repo.token = 'abc' with patch('odoo.addons.runbot.models.repo.requests.Session') as mock_session: @@ -204,22 +203,23 @@ class Test_Repo(common.TransactionCase): self.assertEqual(2, mock_session.return_value.post.call_count, "_github method should try two times by default") -class Test_Repo_Scheduler(common.TransactionCase): - @patch('odoo.addons.runbot.models.repo.runbot_repo._root') - def setUp(self, mock_root): +class Test_Repo_Scheduler(RunbotCase): + + def setUp(self ): # as the _scheduler method commits, we need to protect the database registry = odoo.registry() registry.enter_test_mode() self.addCleanup(registry.leave_test_mode) super(Test_Repo_Scheduler, self).setUp() + self.fqdn_patcher = patch('odoo.addons.runbot.models.host.fqdn') + mock_root = self.patchers['repo_root_patcher'] mock_root.return_value = '/tmp/static' - self.Repo_model = self.env['runbot.repo'] - self.Branch_model = self.env['runbot.branch'] - self.foo_repo = self.Repo_model.create({'name': 'bla@example.com:foo/bar'}) - self.foo_branch = self.Branch_model.create({ + self.foo_repo = self.Repo.create({'name': 'bla@example.com:foo/bar'}) + + self.foo_branch = self.Branch.create({ 'repo_id': self.foo_repo.id, 'name': 'refs/head/foo' }) @@ -227,26 +227,23 @@ class Test_Repo_Scheduler(common.TransactionCase): @patch('odoo.addons.runbot.models.build.runbot_build._reap') @patch('odoo.addons.runbot.models.build.runbot_build._kill') @patch('odoo.addons.runbot.models.build.runbot_build._schedule') - @patch('odoo.addons.runbot.models.host.fqdn') - def test_repo_scheduler(self, mock_fqdn, mock_schedule, mock_kill, mock_reap): - mock_fqdn.return_value = 'test_host' + def test_repo_scheduler(self, mock_schedule, mock_kill, mock_reap): self.env['ir.config_parameter'].set_param('runbot.runbot_workers', 6) - Build_model = self.env['runbot.build'] builds = [] # create 6 builds that are testing on the host to verify that # workers are not overfilled for build_name in ['a', 'b', 'c', 'd', 'e', 'f']: - build = Build_model.create({ + build = self.create_build({ 'branch_id': self.foo_branch.id, 'name': build_name, 'port': '1234', 'build_type': 'normal', 'local_state': 'testing', - 'host': 'test_host' + 'host': 'host.runbot.com' }) builds.append(build) # now the pending build that should stay unasigned - scheduled_build = Build_model.create({ + scheduled_build = self.create_build({ 'branch_id': self.foo_branch.id, 'name': 'sched_build', 'port': '1234', @@ -255,7 +252,7 @@ class Test_Repo_Scheduler(common.TransactionCase): }) builds.append(scheduled_build) # create the build that should be assigned once a slot is available - build = Build_model.create({ + build = self.create_build({ 'branch_id': self.foo_branch.id, 'name': 'foobuild', 'port': '1234', @@ -272,10 +269,10 @@ class Test_Repo_Scheduler(common.TransactionCase): self.assertFalse(scheduled_build.host) # give some room for the pending build - Build_model.search([('name', '=', 'a')]).write({'local_state': 'done'}) + self.Build.search([('name', '=', 'a')]).write({'local_state': 'done'}) self.foo_repo._scheduler() build.invalidate_cache() scheduled_build.invalidate_cache() - self.assertEqual(build.host, 'test_host') + self.assertEqual(build.host, 'host.runbot.com') self.assertFalse(scheduled_build.host) diff --git a/runbot/tests/test_schedule.py b/runbot/tests/test_schedule.py index 3a056f55..194a5d4f 100644 --- a/runbot/tests/test_schedule.py +++ b/runbot/tests/test_schedule.py @@ -3,9 +3,10 @@ import datetime from unittest.mock import patch from odoo.tests import common import odoo +from .common import RunbotCase -class TestSchedule(common.TransactionCase): +class TestSchedule(RunbotCase): def setUp(self): # entering test mode to avoid that the _schedule method commits records @@ -13,19 +14,16 @@ class TestSchedule(common.TransactionCase): registry.enter_test_mode() self.addCleanup(registry.leave_test_mode) super(TestSchedule, self).setUp() - self.Repo = self.env['runbot.repo'] + self.repo = self.Repo.create({'name': 'bla@example.com:foo/bar'}) - self.Branch = self.env['runbot.branch'] self.branch = self.Branch.create({ 'repo_id': self.repo.id, 'name': 'refs/heads/master' }) - self.Build = self.env['runbot.build'] - @patch('odoo.addons.runbot.models.build.os.makedirs') @patch('odoo.addons.runbot.models.build.os.path.getmtime') @patch('odoo.addons.runbot.models.build.docker_is_running') - def test_schedule_mark_done(self, mock_running, mock_getmtime, mock_makedirs): + def test_schedule_mark_done(self, mock_running, mock_getmtime): """ Test that results are set even when job_30_run is skipped """ job_end_time = datetime.datetime.now() mock_getmtime.return_value = job_end_time.timestamp()