[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.
This commit is contained in:
Christophe Monniez 2019-11-10 17:16:41 +01:00
parent 7d1283492b
commit 73f720a55c
16 changed files with 251 additions and 253 deletions

View File

@ -63,10 +63,14 @@ def now():
def grep(filename, string): def grep(filename, string):
if os.path.isfile(filename): if os.path.isfile(filename):
return open(filename).read().find(string) != -1 return find(filename, string) != -1
return False return False
def find(filename, string):
return open(filename).read().find(string)
def uniq_list(l): def uniq_list(l):
return OrderedDict.fromkeys(l).keys() return OrderedDict.fromkeys(l).keys()

View File

@ -14,7 +14,7 @@ import datetime
import io import io
import json import json
import logging import logging
import os from .common import os
import shutil import shutil
import subprocess import subprocess
import time import time

View File

@ -2,14 +2,13 @@
import fnmatch import fnmatch
import glob import glob
import logging import logging
import os
import pwd import pwd
import re import re
import shutil import shutil
import subprocess import subprocess
import time import time
import datetime 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 ..container import docker_build, docker_stop, docker_is_running, Command
from odoo.addons.runbot.models.repo import RunbotException from odoo.addons.runbot.models.repo import RunbotException
from odoo import models, fields, api from odoo import models, fields, api
@ -382,10 +381,7 @@ class runbot_build(models.Model):
def _get_params(self): def _get_params(self):
message = False message = False
try: message = self.repo_id._git(['show', '-s', self.name])
message = self.repo_id._git(['show', '-s', self.name])
except CalledProcessError:
pass # todo remove this try catch and make correct patch for _git
params = defaultdict(lambda: defaultdict(str)) params = defaultdict(lambda: defaultdict(str))
if message: 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 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

View File

@ -1,11 +1,10 @@
import base64 import base64
import glob import glob
import logging import logging
import os
import re import re
import shlex import shlex
import time 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 ..container import docker_run, docker_get_gateway_ip, Command
from odoo import models, fields, api from odoo import models, fields, api
from odoo.exceptions import UserError, ValidationError from odoo.exceptions import UserError, ValidationError

View File

@ -3,7 +3,6 @@ import datetime
import dateutil import dateutil
import json import json
import logging import logging
import os
import random import random
import re import re
import requests import requests
@ -18,7 +17,7 @@ from odoo.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT
from odoo import models, fields, api, registry from odoo import models, fields, api, registry
from odoo.modules.module import get_module_resource from odoo.modules.module import get_module_resource
from odoo.tools import config 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 ..container import docker_ps, docker_stop
from psycopg2.extensions import TransactionRollbackError from psycopg2.extensions import TransactionRollbackError
_logger = logging.getLogger(__name__) _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 """This method have to be called from a dedicated cron on a runbot
in charge of orchestration. in charge of orchestration.
""" """
if hostname != fqdn(): if hostname != fqdn():
return 'Not for me' return 'Not for me'
@ -558,8 +558,10 @@ class runbot_repo(models.Model):
""" This method have to be called from a dedicated cron """ This method have to be called from a dedicated cron
created on each runbot instance. created on each runbot instance.
""" """
if hostname != fqdn(): if hostname != fqdn():
return 'Not for me' return 'Not for me'
host = self.env['runbot.host']._get_current() host = self.env['runbot.host']._get_current()
host.set_psql_conn_count() host.set_psql_conn_count()
host.last_start_loop = fields.Datetime.now() host.last_start_loop = fields.Datetime.now()

View File

@ -1,3 +1,4 @@
from . import common
from . import test_repo from . import test_repo
from . import test_build_error from . import test_build_error
from . import test_branch from . import test_branch

55
runbot/tests/common.py Normal file
View File

@ -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)

View File

@ -1,8 +1,9 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from unittest.mock import patch from unittest.mock import patch
from odoo.tests import common from odoo.tests import common
from .common import RunbotCase
class Test_Branch(common.TransactionCase): class Test_Branch(RunbotCase):
def setUp(self): def setUp(self):
super(Test_Branch, self).setUp() 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.branch_url, 'https://example.com/foo/bar/tree/master')
self.assertEqual(branch.config_id, self.env.ref('runbot.runbot_build_config_default')) 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):
def test_pull_request(self, mock_github): mock_github = self.patchers['github_patcher']
mock_github.return_value = { mock_github.return_value = {
'head' : {'label': 'foo-dev:bar_branch'}, 'head' : {'label': 'foo-dev:bar_branch'},
'base' : {'ref': 'master'}, 'base' : {'ref': 'master'},

View File

@ -1,7 +1,9 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from collections import defaultdict
from unittest.mock import patch from unittest.mock import patch
from odoo.tests import common from odoo.tests import common
from .common import RunbotCase
def rev_parse(repo, branch_name): 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]) head_hash = 'rp_%s_%s_head' % (repo.name.split(':')[1], branch_name.split('/')[-1])
return head_hash return head_hash
class Test_Build(common.TransactionCase): class Test_Build(RunbotCase):
def setUp(self): def setUp(self):
super(Test_Build, self).setUp() 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.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({ self.branch = self.Branch.create({
'repo_id': self.repo.id, 'repo_id': self.repo.id,
'name': 'refs/heads/master' 'name': 'refs/heads/master'
@ -31,11 +31,9 @@ class Test_Build(common.TransactionCase):
'repo_id': self.repo.id, 'repo_id': self.repo.id,
'name': 'refs/heads/11.0' 'name': 'refs/heads/11.0'
}) })
self.Build = self.env['runbot.build']
@patch('odoo.addons.runbot.models.build.fqdn') def test_base_fields(self):
def test_base_fields(self, mock_fqdn): build = self.create_build({
build = self.Build.create({
'branch_id': self.branch.id, 'branch_id': self.branch.id,
'name': 'd0d0caca0000ffffffffffffffffffffffffffff', 'name': 'd0d0caca0000ffffffffffffffffffffffffffff',
'port': '1234', 'port': '1234',
@ -47,14 +45,14 @@ class Test_Build(common.TransactionCase):
self.assertEqual(build.dest, '%05d-master-deadbe' % build.id) self.assertEqual(build.dest, '%05d-master-deadbe' % build.id)
# Test domain compute with fqdn and ir.config_parameter # 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.env['ir.config_parameter'].sudo().set_param('runbot.runbot_domain', False)
self.assertEqual(build.domain, 'runbot98.nowhere.org:1234') self.assertEqual(build.domain, 'runbot98.nowhere.org:1234')
self.env['ir.config_parameter'].set_param('runbot.runbot_domain', 'runbot99.example.org') self.env['ir.config_parameter'].set_param('runbot.runbot_domain', 'runbot99.example.org')
build._compute_domain() build._compute_domain()
self.assertEqual(build.domain, 'runbot99.example.org:1234') self.assertEqual(build.domain, 'runbot99.example.org:1234')
other = self.Build.create({ other = self.create_build({
'branch_id': self.branch.id, 'branch_id': self.branch.id,
'name': 'd0d0caca0000ffffffffffffffffffffffffffff', 'name': 'd0d0caca0000ffffffffffffffffffffffffffff',
'port': '5678', 'port': '5678',
@ -71,11 +69,9 @@ class Test_Build(common.TransactionCase):
builds.write({'local_state': 'duplicate'}) 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_repo_available_modules')
@patch('odoo.addons.runbot.models.build.runbot_build._get_params') def test_filter_modules(self, mock_get_repo_mods):
@patch('odoo.addons.runbot.models.build.fqdn')
def test_filter_modules(self, mock_fqdn, mock_get_params, mock_get_repo_mods):
""" test module filtering """ """ test module filtering """
build = self.Build.create({ build = self.create_build({
'branch_id': self.branch.id, 'branch_id': self.branch.id,
'name': 'd0d0caca0000ffffffffffffffffffffffffffff', 'name': 'd0d0caca0000ffffffffffffffffffffffffffff',
'port': '1234', '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') 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'])) 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') def test_build_cmd_log_db(self, ):
@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):
""" test that the logdb connection URI is taken from the .odoorc file """ """ 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' uri = 'postgres://someone:pass@somewhere.com/db'
self.env['ir.config_parameter'].sudo().set_param("runbot.runbot_logdb_uri", uri) 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, 'branch_id': self.branch.id,
'name': 'd0d0caca0000ffffffffffffffffffffffffffff', 'name': 'd0d0caca0000ffffffffffffffffffffffffffff',
'port': '1234', 'port': '1234',
@ -115,15 +107,9 @@ class Test_Build(common.TransactionCase):
cmd = build._cmd(py_version=3) cmd = build._cmd(py_version=3)
self.assertIn('log-db = %s' % uri, cmd.get_config()) self.assertIn('log-db = %s' % uri, cmd.get_config())
@patch('odoo.addons.runbot.models.build.os.path.isdir') def test_build_cmd_server_path_no_dep(self):
@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):
""" test that the server path and addons path """ """ test that the server path and addons path """
mock_is_file.return_value = True build = self.create_build({
mock_is_dir.return_value = True
build = self.Build.create({
'branch_id': self.branch.id, 'branch_id': self.branch.id,
'name': 'd0d0caca0000ffffffffffffffffffffffffffff', 'name': 'd0d0caca0000ffffffffffffffffffffffffffff',
'port': '1234', 'port': '1234',
@ -135,19 +121,15 @@ class Test_Build(common.TransactionCase):
addons_path_pos = cmd.index('--addons-path') + 1 addons_path_pos = cmd.index('--addons-path') + 1
self.assertEqual(cmd[addons_path_pos], 'bar/addons,bar/core/addons') self.assertEqual(cmd[addons_path_pos], 'bar/addons,bar/core/addons')
@patch('odoo.addons.runbot.models.branch.runbot_branch._is_on_remote') def test_build_cmd_server_path_with_dep(self):
@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):
""" test that the server path and addons path """ """ test that the server path and addons path """
def is_file(file): def is_file(file):
self.assertIn(file, [ self.assertIn(file, [
'/tmp/runbot_test/static/sources/bar-ent/d0d0caca0000ffffffffffffffffffffffffffff/requirements.txt', '/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/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': if file == '/tmp/runbot_test/static/sources/bar-ent/d0d0caca0000ffffffffffffffffffffffffffff/requirements.txt':
return False 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 self.assertTrue(any([path in file for path in paths])) # checking that addons path existence check looks ok
return True return True
mock_is_file.side_effect = is_file self.patchers['isfile'].side_effect = is_file
mock_is_dir.side_effect = is_dir self.patchers['isdir'].side_effect = is_dir
mock_is_on_remote.return_value = True
repo_ent = self.env['runbot.repo'].create({ repo_ent = self.env['runbot.repo'].create({
'name': 'bla@example.com:foo/bar-ent', 'name': 'bla@example.com:foo/bar-ent',
'server_files': '', 'server_files': '',
@ -181,7 +163,7 @@ class Test_Build(common.TransactionCase):
return 'dfdfcfcf0000ffffffffffffffffffffffffffff' return 'dfdfcfcf0000ffffffffffffffffffffffffffff'
with patch('odoo.addons.runbot.models.repo.runbot_repo._git_rev_parse', new=rev_parse): 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, 'branch_id': enterprise_branch.id,
'name': 'd0d0caca0000ffffffffffffffffffffffffffff', 'name': 'd0d0caca0000ffffffffffffffffffffffffffff',
'port': '1234', 'port': '1234',
@ -193,26 +175,21 @@ class Test_Build(common.TransactionCase):
self.assertEqual('bar/server.py', cmd[1]) self.assertEqual('bar/server.py', cmd[1])
self.assertEqual('python3', cmd[0]) self.assertEqual('python3', cmd[0])
@patch('odoo.addons.runbot.models.branch.runbot_branch._is_on_remote') def test_build_cmd_server_path_with_dep_collision(self):
@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):
""" test that the server path and addons path """ """ test that the server path and addons path """
def is_file(file): def is_file(file):
self.assertIn(file, [ self.assertIn(file, [
'/tmp/runbot_test/static/sources/bar/dfdfcfcf0000ffffffffffffffffffffffffffff/requirements.txt', '/tmp/runbot_test/static/sources/bar/dfdfcfcf0000ffffffffffffffffffffffffffff/requirements.txt',
'/tmp/runbot_test/static/sources/bar/d0d0caca0000ffffffffffffffffffffffffffff/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': if file == '/tmp/runbot_test/static/sources/bar/dfdfcfcf0000ffffffffffffffffffffffffffff/requirements.txt':
return False return False
return True return True
mock_is_file.side_effect = is_file self.patchers['isfile'].side_effect = is_file
mock_is_dir.return_value = True
mock_is_on_remote.return_value = True
repo_ent = self.env['runbot.repo'].create({ repo_ent = self.env['runbot.repo'].create({
'name': 'bla@example.com:foo-ent/bar', 'name': 'bla@example.com:foo-ent/bar',
'server_files': '', 'server_files': '',
@ -229,7 +206,7 @@ class Test_Build(common.TransactionCase):
return 'dfdfcfcf0000ffffffffffffffffffffffffffff' return 'dfdfcfcf0000ffffffffffffffffffffffffffff'
with patch('odoo.addons.runbot.models.repo.runbot_repo._git_rev_parse', new=rev_parse): 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, 'branch_id': enterprise_branch.id,
'name': 'd0d0caca0000ffffffffffffffffffffffffffff', 'name': 'd0d0caca0000ffffffffffffffffffffffffffff',
'port': '1234', 'port': '1234',
@ -243,7 +220,7 @@ class Test_Build(common.TransactionCase):
def test_build_config_from_branch_default(self): def test_build_config_from_branch_default(self):
"""test build config_id is computed from branch default config_id""" """test build config_id is computed from branch default config_id"""
build = self.Build.create({ build = self.create_build({
'branch_id': self.branch.id, 'branch_id': self.branch.id,
'name': 'd0d0caca0000ffffffffffffffffffffffffffff', 'name': 'd0d0caca0000ffffffffffffffffffffffffffff',
}) })
@ -252,7 +229,7 @@ class Test_Build(common.TransactionCase):
def test_build_config_from_branch_testing(self): def test_build_config_from_branch_testing(self):
"""test build config_id is computed from branch""" """test build config_id is computed from branch"""
self.branch.config_id = self.env.ref('runbot.runbot_build_config_default_no_run') 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, 'branch_id': self.branch.id,
'name': 'd0d0caca0000ffffffffffffffffffffffffffff', 'name': 'd0d0caca0000ffffffffffffffffffffffffffff',
}) })
@ -261,7 +238,7 @@ class Test_Build(common.TransactionCase):
def test_build_from_branch_no_build(self): def test_build_from_branch_no_build(self):
"""test build is not even created when branch no_build is True""" """test build is not even created when branch no_build is True"""
self.branch.no_build = True self.branch.no_build = True
build = self.Build.create({ build = self.create_build({
'branch_id': self.branch.id, 'branch_id': self.branch.id,
'name': 'd0d0caca0000ffffffffffffffffffffffffffff', 'name': 'd0d0caca0000ffffffffffffffffffffffffffff',
}) })
@ -270,7 +247,7 @@ class Test_Build(common.TransactionCase):
def test_build_config_can_be_set(self): def test_build_config_can_be_set(self):
"""test build config_id can be set to something different than the one on the branch""" """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') 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, 'branch_id': self.branch.id,
'name': 'd0d0caca0000ffffffffffffffffffffffffffff', 'name': 'd0d0caca0000ffffffffffffffffffffffffffff',
'config_id': self.env.ref('runbot.runbot_build_config_default_no_run').id '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') @patch('odoo.addons.runbot.models.build._logger')
def test_build_skip(self, mock_logger): def test_build_skip(self, mock_logger):
"""test build is skipped""" """test build is skipped"""
build = self.Build.create({ build = self.create_build({
'branch_id': self.branch.id, 'branch_id': self.branch.id,
'name': 'd0d0caca0000ffffffffffffffffffffffffffff', 'name': 'd0d0caca0000ffffffffffffffffffffffffffff',
'port': '1234', 'port': '1234',
@ -289,7 +266,7 @@ class Test_Build(common.TransactionCase):
self.assertEqual(build.local_state, 'done') self.assertEqual(build.local_state, 'done')
self.assertEqual(build.local_result, 'skipped') self.assertEqual(build.local_result, 'skipped')
other_build = self.Build.create({ other_build = self.create_build({
'branch_id': self.branch.id, 'branch_id': self.branch.id,
'name': 'deadbeef0000ffffffffffffffffffffffffffff', 'name': 'deadbeef0000ffffffffffffffffffffffffffff',
'port': '1234', 'port': '1234',
@ -302,13 +279,12 @@ class Test_Build(common.TransactionCase):
def test_ask_kill_duplicate(self): def test_ask_kill_duplicate(self):
""" Test that the _ask_kill method works on duplicate""" """ 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, 'branch_id': self.branch_10.id,
'name': 'd0d0caca0000ffffffffffffffffffffffffffff', 'name': 'd0d0caca0000ffffffffffffffffffffffffffff',
}) })
build2 = self.Build.create({ build2 = self.create_build({
'branch_id': self.branch_10.id, 'branch_id': self.branch_10.id,
'name': 'd0d0caca0000ffffffffffffffffffffffffffff', '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') self.assertEqual(build1.local_result, 'skipped', 'A killed pending duplicate build should mark the real build as skipped')
def test_children(self): def test_children(self):
build1 = self.Build.create({ build1 = self.create_build({
'branch_id': self.branch_10.id, 'branch_id': self.branch_10.id,
'name': 'd0d0caca0000ffffffffffffffffffffffffffff', 'name': 'd0d0caca0000ffffffffffffffffffffffffffff',
}) })
build1_1 = self.Build.create({ build1_1 = self.create_build({
'branch_id': self.branch_10.id, 'branch_id': self.branch_10.id,
'name': 'd0d0caca0000ffffffffffffffffffffffffffff', 'name': 'd0d0caca0000ffffffffffffffffffffffffffff',
'parent_id': build1.id, 'parent_id': build1.id,
'hidden': True, 'hidden': True,
'extra_params': '2', # avoid duplicate 'extra_params': '2', # avoid duplicate
}) })
build1_2 = self.Build.create({ build1_2 = self.create_build({
'branch_id': self.branch_10.id, 'branch_id': self.branch_10.id,
'name': 'd0d0caca0000ffffffffffffffffffffffffffff', 'name': 'd0d0caca0000ffffffffffffffffffffffffffff',
'parent_id': build1.id, 'parent_id': build1.id,
'extra_params': '3', 'extra_params': '3',
}) })
build1_1_1 = self.Build.create({ build1_1_1 = self.create_build({
'branch_id': self.branch_10.id, 'branch_id': self.branch_10.id,
'name': 'd0d0caca0000ffffffffffffffffffffffffffff', 'name': 'd0d0caca0000ffffffffffffffffffffffffffff',
'parent_id': build1_1.id, 'parent_id': build1_1.id,
'extra_params': '4', 'extra_params': '4',
}) })
build1_1_2 = self.Build.create({ build1_1_2 = self.create_build({
'branch_id': self.branch_10.id, 'branch_id': self.branch_10.id,
'name': 'd0d0caca0000ffffffffffffffffffffffffffff', 'name': 'd0d0caca0000ffffffffffffffffffffffffffff',
'parent_id': build1_1.id, 'parent_id': build1_1.id,
@ -408,17 +384,17 @@ class Test_Build(common.TransactionCase):
assert_state(0, 0, 0, 'done', build1_1_2) assert_state(0, 0, 0, 'done', build1_1_2)
def test_duplicate_childrens(self): def test_duplicate_childrens(self):
build_old = self.Build.create({ build_old = self.create_build({
'branch_id': self.branch_10.id, 'branch_id': self.branch_10.id,
'name': 'd0d0caca0000ffffffffffffffffffffffffffff', 'name': 'd0d0caca0000ffffffffffffffffffffffffffff',
'extra_params': '0', 'extra_params': '0',
}) })
build_parent = self.Build.create({ build_parent = self.create_build({
'branch_id': self.branch_10.id, 'branch_id': self.branch_10.id,
'name': 'd0d0caca0000ffffffffffffffffffffffffffff', 'name': 'd0d0caca0000ffffffffffffffffffffffffffff',
'extra_params': '1', 'extra_params': '1',
}) })
build_child = self.Build.create({ build_child = self.create_build({
'branch_id': self.branch_10.id, 'branch_id': self.branch_10.id,
'name': 'd0d0caca0000ffffffffffffffffffffffffffff', 'name': 'd0d0caca0000ffffffffffffffffffffffffffff',
'parent_id': build_parent.id, '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.nb_testing, 0)
self.assertEqual(build_parent.global_state, 'done') self.assertEqual(build_parent.global_state, 'done')
class TestClosestBranch(common.TransactionCase):
class TestClosestBranch(RunbotCase):
def branch_description(self, branch): def branch_description(self, branch):
branch_type = 'pull' if 'pull' in branch.name else '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)]: for b1, b2 in [(branch1, branch2), (branch2, branch1)]:
hash = '%s%s' % (b1.name, b2.name) hash = '%s%s' % (b1.name, b2.name)
build1 = self.Build.create({ build1 = self.create_build({
'branch_id': b1.id, 'branch_id': b1.id,
'name': hash, 'name': hash,
}) })
@ -461,7 +438,7 @@ class TestClosestBranch(common.TransactionCase):
if b1_closest: if b1_closest:
self.assertClosest(b1, closest[b1]) self.assertClosest(b1, closest[b1])
build2 = self.Build.create({ build2 = self.create_build({
'branch_id': b2.id, 'branch_id': b2.id,
'name': hash, 'name': hash,
}) })
@ -533,10 +510,10 @@ class TestClosestBranch(common.TransactionCase):
self.Build = self.env['runbot.build'] self.Build = self.env['runbot.build']
@patch('odoo.addons.runbot.models.repo.runbot_repo._github') def test_pr_is_duplicate(self):
def test_pr_is_duplicate(self, mock_github):
""" test PR is a duplicate of a dev branch build """ """ test PR is a duplicate of a dev branch build """
mock_github = self.patchers['github_patcher']
mock_github.return_value = { mock_github.return_value = {
'head': {'label': 'odoo-dev:10.0-fix-thing-moc'}, 'head': {'label': 'odoo-dev:10.0-fix-thing-moc'},
'base': {'ref': '10.0'}, 'base': {'ref': '10.0'},
@ -553,10 +530,8 @@ class TestClosestBranch(common.TransactionCase):
}) })
self.assertDuplicate(dev_branch, pr) self.assertDuplicate(dev_branch, pr)
@patch('odoo.addons.runbot.models.branch.runbot_branch._is_on_remote') def test_closest_branch_01(self):
def test_closest_branch_01(self, mock_is_on_remote):
""" test find a matching branch in a target repo based on branch name """ """ test find a matching branch in a target repo based on branch name """
mock_is_on_remote.return_value = True
self.Branch.create({ self.Branch.create({
'repo_id': self.community_dev_repo.id, '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)) 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):
def test_closest_branch_02(self, mock_github):
""" test find two matching PR having the same head name """ """ test find two matching PR having the same head name """
mock_github = self.patchers['github_patcher']
mock_github.return_value = { mock_github.return_value = {
# "head label" is the repo:branch where the PR comes from # "head label" is the repo:branch where the PR comes from
# "base ref" is the target of the PR # "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)) self.assertEqual((community_branch, 'exact PR'), enterprise_pr._get_closest_branch(self.community_repo.id))
@patch('odoo.addons.runbot.models.repo.runbot_repo._github') def test_closest_branch_02_improved(self):
@patch('odoo.addons.runbot.models.branch.runbot_branch._is_on_remote')
def test_closest_branch_02_improved(self, mock_is_on_remote, mock_github):
""" test that a PR in enterprise with a matching PR in Community """ test that a PR in enterprise with a matching PR in Community
uses the matching one""" uses the matching one"""
mock_is_on_remote.return_value = True mock_github = self.patchers['github_patcher']
com_dev_branch = self.Branch.create({ com_dev_branch = self.Branch.create({
'repo_id': self.community_dev_repo.id, 'repo_id': self.community_dev_repo.id,
@ -656,22 +629,18 @@ class TestClosestBranch(common.TransactionCase):
(com_dev_branch, 'exact PR') (com_dev_branch, 'exact PR')
) )
@patch('odoo.addons.runbot.models.branch.runbot_branch._is_on_remote') def test_closest_branch_03(self):
def test_closest_branch_03(self, mock_is_on_remote):
""" test find a branch based on dashed prefix""" """ test find a branch based on dashed prefix"""
mock_is_on_remote.return_value = True
addons_branch = self.Branch.create({ addons_branch = self.Branch.create({
'repo_id': self.enterprise_dev_repo.id, 'repo_id': self.enterprise_dev_repo.id,
'name': 'refs/heads/10.0-fix-blah-blah-moc' '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)) 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') def test_closest_branch_03_05(self):
@patch('odoo.addons.runbot.models.branch.runbot_branch._is_on_remote')
def test_closest_branch_03_05(self, mock_is_on_remote, mock_github):
""" test that a PR in enterprise without a matching PR in Community """ test that a PR in enterprise without a matching PR in Community
and no branch in community""" and no branch in community"""
mock_is_on_remote.return_value = True mock_github = self.patchers['github_patcher']
# comm_repo = self.repo # comm_repo = self.repo
# self.repo.write({'token': 1}) # self.repo.write({'token': 1})
@ -715,12 +684,10 @@ class TestClosestBranch(common.TransactionCase):
(com_branch, 'prefix'), (com_branch, 'prefix'),
) )
@patch('odoo.addons.runbot.models.repo.runbot_repo._github') def test_closest_branch_04(self):
@patch('odoo.addons.runbot.models.branch.runbot_branch._is_on_remote')
def test_closest_branch_04(self, mock_is_on_remote, mock_github):
""" test that a PR in enterprise without a matching PR in Community """ test that a PR in enterprise without a matching PR in Community
uses the corresponding exact branch 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({ com_dev_branch = self.Branch.create({
'repo_id': self.community_dev_repo.id, 'repo_id': self.community_dev_repo.id,
@ -753,9 +720,9 @@ class TestClosestBranch(common.TransactionCase):
(com_dev_branch, 'no PR') (com_dev_branch, 'no PR')
) )
@patch('odoo.addons.runbot.models.repo.runbot_repo._github') def test_closest_branch_05(self):
def test_closest_branch_05(self, mock_github):
""" test last resort value """ """ test last resort value """
mock_github = self.patchers['github_patcher']
mock_github.return_value = { mock_github.return_value = {
'head': {'label': 'foo-dev:bar_branch'}, 'head': {'label': 'foo-dev:bar_branch'},
'base': {'ref': '10.0'}, '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)) 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):
def test_no_duplicate_update(self, mock_is_on_remote):
"""push a dev branch in enterprise with same head as sticky, but with a matching branch in community""" """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({ community_sticky_branch = self.Branch.create({
'repo_id': self.community_repo.id, 'repo_id': self.community_repo.id,
'name': 'refs/heads/saas-12.2', 'name': 'refs/heads/saas-12.2',
@ -811,7 +776,7 @@ class TestClosestBranch(common.TransactionCase):
# we shouldn't have duplicate since community_dev_branch exists # 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): with patch('odoo.addons.runbot.models.repo.runbot_repo._git_rev_parse', new=rev_parse):
# lets create an old enterprise build # lets create an old enterprise build
self.Build.create({ self.create_build({
'branch_id': enterprise_sticky_branch.id, 'branch_id': enterprise_sticky_branch.id,
'name': 'd0d0caca0000ffffffffffffffffffffffffffff', 'name': 'd0d0caca0000ffffffffffffffffffffffffffff',
}) })
@ -822,9 +787,9 @@ class TestClosestBranch(common.TransactionCase):
(community_dev_branch, 'exact'), (community_dev_branch, 'exact'),
) )
@patch('odoo.addons.runbot.models.repo.runbot_repo._github') def test_external_pr_closest_branch(self):
def test_external_pr_closest_branch(self, mock_github):
""" test last resort value target_name""" """ test last resort value target_name"""
mock_github = self.patchers['github_patcher']
mock_github.return_value = { mock_github.return_value = {
'head': {'label': 'external_repo:11.0-fix'}, 'head': {'label': 'external_repo:11.0-fix'},
'base': {'ref': '11.0'}, 'base': {'ref': '11.0'},
@ -838,9 +803,9 @@ class TestClosestBranch(common.TransactionCase):
closest_branch = enterprise_pr._get_closest_branch(dependency_repo.id) 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')) 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):
def test_external_pr_with_comunity_pr_closest_branch(self, mock_github):
""" test matching external pr """ """ test matching external pr """
mock_github = self.patchers['github_patcher']
mock_github.return_value = { mock_github.return_value = {
'head': {'label': 'external_dev_repo:11.0-fix'}, 'head': {'label': 'external_dev_repo:11.0-fix'},
'base': {'ref': '11.0'}, 'base': {'ref': '11.0'},
@ -860,7 +825,7 @@ class TestClosestBranch(common.TransactionCase):
'name': 'refs/pull/123' 'name': 'refs/pull/123'
}) })
with patch('odoo.addons.runbot.models.repo.runbot_repo._git_rev_parse', new=rev_parse): 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, 'branch_id': enterprise_pr.id,
'name': 'd0d0caca0000ffffffffffffffffffffffffffff', 'name': 'd0d0caca0000ffffffffffffffffffffffffffff',
}) })

View File

@ -1,15 +1,13 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from unittest.mock import patch from unittest.mock import patch
from odoo.tests import common from odoo.tests import common
from .common import RunbotCase
class TestBuildConfigStep(RunbotCase):
class TestBuildConfigStep(common.TransactionCase):
def setUp(self): def setUp(self):
super(TestBuildConfigStep, self).setUp() super(TestBuildConfigStep, self).setUp()
self.Repo = self.env['runbot.repo'] self.repo = self.Repo.create({'name': 'bla@example.com:foo/bar', 'server_files': 'server.py'})
self.repo = self.Repo.create({'name': 'bla@example.com:foo/bar'})
self.Branch = self.env['runbot.branch']
self.branch = self.Branch.create({ self.branch = self.Branch.create({
'repo_id': self.repo.id, 'repo_id': self.repo.id,
'name': 'refs/heads/master' 'name': 'refs/heads/master'
@ -31,6 +29,9 @@ class TestBuildConfigStep(common.TransactionCase):
'name': 'd0d0caca0000ffffffffffffffffffffffffffff', 'name': 'd0d0caca0000ffffffffffffffffffffffffffff',
'port': '1234', '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): def test_config_step_create_results(self):
""" Test child builds are taken into account""" """ Test child builds are taken into account"""
@ -81,50 +82,48 @@ class TestBuildConfigStep(common.TransactionCase):
self.assertFalse(self.parent_build.global_result) 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.runbot_build._checkout')
@patch('odoo.addons.runbot.models.build_config.docker_run') def test_coverage(self, mock_checkout):
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):
config_step = self.ConfigStep.create({ config_step = self.ConfigStep.create({
'name': 'coverage', 'name': 'coverage',
'job_type': 'install_odoo', 'job_type': 'install_odoo',
'coverage': True 'coverage': True
}) })
mock_checkout.return_value = {} def docker_run(cmd, log_path, *args, **kwargs):
mock_server.return_value = 'bar' cmds = cmd.build().split(' && ')
mock_get_py_version.return_value = '3' dest = self.parent_build.dest
mock_get_addons_path.return_value = ['bar/addons'] self.assertEqual(cmd.pres, [['sudo', 'pip3', 'install', '-r', 'bar/requirements.txt']])
mock_get_server_info.return_value = (self.parent_build._get_all_commit()[0], 'server.py') self.assertEqual(cmd.cmd[:10], ['python3', '-m', 'coverage', 'run', '--branch', '--source', '/data/build', '--omit', '*__manifest__.py', 'bar/server.py'])
mock_local_pg_createdb.return_value = True #['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']])
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))
self.assertEqual(log_path, 'dev/null/logpath') 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') 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.runbot_build._checkout')
@patch('odoo.addons.runbot.models.build_config.docker_run') def test_install_tags(self, mock_checkout):
@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):
config_step = self.ConfigStep.create({ config_step = self.ConfigStep.create({
'name': 'all', 'name': 'all',
'job_type': 'install_odoo', 'job_type': 'install_odoo',
@ -137,29 +136,22 @@ class TestBuildConfigStep(common.TransactionCase):
'test_tags': ':otherclass.othertest' 'test_tags': ':otherclass.othertest'
}) })
mock_checkout.return_value = {} def docker_run(cmd, *args, **kwargs):
mock_server.return_value = 'bar' cmds = cmd.build().split(' && ')
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(' && ')
self.assertEqual(cmds[1].split(' bar/server.py')[0], 'python3') self.assertEqual(cmds[1].split(' bar/server.py')[0], 'python3')
tags = cmds[1].split('--test-tags ')[1].split(' ')[0] tags = cmds[1].split('--test-tags ')[1].split(' ')[0]
self.assertEqual(tags, '/module,:class.method') 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._run_odoo_install(self.parent_build, 'dev/null/logpath')
config_step.enable_auto_tags =True config_step.enable_auto_tags = True
def docker_run(cmd, log_path, build_dir, *args, **kwargs):
cmds = cmd.split(' && ') def docker_run2(cmd, *args, **kwargs):
cmds = cmd.build().split(' && ')
self.assertEqual(cmds[1].split(' bar/server.py')[0], 'python3') self.assertEqual(cmds[1].split(' bar/server.py')[0], 'python3')
tags = cmds[1].split('--test-tags ')[1].split(' ')[0] tags = cmds[1].split('--test-tags ')[1].split(' ')[0]
self.assertEqual(tags, '/module,:class.method,-:otherclass.othertest') 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') config_step._run_odoo_install(self.parent_build, 'dev/null/logpath')

View File

@ -1,6 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from unittest.mock import patch from unittest.mock import patch
from odoo.tests import common from odoo.tests import common
from .common import RunbotCase
RTE_ERROR = """FAIL: TestUiTranslate.test_admin_tour_rte_translator RTE_ERROR = """FAIL: TestUiTranslate.test_admin_tour_rte_translator
Traceback (most recent call last): 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) 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 = { create_vals = {
'branch_id': self.branch.id, 'branch_id': self.branch.id,
'name': 'deadbeaf0000ffffffffffffffffffffffffffff', 'name': 'deadbeaf0000ffffffffffffffffffffffffffff',
@ -24,11 +26,10 @@ class TestBuildError(common.TransactionCase):
'local_result': 'ok' 'local_result': 'ok'
} }
create_vals.update(vals) 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() super(TestBuildError, self).setUp()
repo = self.env['runbot.repo'].create({'name': 'bla@example.com:foo/bar'}) repo = self.env['runbot.repo'].create({'name': 'bla@example.com:foo/bar'})
self.branch = self.env['runbot.branch'].create({ self.branch = self.env['runbot.branch'].create({
@ -36,14 +37,12 @@ class TestBuildError(common.TransactionCase):
'name': 'refs/heads/master' 'name': 'refs/heads/master'
}) })
self.Build = self.env['runbot.build']
self.BuildError = self.env['runbot.build.error'] self.BuildError = self.env['runbot.build.error']
@patch('odoo.addons.runbot.models.build.runbot_build._get_params') def test_build_scan(self):
def test_build_scan(self, mock_get_params):
IrLog = self.env['ir.logging'] IrLog = self.env['ir.logging']
ko_build = self.create_build({'local_result': 'ko'}) ko_build = self.create_test_build({'local_result': 'ko'})
ok_build = self.create_build({'local_result': 'ok'}) ok_build = self.create_test_build({'local_result': 'ok'})
log = {'message': RTE_ERROR, log = {'message': RTE_ERROR,
'build_id': ko_build.id, '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') 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 # 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}) log.update({'build_id': ko_build_same_error.id})
IrLog.create(log) IrLog.create(log)
ko_build_same_error._parse_logs() 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') 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 # 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') 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}) log.update({'build_id': ko_build_diff_number.id, 'message': rte_diff_numbers})
IrLog.create(log) IrLog.create(log)
ko_build_diff_number._parse_logs() 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') 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, # Test that when an error re-appears after the bug has been fixed,
# a new build error is created, with the old one linked # a new build error is created, with the old one linked
build_error.active = False 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}) log.update({'build_id': ko_build_new.id})
IrLog.create(log) IrLog.create(log)
ko_build_new._parse_logs() 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(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') 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):
def test_build_error_links(self, mock_get_params): build_a = self.create_test_build({'local_result': 'ko'})
build_a = self.create_build({'local_result': 'ko'}) build_b = self.create_test_build({'local_result': 'ko'})
build_b = self.create_build({'local_result': 'ko'})
error_a = self.env['runbot.build.error'].create({ error_a = self.env['runbot.build.error'].create({
'content': 'foo', 'content': 'foo',

View File

@ -1,13 +1,14 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from unittest.mock import patch from unittest.mock import patch
from odoo.tests import common from odoo.tests import common
from .common import RunbotCase
class Test_Cron(common.TransactionCase): class Test_Cron(RunbotCase):
def setUp(self): def setUp(self):
super(Test_Cron, self).setUp() 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') @patch('odoo.addons.runbot.models.repo.config.get')
def test_cron_period(self, mock_config_get): def test_cron_period(self, mock_config_get):
@ -19,53 +20,43 @@ class Test_Cron(common.TransactionCase):
for i in range(200): for i in range(200):
self.assertLess(period, 400) self.assertLess(period, 400)
@patch('odoo.addons.runbot.models.repo.fqdn') def test_crons_returns(self):
def test_crons_returns(self, mock_fqdn):
""" test that cron_fetch_and_schedule and _cron_fetch_and_build """ test that cron_fetch_and_schedule and _cron_fetch_and_build
return directly when called on wrong host return directly when called on wrong host
""" """
mock_fqdn.return_value = 'runboty.foo.com'
ret = self.Repo._cron_fetch_and_schedule('runbotx.foo.com') ret = self.Repo._cron_fetch_and_schedule('runbotx.foo.com')
self.assertEqual(ret, 'Not for me') self.assertEqual(ret, 'Not for me')
ret = self.Repo._cron_fetch_and_build('runbotx.foo.com') ret = self.Repo._cron_fetch_and_build('runbotx.foo.com')
self.assertEqual(ret, 'Not for me') 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._create_pending_builds')
@patch('odoo.addons.runbot.models.repo.runbot_repo._update') @patch('odoo.addons.runbot.models.repo.runbot_repo._update')
@patch('odoo.addons.runbot.models.repo.fqdn') def test_cron_schedule(self, mock_update, mock_create):
def test_cron_schedule(self, mock_fqdn, mock_update, mock_create, mock_cron_period):
""" test that cron_fetch_and_schedule do its work """ """ 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.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.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 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 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) self.assertEqual(None, ret)
mock_update.assert_called_with(force=False) mock_update.assert_called_with(force=False)
mock_create.assert_called_with() 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._reload_nginx')
@patch('odoo.addons.runbot.models.repo.runbot_repo._scheduler') @patch('odoo.addons.runbot.models.repo.runbot_repo._scheduler')
@patch('odoo.addons.runbot.models.repo.fqdn') def test_cron_build(self, mock_scheduler, mock_reload):
def test_cron_build(self, mock_fqdn, mock_scheduler, mock_reload, mock_cron_period, mock_host_fqdn):
""" test that cron_fetch_and_build do its work """ """ test that cron_fetch_and_build do its work """
hostname = 'runbotx.foo.com' hostname = 'host.runbot.com'
mock_fqdn.return_value = mock_host_fqdn.return_value = hostname
mock_cron_period.return_value = 2
self.env['ir.config_parameter'].sudo().set_param('runbot.runbot_update_frequency', 1) 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.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 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 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) self.assertEqual(None, ret)
mock_scheduler.assert_called() mock_scheduler.assert_called()
self.assertTrue(mock_reload.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.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') self.assertGreater(host.psql_conn_count, 0, 'A least one connection should exist on the current psql instance')

View File

@ -1,15 +1,14 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from unittest.mock import patch from unittest.mock import patch
from odoo.tests import common from odoo.tests import common
from .common import RunbotCase
class TestIrLogging(common.TransactionCase): class TestIrLogging(RunbotCase):
def setUp(self): def setUp(self):
super(TestIrLogging, self).setUp() 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.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({ self.branch = self.Branch.create({
'repo_id': self.repo.id, 'repo_id': self.repo.id,
'name': 'refs/heads/master' '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) VALUES (NOW() at time zone 'UTC', %s, %s, %s, %s, %s, %s, %s, %s)
""", val) """, val)
@patch('odoo.addons.runbot.models.build.runbot_build._get_params') def test_ir_logging(self):
@patch('odoo.addons.runbot.models.build.fqdn') build = self.create_build({
def test_ir_logging(self, mock_fqdn, mock_get_params):
build = self.Build.create({
'branch_id': self.branch.id, 'branch_id': self.branch.id,
'name': 'd0d0caca0000ffffffffffffffffffffffffffff', 'name': 'd0d0caca0000ffffffffffffffffffffffffffff',
'port': '1234', 'port': '1234',

View File

@ -1,12 +1,14 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from collections import defaultdict
from itertools import cycle from itertools import cycle
from unittest.mock import patch from unittest.mock import patch
from werkzeug.wrappers import Response from werkzeug.wrappers import Response
from odoo.tests import common from odoo.tests import common
from odoo.addons.runbot.controllers import frontend from odoo.addons.runbot.controllers import frontend
from .common import RunbotCase
class Test_Frontend(common.HttpCase): class Test_Frontend(RunbotCase):
def setUp(self): def setUp(self):
super(Test_Frontend, self).setUp() super(Test_Frontend, self).setUp()

View File

@ -2,22 +2,22 @@
import datetime import datetime
from unittest import skip from unittest import skip
from unittest.mock import patch, Mock from unittest.mock import patch, Mock
from odoo.tests import common from odoo.tests import common, TransactionCase
import logging import logging
import odoo import odoo
import time import time
import datetime from .common import RunbotCase
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
class Test_Repo(common.TransactionCase): class Test_Repo(RunbotCase):
def setUp(self): def setUp(self):
super(Test_Repo, self).setUp() super(Test_Repo, self).setUp()
self.Repo = self.env['runbot.repo']
self.commit_list = [] self.commit_list = []
self.mock_root = self.patchers['repo_root_patcher']
def mock_git_helper(self): def mock_git_helper(self):
"""Helper that returns a mock for repo._git()""" """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 '\n'.join(['\0'.join(commit_fields) for commit_fields in self.commit_list])
return mock_git return mock_git
@patch('odoo.addons.runbot.models.repo.runbot_repo._root') def test_base_fields(self):
def test_base_fields(self, mock_root): self.mock_root.return_value = '/tmp/static'
mock_root.return_value = '/tmp/static'
repo = self.Repo.create({'name': 'bla@example.com:foo/bar'}) repo = self.Repo.create({'name': 'bla@example.com:foo/bar'})
self.assertEqual(repo.path, '/tmp/static/repo/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'}) local_repo = self.Repo.create({'name': '/path/somewhere/rep.git'})
self.assertEqual(local_repo.short_name, 'somewhere/rep') 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') @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 """ Test that when finding new refs in a repo, the missing branches
are created and new builds are created in pending state 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'}) repo = self.Repo.create({'name': 'bla@example.com:foo/bar'})
# create another repo and branch to ensure there is no mismatch # 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') @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):
def test_repo_perf_find_new_commits(self, mock_root):
mock_root.return_value = '/tmp/static' mock_root.return_value = '/tmp/static'
repo = self.env['runbot.repo'].search([('name', '=', 'blabla')]) 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)) _logger.info('Create pending builds took: %ssec', (time.time() - inserted_time))
class Test_Github(TransactionCase):
def test_github(self): def test_github(self):
""" Test different github responses or failures""" """ 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') self.assertEqual(repo._github('/repos/:owner/:repo/statuses/abcdef', dict(), ignore_errors=True), None, 'A repo without token should return None')
repo.token = 'abc' repo.token = 'abc'
with patch('odoo.addons.runbot.models.repo.requests.Session') as mock_session: 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") 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') class Test_Repo_Scheduler(RunbotCase):
def setUp(self, mock_root):
def setUp(self ):
# as the _scheduler method commits, we need to protect the database # as the _scheduler method commits, we need to protect the database
registry = odoo.registry() registry = odoo.registry()
registry.enter_test_mode() registry.enter_test_mode()
self.addCleanup(registry.leave_test_mode) self.addCleanup(registry.leave_test_mode)
super(Test_Repo_Scheduler, self).setUp() 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' 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, 'repo_id': self.foo_repo.id,
'name': 'refs/head/foo' '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._reap')
@patch('odoo.addons.runbot.models.build.runbot_build._kill') @patch('odoo.addons.runbot.models.build.runbot_build._kill')
@patch('odoo.addons.runbot.models.build.runbot_build._schedule') @patch('odoo.addons.runbot.models.build.runbot_build._schedule')
@patch('odoo.addons.runbot.models.host.fqdn') def test_repo_scheduler(self, mock_schedule, mock_kill, mock_reap):
def test_repo_scheduler(self, mock_fqdn, mock_schedule, mock_kill, mock_reap):
mock_fqdn.return_value = 'test_host'
self.env['ir.config_parameter'].set_param('runbot.runbot_workers', 6) self.env['ir.config_parameter'].set_param('runbot.runbot_workers', 6)
Build_model = self.env['runbot.build']
builds = [] builds = []
# create 6 builds that are testing on the host to verify that # create 6 builds that are testing on the host to verify that
# workers are not overfilled # workers are not overfilled
for build_name in ['a', 'b', 'c', 'd', 'e', 'f']: for build_name in ['a', 'b', 'c', 'd', 'e', 'f']:
build = Build_model.create({ build = self.create_build({
'branch_id': self.foo_branch.id, 'branch_id': self.foo_branch.id,
'name': build_name, 'name': build_name,
'port': '1234', 'port': '1234',
'build_type': 'normal', 'build_type': 'normal',
'local_state': 'testing', 'local_state': 'testing',
'host': 'test_host' 'host': 'host.runbot.com'
}) })
builds.append(build) builds.append(build)
# now the pending build that should stay unasigned # now the pending build that should stay unasigned
scheduled_build = Build_model.create({ scheduled_build = self.create_build({
'branch_id': self.foo_branch.id, 'branch_id': self.foo_branch.id,
'name': 'sched_build', 'name': 'sched_build',
'port': '1234', 'port': '1234',
@ -255,7 +252,7 @@ class Test_Repo_Scheduler(common.TransactionCase):
}) })
builds.append(scheduled_build) builds.append(scheduled_build)
# create the build that should be assigned once a slot is available # 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, 'branch_id': self.foo_branch.id,
'name': 'foobuild', 'name': 'foobuild',
'port': '1234', 'port': '1234',
@ -272,10 +269,10 @@ class Test_Repo_Scheduler(common.TransactionCase):
self.assertFalse(scheduled_build.host) self.assertFalse(scheduled_build.host)
# give some room for the pending build # 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() self.foo_repo._scheduler()
build.invalidate_cache() build.invalidate_cache()
scheduled_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) self.assertFalse(scheduled_build.host)

View File

@ -3,9 +3,10 @@ import datetime
from unittest.mock import patch from unittest.mock import patch
from odoo.tests import common from odoo.tests import common
import odoo import odoo
from .common import RunbotCase
class TestSchedule(common.TransactionCase): class TestSchedule(RunbotCase):
def setUp(self): def setUp(self):
# entering test mode to avoid that the _schedule method commits records # entering test mode to avoid that the _schedule method commits records
@ -13,19 +14,16 @@ class TestSchedule(common.TransactionCase):
registry.enter_test_mode() registry.enter_test_mode()
self.addCleanup(registry.leave_test_mode) self.addCleanup(registry.leave_test_mode)
super(TestSchedule, self).setUp() super(TestSchedule, self).setUp()
self.Repo = self.env['runbot.repo']
self.repo = self.Repo.create({'name': 'bla@example.com:foo/bar'}) self.repo = self.Repo.create({'name': 'bla@example.com:foo/bar'})
self.Branch = self.env['runbot.branch']
self.branch = self.Branch.create({ self.branch = self.Branch.create({
'repo_id': self.repo.id, 'repo_id': self.repo.id,
'name': 'refs/heads/master' '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.os.path.getmtime')
@patch('odoo.addons.runbot.models.build.docker_is_running') @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 """ """ Test that results are set even when job_30_run is skipped """
job_end_time = datetime.datetime.now() job_end_time = datetime.datetime.now()
mock_getmtime.return_value = job_end_time.timestamp() mock_getmtime.return_value = job_end_time.timestamp()