[IMP] runbot: add basic tests

Deploying non tested code is prone to errors and leads to frustration.
With this commit, basic tests are done to avoid such a situation.
This commit is contained in:
Christophe Monniez 2018-11-30 16:58:00 +01:00
parent 84e6be84ad
commit 4ac6a0db98
7 changed files with 416 additions and 1 deletions

View File

@ -86,4 +86,3 @@ def get_py_version(build):
if f.readline().strip().endswith('python3'):
return 'python3'
return 'python'

5
runbot/tests/__init__.py Normal file
View File

@ -0,0 +1,5 @@
from . import test_repo
from . import test_branch
from . import test_build
from . import test_jobs
from . import test_frontend

View File

@ -0,0 +1,52 @@
# -*- coding: utf-8 -*-
from unittest.mock import patch
from odoo.tests import common
class Test_Branch(common.TransactionCase):
def setUp(self):
super(Test_Branch, self).setUp()
Repo = self.env['runbot.repo']
self.repo = Repo.create({'name': 'bla@example.com:foo/bar', 'token': '123'})
self.Branch = self.env['runbot.branch']
#mock_patch = patch('odoo.addons.runbot.models.repo.runbot_repo._github', self._github)
#mock_patch.start()
#self.addCleanup(mock_patch.stop)
def test_base_fields(self):
branch = self.Branch.create({
'repo_id': self.repo.id,
'name': 'refs/head/master'
})
self.assertEqual(branch.branch_name, 'master')
self.assertEqual(branch.branch_url, 'https://example.com/foo/bar/tree/master')
@patch('odoo.addons.runbot.models.repo.runbot_repo._github')
def test_pull_request(self, mock_github):
mock_github.return_value = {
'head' : {'label': 'foo-dev:bar_branch'},
'base' : {'ref': 'master'},
}
pr = self.Branch.create({
'repo_id': self.repo.id,
'name': 'refs/pull/12345'
})
self.assertEqual(pr.branch_name, '12345')
self.assertEqual(pr.branch_url, 'https://example.com/foo/bar/pull/12345')
self.assertEqual(pr.target_branch_name, 'master')
self.assertEqual(pr.pull_head_name, 'foo-dev:bar_branch')
def test_coverage_in_name(self):
"""Test that coverage in branch name enables coverage"""
branch = self.Branch.create({
'repo_id': self.repo.id,
'name': 'refs/head/foo-branch-bar'
})
self.assertFalse(branch.coverage)
cov_branch = self.Branch.create({
'repo_id': self.repo.id,
'name': 'refs/head/foo-coverage-branch-bar'
})
self.assertTrue(cov_branch.coverage)

215
runbot/tests/test_build.py Normal file
View File

@ -0,0 +1,215 @@
# -*- coding: utf-8 -*-
from unittest.mock import patch
from odoo.tests import common
class Test_Build(common.TransactionCase):
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'})
self.Branch = self.env['runbot.branch']
self.branch = self.Branch.create({
'repo_id': self.repo.id,
'name': 'refs/heads/master'
})
self.branch_10 = self.Branch.create({
'repo_id': self.repo.id,
'name': 'refs/heads/10.0'
})
self.branch_11 = self.Branch.create({
'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({
'branch_id': self.branch.id,
'name': 'd0d0caca0000ffffffffffffffffffffffffffff',
'port' : '1234',
})
self.assertEqual(build.id, build.sequence)
self.assertEqual(build.dest, '%05d-master-d0d0ca' % build.id)
# test dest change on new commit
build.name = 'deadbeef0000ffffffffffffffffffffffffffff'
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.assertEqual(build.domain, 'runbot98.nowhere.org:1234')
self.env['ir.config_parameter'].set_param('runbot.runbot_domain', 'runbot99.example.org')
build._get_domain()
self.assertEqual(build.domain, 'runbot99.example.org:1234')
def test_pr_is_duplicate(self):
""" test PR is a duplicate of a dev branch build """
dup_repo = self.Repo.create({
'name': 'bla@example.com:foo-dev/bar',
'duplicate_id': self.repo.id
})
self.repo.duplicate_id = dup_repo.id
dev_branch = self.Branch.create({
'repo_id': dup_repo.id,
'name': 'refs/heads/10.0-fix-thing-moc'
})
dev_build = self.Build.create({
'branch_id': dev_branch.id,
'name': 'd0d0caca0000ffffffffffffffffffffffffffff',
})
pr = self.Branch.create({
'repo_id': self.repo.id,
'name': 'refs/pull/12345'
})
pr_build = self.Build.create({
'branch_id': pr.id,
'name': 'd0d0caca0000ffffffffffffffffffffffffffff',
})
self.assertEqual(pr_build.state, 'duplicate')
self.assertEqual(pr_build.duplicate_id.id, dev_build.id)
def test_dev_is_duplicate(self):
""" test dev branch build is a duplicate of a PR """
dup_repo = self.Repo.create({
'name': 'bla@example.com:foo-dev/bar',
'duplicate_id': self.repo.id
})
self.repo.duplicate_id = dup_repo.id
dev_branch = self.Branch.create({
'repo_id': dup_repo.id,
'name': 'refs/heads/10.0-fix-thing-moc'
})
pr = self.Branch.create({
'repo_id': self.repo.id,
'name': 'refs/pull/12345'
})
pr_build = self.Build.create({
'branch_id': pr.id,
'name': 'd0d0caca0000ffffffffffffffffffffffffffff',
})
dev_build = self.Build.create({
'branch_id': dev_branch.id,
'name': 'd0d0caca0000ffffffffffffffffffffffffffff',
})
self.assertEqual(dev_build.state, 'duplicate')
self.assertEqual(dev_build.duplicate_id.id, pr_build.id)
@patch('odoo.addons.runbot.models.branch.runbot_branch._is_on_remote')
def test_closest_branch_01(self, mock_is_on_remote):
""" test find a matching branch in a target repo based on branch name """
mock_is_on_remote.return_value = True
server_repo = self.Repo.create({'name': 'bla@example.com:foo-dev/bar'})
addons_repo = self.Repo.create({'name': 'bla@example.com:ent-dev/bar'})
self.Branch.create({
'repo_id': server_repo.id,
'name': 'refs/heads/10.0-fix-thing-moc'
})
addons_branch = self.Branch.create({
'repo_id': addons_repo.id,
'name': 'refs/heads/10.0-fix-thing-moc'
})
addons_build = self.Build.create({
'branch_id': addons_branch.id,
'name': 'd0d0caca0000ffffffffffffffffffffffffffff',
})
self.assertEqual((server_repo.id, addons_branch.name, 'exact'), addons_build._get_closest_branch_name(server_repo.id))
@patch('odoo.addons.runbot.models.repo.runbot_repo._github')
def test_closest_branch_02(self, mock_github):
""" test find two matching PR having the same head name """
mock_github.return_value = {
'head' : {'label': 'foo-dev:bar_branch'},
'base' : {'ref': 'master'},
'state': 'open'
}
server_repo = self.Repo.create({'name': 'bla@example.com:foo-dev/bar', 'token': '1'})
addons_repo = self.Repo.create({'name': 'bla@example.com:ent-dev/bar', 'token': '1'})
server_pr = self.Branch.create({
'repo_id': server_repo.id,
'name': 'refs/pull/123456'
})
addons_pr = self.Branch.create({
'repo_id': addons_repo.id,
'name': 'refs/pull/789101'
})
addons_build = self.Build.create({
'branch_id': addons_pr.id,
'name': 'd0d0caca0000ffffffffffffffffffffffffffff',
})
self.assertEqual((server_repo.id, server_pr.name, 'exact'), addons_build._get_closest_branch_name(server_repo.id))
@patch('odoo.addons.runbot.models.build.runbot_build._branch_exists')
def test_closest_branch_03(self, mock_branch_exists):
""" test find a branch based on dashed prefix"""
mock_branch_exists.return_value = True
addons_repo = self.Repo.create({'name': 'bla@example.com:ent-dev/bar', 'token': '1'})
addons_branch = self.Branch.create({
'repo_id': addons_repo.id,
'name': 'refs/heads/10.0-fix-blah-blah-moc'
})
addons_build = self.Build.create({
'branch_id': addons_branch.id,
'name': 'd0d0caca0000ffffffffffffffffffffffffffff',
})
self.assertEqual((self.repo.id, 'refs/heads/10.0', 'prefix'), addons_build._get_closest_branch_name(self.repo.id))
@patch('odoo.addons.runbot.models.repo.runbot_repo._github')
def test_closest_branch_05(self, mock_github):
""" test last resort value """
mock_github.return_value = {
'head' : {'label': 'foo-dev:bar_branch'},
'base' : {'ref': '10.0'},
'state': 'open'
}
server_repo = self.Repo.create({'name': 'bla@example.com:foo-dev/bar', 'token': '1'})
addons_repo = self.Repo.create({'name': 'bla@example.com:ent-dev/bar', 'token': '1'})
server_pr = self.Branch.create({
'repo_id': server_repo.id,
'name': 'refs/pull/123456'
})
mock_github.return_value = {
'head' : {'label': 'foo-dev:foobar_branch'},
'base' : {'ref': '10.0'},
'state': 'open'
}
addons_pr = self.Branch.create({
'repo_id': addons_repo.id,
'name': 'refs/pull/789101'
})
addons_build = self.Build.create({
'branch_id': addons_pr.id,
'name': 'd0d0caca0000ffffffffffffffffffffffffffff',
})
self.assertEqual((server_repo.id, server_pr.target_branch_name, 'default'), addons_build._get_closest_branch_name(server_repo.id))
@patch('odoo.addons.runbot.models.repo.runbot_repo._github')
def test_closest_branch_05_master(self, mock_github):
""" test last resort value when nothing common can be found"""
mock_github.return_value = {
'head' : {'label': 'foo-dev:bar_branch'},
'base' : {'ref': 'saas-14'},
'state': 'open'
}
server_repo = self.Repo.create({'name': 'bla@example.com:foo-dev/bar', 'token': '1'})
addons_repo = self.Repo.create({'name': 'bla@example.com:ent-dev/bar', 'token': '1'})
server_pr = self.Branch.create({
'repo_id': server_repo.id,
'name': 'refs/pull/123456'
})
mock_github.return_value = {
'head' : {'label': 'foo-dev:foobar_branch'},
'base' : {'ref': '10.0'},
'state': 'open'
}
addons_pr = self.Branch.create({
'repo_id': addons_repo.id,
'name': 'refs/pull/789101'
})
addons_build = self.Build.create({
'branch_id': addons_pr.id,
'name': 'd0d0caca0000ffffffffffffffffffffffffffff',
})
self.assertEqual((server_repo.id, 'master', 'default'), addons_build._get_closest_branch_name(server_repo.id))

View File

@ -0,0 +1,71 @@
# -*- coding: utf-8 -*-
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
class Test_Frontend(common.HttpCase):
def setUp(self):
super(Test_Frontend, self).setUp()
Repo = self.env['runbot.repo']
self.repo = Repo.create({'name': 'bla@example.com:foo/bar', 'token': '123'})
self.Branch = self.env['runbot.branch']
self.sticky_branch = self.Branch.create({
'repo_id': self.repo.id,
'name': 'refs/heads/master',
'sticky': True,
})
self.branch = self.Branch.create({
'repo_id': self.repo.id,
'name': 'refs/heads/master-test-moc',
'sticky': False,
})
self.Build = self.env['runbot.build']
@patch('odoo.http.Response.set_default')
@patch('odoo.addons.runbot.controllers.frontend.request')
def test_frontend_basic(self, mock_request, mock_set_default):
mock_request.env = self.env
mock_request._cr = self.cr
controller = frontend.Runbot()
states = ['done', 'pending', 'testing', 'running', 'deathrow']
branches = [self.branch, self.sticky_branch]
names = ['deadbeef', 'd0d0caca', 'deadface', 'cacafeed']
# create 5 builds in each branch
for i, state, branch, name in zip(range(10), cycle(states), cycle(branches), cycle(names)):
self.Build.create({
'branch_id': branch.id,
'name': '%s0000ffffffffffffffffffffffffffff' % name,
'port': '1234',
'state': state,
'result': 'ok'
})
def mocked_simple_repo_render(template, context):
self.assertEqual(template, 'runbot.repo', 'The frontend controller should use "runbot.repo" template')
self.assertEqual(self.sticky_branch, context['branches'][0]['branch'], "The sticky branch should be in first place")
self.assertEqual(self.branch, context['branches'][1]['branch'], "The non sticky branch should be in second place")
self.assertEqual(len(context['branches'][0]['builds']), 4, "Only the 4 last builds should appear in the context")
self.assertEqual(context['pending'], 2, "There should be 2 pending builds")
self.assertEqual(context['running'], 2, "There should be 2 running builds")
self.assertEqual(context['testing'], 2, "There should be 2 testing builds")
self.assertEqual(context['pending_total'], 2, "There should be 2 pending builds")
self.assertEqual(context['pending_level'], 'info', "The pending level should be info")
return Response()
mock_request.render = mocked_simple_repo_render
controller.repo()
def mocked_repo_search_render(template, context):
dead_count = len([bu['name'] for b in context['branches'] for bu in b['builds'] if bu['name'].startswith('dead')])
undead_count = len([bu['name'] for b in context['branches'] for bu in b['builds'] if not bu['name'].startswith('dead')])
self.assertEqual(dead_count, 4, 'The search for "dead" should return 4 builds')
self.assertEqual(undead_count, 0, 'The search for "dead" should not return any build without "dead" in its name')
return Response()
mock_request.render = mocked_repo_search_render
controller.repo(search='dead')

56
runbot/tests/test_jobs.py Normal file
View File

@ -0,0 +1,56 @@
# -*- coding: utf-8 -*-
import datetime
from time import localtime
from unittest.mock import patch
from odoo.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT
from odoo.tests import common
class Test_Jobs(common.TransactionCase):
def setUp(self):
super(Test_Jobs, self).setUp()
self.Repo = self.env['runbot.repo']
self.repo = self.Repo.create({'name': 'bla@example.com:foo/bar', 'token': 'xxx'})
self.Branch = self.env['runbot.branch']
self.branch_master = self.Branch.create({
'repo_id': self.repo.id,
'name': 'refs/heads/master',
})
self.Build = self.env['runbot.build']
@patch('odoo.addons.runbot.models.repo.runbot_repo._domain')
@patch('odoo.addons.runbot.models.repo.runbot_repo._github')
@patch('odoo.addons.runbot.models.build.runbot_build._cmd')
@patch('odoo.addons.runbot.models.build.os.path.getmtime')
@patch('odoo.addons.runbot.models.build.time.localtime')
@patch('odoo.addons.runbot.models.build.docker_run')
@patch('odoo.addons.runbot.models.build.grep')
def test_job_30_failed(self, mock_grep, mock_docker_run, mock_localtime, mock_getmtime, mock_cmd, mock_github, mock_domain):
""" Test that a failed build sets the failure state on github """
a_time = datetime.datetime.now().strftime(DEFAULT_SERVER_DATETIME_FORMAT)
mock_grep.return_value = False
mock_docker_run.return_value = 2
now = localtime()
mock_localtime.return_value = now
mock_getmtime.return_value = None
mock_cmd.return_value = ([], [])
mock_domain.return_value = 'runbotxx.somewhere.com'
build = self.Build.create({
'branch_id': self.branch_master.id,
'name': 'd0d0caca0000ffffffffffffffffffffffffffff',
'port' : '1234',
'state': 'done',
'job_start': a_time,
'job_end': a_time
})
self.assertFalse(build.result)
self.Build._job_30_run(build, '/tmp/x.log')
self.assertEqual(build.result, 'ko')
expected_status = {
'state': 'failure',
'target_url': 'http://runbotxx.somewhere.com/runbot/build/%s' % build.id,
'description': 'runbot build %s (runtime 0s)' % build.dest,
'context': 'ci/runbot'
}
mock_github.assert_called_with('/repos/:owner/:repo/statuses/d0d0caca0000ffffffffffffffffffffffffffff', expected_status, ignore_errors=True)

17
runbot/tests/test_repo.py Normal file
View File

@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
from unittest.mock import patch
from odoo.tests import common
class Test_Repo(common.TransactionCase):
def setUp(self):
super(Test_Repo, self).setUp()
self.Repo = self.env['runbot.repo']
@patch('odoo.addons.runbot.models.repo.runbot_repo._root')
def test_base_fields(self, mock_root):
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')
self.assertEqual(repo.base, 'example.com/foo/bar')