blob: b5a59486c7f63b2f91b4f29e627aa891e2a69b40 [file] [log] [blame]
#!/usr/bin/env vpython3
# Copyright 2017 The LUCI Authors. All rights reserved.
# Use of this source code is governed under the Apache License, Version 2.0
# that can be found in the LICENSE file.
import hashlib
import struct
import sys
import test_env
from recipe_engine.internal.autoroll_impl.commit_list import \
BackwardsRoll, CommitMetadata, CommitList, UnknownCommit
from PB.recipe_engine.recipes_cfg import RepoSpec
class BaseCommitTest(test_env.RecipeEngineUnitTest):
def __init__(self, *args, **kwargs):
super(BaseCommitTest, self).__init__(*args, **kwargs)
self.cm_counter = 0
self.cm_timestamp = 0
def setUp(self):
super(BaseCommitTest, self).setUp()
self.cm_counter = 0
self.cm_timestamp = 1501807893
def cm(self, repo='repo', deps=(), revision=None,
author_email='author@example.com', commit_timestamp=None,
message_lines=('message', 'lines'), roll_candidate=False):
spec = RepoSpec(
api_version=2,
repo_name=repo,
canonical_repo_url='https://git.example.com/%s.git' % repo,
)
for commit in deps:
repo = commit.spec.repo_name
spec.deps[repo].url = commit.spec.canonical_repo_url
spec.deps[repo].revision = commit.revision
if revision is None:
revision = hashlib.sha1(struct.pack('!Q', self.cm_counter)).hexdigest()
self.cm_counter += 1
if commit_timestamp is None:
# Increase the commit timestamp by a non-fixed, yet deterministic, amount
# so that commit timestamps are monotonically increasing.
commit_timestamp = self.cm_timestamp
self.cm_timestamp += 60 * (int(revision[:2], 16)+1)
return CommitMetadata(revision, author_email, commit_timestamp,
message_lines, spec, roll_candidate)
def cl(self, count):
return CommitList([self.cm() for _ in range(count)])
class TestCommitList(BaseCommitTest):
def test_empty(self):
with self.assertRaisesRegexp(AssertionError, 'is empty'):
CommitList('fake-repo', 'fake-branch', [])
def test_single(self):
c = self.cm()
cl = CommitList('fake-repo', 'fake-branch', [c])
self.assertEqual(len(cl), 1)
cursor = cl.cursor()
self.assertEqual(cursor.current, c)
self.assertEqual(cursor.next_roll_candidate, None)
def test_five(self):
cs = [self.cm() for _ in range(5)]
cl = CommitList('fake-repo', 'fake-branch', cs)
self.assertEqual(len(cl), 5)
cursor = cl.cursor()
self.assertEqual(cursor.current, cs[0])
cursor.advance_to(cs[1].revision)
self.assertEqual(cursor.current, cs[1])
self.assertEqual(cl.lookup(cs[0].revision), cs[0])
with self.assertRaises(UnknownCommit):
cursor.advance_to('not_a_known_commit')
self.assertEqual(cursor.current, cs[1])
cursor.advance_to(cs[4].revision)
self.assertEqual(cursor.current, cs[4])
with self.assertRaises(BackwardsRoll):
cursor.advance_to(cs[3].revision)
self.assertEqual(cursor.current, cs[4])
def test_dist(self):
cs = [self.cm() for _ in range(5)]
cl = CommitList('fake-repo', 'fake-branch', cs)
# 1 revision
self.assertEqual(cl.dist(cs[0].revision), 0)
self.assertEqual(cl.dist(cs[4].revision), 4)
self.assertIsNone(cl.dist('unknown-revision'))
# 2 revisions
self.assertEqual(cl.dist(cs[0].revision, cs[4].revision), 4)
self.assertEqual(cl.dist(cs[1].revision, cs[3].revision), 2)
self.assertIsNone(cl.dist(cs[4].revision, cs[0].revision))
self.assertIsNone(cl.dist(cs[0].revision, 'unknown-revision'))
with self.assertRaises(UnknownCommit):
cl.dist('unknown-revision', cs[4].revision)
def test_compatibility(self):
cs1 = [self.cm('1') for _ in range(5)]
cs2 = [
self.cm('2', [cs1[3]]), # simulates an out-of-order dependency (revert)
self.cm('2', [cs1[0]]),
self.cm('2', [cs1[0]]),
self.cm('2', [cs1[1]]),
self.cm('2', [cs1[3]]),
self.cm('2'), # simulates a dep being removed
self.cm('2', [cs1[3]]), # simulates a dep being added
]
cs3 = [self.cm('3') for _ in range(3)]
cl1 = CommitList('fake-repo-1', 'fake-branch', cs1)
cl2 = CommitList('fake-repo-2', 'fake-branch', cs2)
cl3 = CommitList('fake-repo-3', 'fake-branch', cs3)
self.assertTrue(cl2.is_compatible(cs2[0].revision, {'1': cs1[3].revision}))
self.assertFalse(cl2.is_compatible(cs2[0].revision, {'1': cs1[0].revision}))
self.assertTrue(cl2.is_compatible(cs2[0].revision, {'3': cs3[0].revision}))
self.assertTrue(cl3.is_compatible(cs3[0].revision, {'2': cs2[0].revision}))
self.assertEqual(
cl2.compatible_commits({
'1': cs1[3].revision,
'3': cs3[0].revision
}), [cs2[0], cs2[4], cs2[5], cs2[6]])
if __name__ == '__main__':
sys.exit(test_env.main())