blob: db4c703f4706a5534c7f728ecaaa95150122de90 [file] [log] [blame]
#!/usr/bin/env python
# Copyright (c) 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Source file for lkgr_lib testcases."""
import datetime
import httplib2
import json
import mock
import os
import sys
import tempfile
import unittest
import google.protobuf.message
from infra import init_python_pb2 # pylint: disable=unused-import
from go.chromium.org.luci.buildbucket.proto import common_pb2
from go.chromium.org.luci.buildbucket.proto import rpc_pb2
from infra.services.lkgr_finder import lkgr_lib
from infra.services.lkgr_finder.status_generator import StatusGeneratorStub
# TODO(agable): Test everything else once I can import pymox.
class FetchBuildbucketBuildsForBuilderTest(unittest.TestCase):
@mock.patch('infra.services.lkgr_finder.lkgr_lib._FetchFromBuildbucketImpl')
def testFetchSucceeded(self, mocked_fetch):
response_pb = rpc_pb2.SearchBuildsResponse()
build_pb = response_pb.builds.add()
build_pb.number = 123
build_pb.status = common_pb2.SUCCESS
build_pb.input.gitiles_commit.id = 'a' * 40
mocked_fetch.return_value = response_pb
builds = lkgr_lib.FetchBuildbucketBuildsForBuilder(
'project1/bucket1', 'builder1')
self.assertEquals(
builds,
[lkgr_lib.Build(123, lkgr_lib.STATUS.SUCCESS, 'a' * 40)])
@mock.patch('infra.services.lkgr_finder.lkgr_lib._FetchFromBuildbucketImpl')
def testFetchFailed(self, mocked_fetch):
mocked_fetch.side_effect = httplib2.HttpLib2Error
builds = lkgr_lib.FetchBuildbucketBuildsForBuilder(
'project1/bucket1', 'builder1')
self.assertEquals(builds, None)
@mock.patch('infra.services.lkgr_finder.lkgr_lib._FetchFromBuildbucketImpl')
def testProtoError(self, mocked_fetch):
mocked_fetch.side_effect = google.protobuf.message.DecodeError
builds = lkgr_lib.FetchBuildbucketBuildsForBuilder(
'project1/bucket1', 'builder1')
self.assertEquals(builds, None)
@mock.patch('infra.services.lkgr_finder.lkgr_lib._FetchFromBuildbucketImpl')
def testMalformattedBucket(self, mocked_fetch):
builds = lkgr_lib.FetchBuildbucketBuildsForBuilder(
'project1_bucket1', 'builder1')
self.assertEquals(builds, None)
mocked_fetch.assert_not_called()
@mock.patch('infra.services.lkgr_finder.lkgr_lib._FetchFromBuildbucketImpl')
def testFetchBuildCanceled(self, mocked_fetch):
response_pb = rpc_pb2.SearchBuildsResponse()
build_pb = response_pb.builds.add()
build_pb.number = 123
build_pb.status = common_pb2.CANCELED
build_pb.input.gitiles_commit.id = 'a' * 40
mocked_fetch.return_value = response_pb
builds = lkgr_lib.FetchBuildbucketBuildsForBuilder(
'project1/bucket1', 'builder1')
self.assertEquals(
builds,
[lkgr_lib.Build(123, lkgr_lib.STATUS.UNKNOWN, 'a' * 40)])
@mock.patch('infra.services.lkgr_finder.lkgr_lib._FetchFromBuildbucketImpl')
def testFetchBuildFailed(self, mocked_fetch):
response_pb = rpc_pb2.SearchBuildsResponse()
build_pb = response_pb.builds.add()
build_pb.number = 123
build_pb.status = common_pb2.FAILURE
build_pb.input.gitiles_commit.id = 'a' * 40
mocked_fetch.return_value = response_pb
builds = lkgr_lib.FetchBuildbucketBuildsForBuilder(
'project1/bucket1', 'builder1')
self.assertEquals(
builds,
[lkgr_lib.Build(123, lkgr_lib.STATUS.FAILURE, 'a' * 40)])
@mock.patch('infra.services.lkgr_finder.lkgr_lib._FetchFromBuildbucketImpl')
def testFetchMissingPieces(self, mocked_fetch):
response_pb = rpc_pb2.SearchBuildsResponse()
build_pb = response_pb.builds.add()
build_pb.number = 123
build_pb.status = common_pb2.SCHEDULED
mocked_fetch.return_value = response_pb
builds = lkgr_lib.FetchBuildbucketBuildsForBuilder(
'project1/bucket1', 'builder1')
self.assertEquals(builds, [])
class CollateRevisionHistoryTest(unittest.TestCase):
def testSortsBuildHistories(self):
build_history = {
'master1': {
'builder1': [
lkgr_lib.Build(121, lkgr_lib.STATUS.SUCCESS,
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab'),
lkgr_lib.Build(123, lkgr_lib.STATUS.SUCCESS,
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'),
lkgr_lib.Build(122, lkgr_lib.STATUS.SUCCESS,
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac'),
]
}
}
expected_builds = [
lkgr_lib.Build(123, lkgr_lib.STATUS.SUCCESS,
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'),
lkgr_lib.Build(121, lkgr_lib.STATUS.SUCCESS,
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab'),
lkgr_lib.Build(122, lkgr_lib.STATUS.SUCCESS,
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac'),
]
repo = mock.MagicMock()
def mock_repo_sort(history, keyfunc=None):
return sorted(history, key=keyfunc)
repo.sort = mock.MagicMock(side_effect=mock_repo_sort)
collated_build_history, _ = lkgr_lib.CollateRevisionHistory(
build_history, repo)
actual_builds = (
collated_build_history.get('master1', {}).get('builder1', []))
self.assertEquals(actual_builds, expected_builds)
def testSortsRevisions(self):
build_history = {
'master1': {
'builder1': [
lkgr_lib.Build(121, lkgr_lib.STATUS.SUCCESS,
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab'),
lkgr_lib.Build(123, lkgr_lib.STATUS.SUCCESS,
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'),
lkgr_lib.Build(122, lkgr_lib.STATUS.SUCCESS,
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac'),
]
}
}
expected_revisions = [
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab',
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac',
]
repo = mock.MagicMock()
def mock_repo_sort(history, keyfunc=None):
return sorted(history, key=keyfunc)
repo.sort = mock.MagicMock(side_effect=mock_repo_sort)
_, actual_revisions = lkgr_lib.CollateRevisionHistory(
build_history, repo)
self.assertEquals(actual_revisions, expected_revisions)
class FindLKGRCandidateTest(unittest.TestCase):
def setUp(self):
self.status_stub = StatusGeneratorStub()
self.good = lkgr_lib.STATUS.SUCCESS
self.fail = lkgr_lib.STATUS.FAILURE
self.keyfunc = int
def testSimpleSucceeds(self):
build_history = {'m1': {'b1': [lkgr_lib.Build(1, self.good, 1)]}}
revisions = [1]
candidate = lkgr_lib.FindLKGRCandidate(
build_history, revisions, self.keyfunc, self.status_stub)
self.assertEquals(candidate, 1)
def testSimpleFails(self):
build_history = {'m1': {'b1': [lkgr_lib.Build(1, self.fail, 1)]}}
revisions = [1]
candidate = lkgr_lib.FindLKGRCandidate(
build_history, revisions, self.keyfunc, self.status_stub)
self.assertEquals(candidate, None)
def testModerateSuccess(self):
build_history = {
'm1': {'b1': [lkgr_lib.Build(1, self.good, 1)]},
'm2': {'b2': [lkgr_lib.Build(1, self.good, 1)]}}
revisions = [1]
candidate = lkgr_lib.FindLKGRCandidate(
build_history, revisions, self.keyfunc, self.status_stub)
self.assertEquals(candidate, 1)
def testModerateFailsOne(self):
build_history = {
'm1': {'b1': [lkgr_lib.Build(1, self.good, 1)]},
'm2': {'b2': [lkgr_lib.Build(1, self.fail, 1)]}}
revisions = [1]
candidate = lkgr_lib.FindLKGRCandidate(
build_history, revisions, self.keyfunc, self.status_stub)
self.assertEquals(candidate, None)
def testModerateFailsTwo(self):
build_history = {
'm1': {'b1': [lkgr_lib.Build(1, self.fail, 1)]},
'm2': {'b2': [lkgr_lib.Build(1, self.good, 1)]}}
revisions = [1]
candidate = lkgr_lib.FindLKGRCandidate(
build_history, revisions, self.keyfunc, self.status_stub)
self.assertEquals(candidate, None)
def testMultipleRevHistory(self):
build_history = {
'm1': {'b1': [lkgr_lib.Build(1, self.fail, 1),
lkgr_lib.Build(2, self.good, 2),
lkgr_lib.Build(3, self.fail, 3),
lkgr_lib.Build(4, self.good, 4)]},
'm2': {'b2': [lkgr_lib.Build(1, self.fail, 1),
lkgr_lib.Build(2, self.fail, 2),
lkgr_lib.Build(3, self.good, 3),
lkgr_lib.Build(4, self.good, 4)]}}
revisions = [1, 2, 3, 4]
candidate = lkgr_lib.FindLKGRCandidate(
build_history, revisions, self.keyfunc, self.status_stub)
self.assertEquals(candidate, 4)
def testMultipleSuccess(self):
build_history = {
'm1': {'b1': [lkgr_lib.Build(1, self.fail, 1),
lkgr_lib.Build(2, self.good, 2),
lkgr_lib.Build(3, self.fail, 3),
lkgr_lib.Build(4, self.good, 4),
lkgr_lib.Build(5, self.good, 5)]},
'm2': {'b2': [lkgr_lib.Build(1, self.fail, 1),
lkgr_lib.Build(2, self.fail, 2),
lkgr_lib.Build(3, self.good, 3),
lkgr_lib.Build(4, self.good, 4),
lkgr_lib.Build(5, self.good, 5)]}}
revisions = [1, 2, 3, 4, 5]
candidate = lkgr_lib.FindLKGRCandidate(
build_history, revisions, self.keyfunc, self.status_stub)
self.assertEquals(candidate, 5)
def testMissingFails(self):
build_history = {
'm1': {'b1': [lkgr_lib.Build(1, self.fail, 1),
lkgr_lib.Build(2, self.good, 2),
lkgr_lib.Build(3, self.fail, 3),
lkgr_lib.Build(5, self.good, 5)]},
'm2': {'b2': [lkgr_lib.Build(1, self.fail, 1),
lkgr_lib.Build(2, self.fail, 2),
lkgr_lib.Build(3, self.good, 3),
lkgr_lib.Build(4, self.good, 4)]}}
revisions = [1, 2, 3, 4, 5]
candidate = lkgr_lib.FindLKGRCandidate(
build_history, revisions, self.keyfunc, self.status_stub)
self.assertEquals(candidate, None)
def testMissingSuccess(self):
build_history = {
'm1': {'b1': [lkgr_lib.Build(1, self.fail, 1),
lkgr_lib.Build(2, self.good, 2),
lkgr_lib.Build(3, self.fail, 3),
lkgr_lib.Build(5, self.good, 5)]},
'm2': {'b2': [lkgr_lib.Build(1, self.fail, 1),
lkgr_lib.Build(2, self.fail, 2),
lkgr_lib.Build(3, self.good, 3),
lkgr_lib.Build(4, self.good, 4),
lkgr_lib.Build(6, self.good, 6)]}}
revisions = [1, 2, 3, 4, 5, 6]
candidate = lkgr_lib.FindLKGRCandidate(
build_history, revisions, self.keyfunc, self.status_stub)
self.assertEquals(candidate, 5)
def testNoBuilderHistory(self):
build_history = {
'm1': {'b1': []},
}
revisions = [1]
def allow_norev_keyfunc(val):
if val is lkgr_lib.NOREV:
return -1
return int(val)
candidate = lkgr_lib.FindLKGRCandidate(
build_history, revisions, allow_norev_keyfunc, self.status_stub)
self.assertEquals(candidate, None)
class CheckLKGRLagTest(unittest.TestCase):
allowed_lag = 2 # Default allowed lag is 2 hours
allowed_gap = 150 # Default allowed gap is 150 revisions
@staticmethod
def lag(minutes):
return datetime.timedelta(minutes=minutes)
def testNoGapSucceeds(self):
res = lkgr_lib.CheckLKGRLag(self.lag(1), 0,
self.allowed_lag, self.allowed_gap)
self.assertTrue(res)
def testNoLagSucceeds(self):
res = lkgr_lib.CheckLKGRLag(self.lag(0), 1,
self.allowed_lag, self.allowed_gap)
self.assertTrue(res)
def testSimpleSucceeds(self):
res = lkgr_lib.CheckLKGRLag(self.lag(15), 10,
self.allowed_lag, self.allowed_gap)
self.assertTrue(res)
def testLagFails(self):
res = lkgr_lib.CheckLKGRLag(self.lag(60 * 4), 150,
self.allowed_lag, self.allowed_gap)
self.assertFalse(res)
def testFlexLagSucceeds(self):
res = lkgr_lib.CheckLKGRLag(self.lag(60 * 4), 10,
self.allowed_lag, self.allowed_gap)
self.assertTrue(res)
class LoadBuildsTest(unittest.TestCase):
def testSuccess(self):
try:
f = tempfile.NamedTemporaryFile(suffix='.json', delete=False)
with f:
f.write(json.dumps({
'builds': {
'master1': {
'builder1': [
[123, lkgr_lib.STATUS.SUCCESS, '01234567']
]
},
},
'version': lkgr_lib._BUILD_DATA_VERSION,
}))
expected_builds = {
'master1': {
'builder1': [
lkgr_lib.Build(123, lkgr_lib.STATUS.SUCCESS, '01234567'),
]
}
}
actual_builds = lkgr_lib.LoadBuilds(f.name)
self.assertEquals(actual_builds, expected_builds)
finally:
if os.path.exists(f.name): # pragma: no branch
os.unlink(f.name)
def testOldVersion(self):
try:
f = tempfile.NamedTemporaryFile(suffix='.json', delete=False)
with f:
f.write(json.dumps({
'builds': {
'master1': {
'builder1': [
[123, lkgr_lib.STATUS.SUCCESS, '01234567']
]
},
},
'version': lkgr_lib._BUILD_DATA_VERSION - 1,
}))
expected_builds = None
actual_builds = lkgr_lib.LoadBuilds(f.name)
self.assertEquals(actual_builds, expected_builds)
finally:
if os.path.exists(f.name): # pragma: no branch
os.unlink(f.name)
def testNoVersion(self):
try:
f = tempfile.NamedTemporaryFile(suffix='.json', delete=False)
with f:
f.write(json.dumps({
'master1': {
'builder1': [
[123, lkgr_lib.STATUS.SUCCESS, '01234567']
]
},
}))
expected_builds = None
actual_builds = lkgr_lib.LoadBuilds(f.name)
self.assertEquals(actual_builds, expected_builds)
finally:
if os.path.exists(f.name): # pragma: no branch
os.unlink(f.name)
class DumpBuildsTest(unittest.TestCase):
def testSuccess(self):
try:
f = tempfile.NamedTemporaryFile(suffix='.json', delete=False)
builds = {
'master1': {
'builder1': [
lkgr_lib.Build(123, lkgr_lib.STATUS.SUCCESS, '01234567'),
]
}
}
expected_contents = {
'builds': {
'master1': {
'builder1': [
[123, lkgr_lib.STATUS.SUCCESS, '01234567'],
]
}
},
'version': lkgr_lib._BUILD_DATA_VERSION,
}
lkgr_lib.DumpBuilds(builds, f.name)
with f:
actual_contents = json.load(f)
self.assertEquals(actual_contents, expected_contents)
finally:
if os.path.exists(f.name): # pragma: no branch
os.unlink(f.name)