diff options
author | Mike Frysinger <vapier@chromium.org> | 2012-05-24 18:17:38 -0400 |
---|---|---|
committer | Gerrit <chrome-bot@google.com> | 2012-05-31 09:49:15 -0700 |
commit | 6cb624a11596132868ee384ab1a2b0a1330bca5d (patch) | |
tree | 5f27c25f60ac7f5187d8f92d23cb744991934292 /scripts | |
parent | e522add7f9cf7d6d573a66e36a9c7e47fca0e1d6 (diff) | |
download | chromite-6cb624a11596132868ee384ab1a2b0a1330bca5d.tar.gz |
cros_mark_{chrome_,}as_stable: relocate to scripts/
We don't import these as modules, just execute them directly, so move
them to the scripts/ subdir.
BUG=None
TEST=`./buildbot/run_tests` passes
TEST=`cbuildbot arm-tegra2-paladin` with manual uprev works
TEST=`cbuildbot x86-generic-tot-chrome-pfq-informational` passes
Change-Id: I33b064aafa06b506022ba930c953f625bbb13795
Reviewed-on: https://gerrit.chromium.org/gerrit/23569
Commit-Ready: Mike Frysinger <vapier@chromium.org>
Reviewed-by: Mike Frysinger <vapier@chromium.org>
Tested-by: Mike Frysinger <vapier@chromium.org>
Diffstat (limited to 'scripts')
-rw-r--r-- | scripts/cros_mark_as_stable.py | 329 | ||||
-rwxr-xr-x | scripts/cros_mark_as_stable_unittest.py | 112 | ||||
-rw-r--r-- | scripts/cros_mark_chrome_as_stable.py | 509 | ||||
-rwxr-xr-x | scripts/cros_mark_chrome_as_stable_unittest.py | 326 |
4 files changed, 1276 insertions, 0 deletions
diff --git a/scripts/cros_mark_as_stable.py b/scripts/cros_mark_as_stable.py new file mode 100644 index 000000000..beebccd56 --- /dev/null +++ b/scripts/cros_mark_as_stable.py @@ -0,0 +1,329 @@ +#!/usr/bin/python + +# Copyright (c) 2012 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""This module uprevs a given package's ebuild to the next revision.""" + +import multiprocessing +import optparse +import os +import sys + +from chromite.buildbot import constants +from chromite.buildbot import cbuildbot_background as background +from chromite.buildbot import portage_utilities +from chromite.lib import cros_build_lib + + +# TODO(sosa): Remove during OO refactor. +VERBOSE = False + +# Dictionary of valid commands with usage information. +COMMAND_DICTIONARY = { + 'commit': + 'Marks given ebuilds as stable locally', + 'push': + 'Pushes previous marking of ebuilds to remote repo', + } + + +# ======================= Global Helper Functions ======================== + + +def _Print(message): + """Verbose print function.""" + if VERBOSE: + cros_build_lib.Info(message) + + +def CleanStalePackages(boards, package_atoms): + """Cleans up stale package info from a previous build. + Args: + boards: Boards to clean the packages from. + package_atoms: The actual package atom to unmerge. + """ + if package_atoms: + cros_build_lib.Info('Cleaning up stale packages %s.' % package_atoms) + + # First unmerge all the packages for a board, then eclean it. + # We need these two steps to run in order (unmerge/eclean), + # but we can let all the boards run in parallel. + def _CleanStalePackages(board): + if board: + suffix = '-' + board + runcmd = cros_build_lib.RunCommand + else: + suffix = '' + runcmd = cros_build_lib.SudoRunCommand + + if package_atoms: + runcmd(['emerge' + suffix, '-q', '--unmerge'] + package_atoms); + runcmd(['eclean' + suffix, '-d', 'packages'], + redirect_stdout=True, redirect_stderr=True) + + tasks = [] + for board in boards: + tasks.append([board]) + tasks.append([None]) + + background.RunTasksInProcessPool(_CleanStalePackages, tasks) + + +# TODO(build): This code needs to be gutted and rebased to cros_build_lib. +def _DoWeHaveLocalCommits(stable_branch, tracking_branch, cwd): + """Returns true if there are local commits.""" + current_branch = cros_build_lib.GetCurrentBranch(cwd) + + if current_branch != stable_branch: + return False + output = cros_build_lib.RunGitCommand( + cwd, ['rev-parse', 'HEAD', tracking_branch]).output.split() + return output[0] != output[1] + + +def _CheckSaneArguments(package_list, command, options): + """Checks to make sure the flags are sane. Dies if arguments are not sane.""" + if not command in COMMAND_DICTIONARY.keys(): + _PrintUsageAndDie('%s is not a valid command' % command) + if not options.packages and command == 'commit' and not options.all: + _PrintUsageAndDie('Please specify at least one package') + if not options.boards and command == 'commit': + _PrintUsageAndDie('Please specify a board') + if not os.path.isdir(options.srcroot): + _PrintUsageAndDie('srcroot is not a valid path') + options.srcroot = os.path.abspath(options.srcroot) + + +def _PrintUsageAndDie(error_message=''): + """Prints optional error_message the usage and returns an error exit code.""" + command_usage = 'Commands: \n' + # Add keys and usage information from dictionary. + commands = sorted(COMMAND_DICTIONARY.keys()) + for command in commands: + command_usage += ' %s: %s\n' % (command, COMMAND_DICTIONARY[command]) + commands_str = '|'.join(commands) + cros_build_lib.Warning('Usage: %s FLAGS [%s]\n\n%s' % ( + sys.argv[0], commands_str, command_usage)) + if error_message: + cros_build_lib.Die(error_message) + else: + sys.exit(1) + + +# ======================= End Global Helper Functions ======================== + + +def PushChange(stable_branch, tracking_branch, dryrun, cwd): + """Pushes commits in the stable_branch to the remote git repository. + + Pushes local commits from calls to CommitChange to the remote git + repository specified by current working directory. If changes are + found to commit, they will be merged to the merge branch and pushed. + In that case, the local repository will be left on the merge branch. + + Args: + stable_branch: The local branch with commits we want to push. + tracking_branch: The tracking branch of the local branch. + dryrun: Use git push --dryrun to emulate a push. + cwd: The directory to run commands in. + Raises: + OSError: Error occurred while pushing. + """ + if not _DoWeHaveLocalCommits(stable_branch, tracking_branch, cwd): + cros_build_lib.Info('No work found to push in %s. Exiting', cwd) + return + + # For the commit queue, our local branch may contain commits that were + # just tested and pushed during the CommitQueueCompletion stage. Sync + # and rebase our local branch on top of the remote commits. + remote, push_branch = cros_build_lib.GetTrackingBranch(cwd, for_push=True) + cros_build_lib.SyncPushBranch(cwd, remote, push_branch) + + # Check whether any local changes remain after the sync. + if not _DoWeHaveLocalCommits(stable_branch, push_branch, cwd): + cros_build_lib.Info('All changes already pushed for %s. Exiting', cwd) + return + + description = cros_build_lib.RunCommandCaptureOutput( + ['git', 'log', '--format=format:%s%n%n%b', '%s..%s' % ( + push_branch, stable_branch)], cwd=cwd).output + description = 'Marking set of ebuilds as stable\n\n%s' % description + cros_build_lib.Info('For %s, using description %s', cwd, description) + cros_build_lib.CreatePushBranch(constants.MERGE_BRANCH, cwd) + cros_build_lib.RunGitCommand(cwd, ['merge', '--squash', stable_branch]) + cros_build_lib.RunGitCommand(cwd, ['commit', '-m', description]) + cros_build_lib.RunGitCommand(cwd, ['config', 'push.default', 'tracking']) + cros_build_lib.GitPushWithRetry(constants.MERGE_BRANCH, cwd, + dryrun=dryrun) + + +class GitBranch(object): + """Wrapper class for a git branch.""" + + def __init__(self, branch_name, tracking_branch, cwd): + """Sets up variables but does not create the branch.""" + self.branch_name = branch_name + self.tracking_branch = tracking_branch + self.cwd = cwd + + def CreateBranch(self): + self.Checkout() + + def Checkout(self, branch=None): + """Function used to check out to another GitBranch.""" + if not branch: + branch = self.branch_name + if branch == self.tracking_branch or self.Exists(branch): + git_cmd = ['git', 'checkout', '-f', branch] + else: + git_cmd = ['repo', 'start', branch, '.'] + cros_build_lib.RunCommandCaptureOutput(git_cmd, print_cmd=False, + cwd=self.cwd) + + def Exists(self, branch=None): + """Returns True if the branch exists.""" + if not branch: + branch = self.branch_name + branches = cros_build_lib.RunCommandCaptureOutput(['git', 'branch'], + print_cmd=False, + cwd=self.cwd).output + return branch in branches.split() + + +def main(argv): + parser = optparse.OptionParser('cros_mark_as_stable OPTIONS packages') + parser.add_option('--all', action='store_true', + help='Mark all packages as stable.') + parser.add_option('-b', '--boards', + help='Colon-separated list of boards') + parser.add_option('--drop_file', + help='File to list packages that were revved.') + parser.add_option('--dryrun', action='store_true', + help='Passes dry-run to git push if pushing a change.') + parser.add_option('-o', '--overlays', + help='Colon-separated list of overlays to modify.') + parser.add_option('-p', '--packages', + help='Colon separated list of packages to rev.') + parser.add_option('-r', '--srcroot', + default='%s/trunk/src' % os.environ['HOME'], + help='Path to root src directory.') + parser.add_option('--verbose', action='store_true', + help='Prints out debug info.') + (options, args) = parser.parse_args() + + global VERBOSE + VERBOSE = options.verbose + portage_utilities.EBuild.VERBOSE = options.verbose + + if len(args) != 1: + _PrintUsageAndDie('Must specify a valid command [commit, push]') + + command = args[0] + package_list = None + if options.packages: + package_list = options.packages.split(':') + + _CheckSaneArguments(package_list, command, options) + if options.overlays: + overlays = {} + for path in options.overlays.split(':'): + if not os.path.isdir(path): + cros_build_lib.Die('Cannot find overlay: %s' % path) + overlays[path] = [] + else: + cros_build_lib.Warning('Missing --overlays argument') + overlays = { + '%s/private-overlays/chromeos-overlay' % options.srcroot: [], + '%s/third_party/chromiumos-overlay' % options.srcroot: [] + } + + if command == 'commit': + portage_utilities.BuildEBuildDictionary( + overlays, options.all, package_list) + + manifest = cros_build_lib.ManifestCheckout.Cached(options.srcroot) + + # Contains the array of packages we actually revved. + revved_packages = [] + new_package_atoms = [] + + # Slight optimization hack: process the chromiumos overlay before any other + # cros-workon overlay first so we can do background cache generation in it. + # A perfect solution would walk all the overlays, figure out any dependencies + # between them (with layout.conf), and then process them in dependency order. + # However, this operation isn't slow enough to warrant that level of + # complexity, so we'll just special case the main overlay. + # + # Similarly, generate the cache in the portage-stable tree asap. We know + # we won't have any cros-workon packages in there, so generating the cache + # is the only thing it'll be doing. The chromiumos overlay instead might + # have revbumping to do before it can generate the cache. + keys = overlays.keys() + for overlay in ('/third_party/chromiumos-overlay', + '/third_party/portage-stable'): + for k in keys: + if k.endswith(overlay): + keys.remove(k) + keys.insert(0, k) + break + + cache_queue = multiprocessing.Queue() + with background.BackgroundTaskRunner(cache_queue, + portage_utilities.RegenCache): + for overlay in keys: + ebuilds = overlays[overlay] + if not os.path.isdir(overlay): + cros_build_lib.Warning("Skipping %s" % overlay) + continue + + # Note we intentionally work from the non push tracking branch; + # everything built thus far has been against it (meaning, http mirrors), + # thus we should honor that. During the actual push, the code switches + # to the correct urls, and does an appropriate rebasing. + tracking_branch = cros_build_lib.GetTrackingBranchViaManifest( + overlay, manifest=manifest)[1] + + if command == 'push': + PushChange(constants.STABLE_EBUILD_BRANCH, tracking_branch, + options.dryrun, cwd=overlay) + elif command == 'commit' and ebuilds: + existing_branch = cros_build_lib.GetCurrentBranch(overlay) + work_branch = GitBranch(constants.STABLE_EBUILD_BRANCH, tracking_branch, + cwd=overlay) + work_branch.CreateBranch() + if not work_branch.Exists(): + cros_build_lib.Die('Unable to create stabilizing branch in %s' % + overlay) + + # In the case of uprevving overlays that have patches applied to them, + # include the patched changes in the stabilizing branch. + if existing_branch: + cros_build_lib.RunCommand(['git', 'rebase', existing_branch], + print_cmd=False, cwd=overlay) + + for ebuild in ebuilds: + try: + _Print('Working on %s' % ebuild.package) + new_package = ebuild.RevWorkOnEBuild(options.srcroot) + if new_package: + revved_packages.append(ebuild.package) + new_package_atoms.append('=%s' % new_package) + except (OSError, IOError): + cros_build_lib.Warning('Cannot rev %s\n' % ebuild.package + + 'Note you will have to go into %s ' + 'and reset the git repo yourself.' % overlay) + raise + + if command == 'commit': + # Regenerate caches if need be. We do this all the time to + # catch when users make changes without updating cache files. + cache_queue.put([overlay]) + + if command == 'commit': + CleanStalePackages(options.boards.split(':'), new_package_atoms) + if options.drop_file: + fh = open(options.drop_file, 'w') + fh.write(' '.join(revved_packages)) + fh.close() diff --git a/scripts/cros_mark_as_stable_unittest.py b/scripts/cros_mark_as_stable_unittest.py new file mode 100755 index 000000000..635de287c --- /dev/null +++ b/scripts/cros_mark_as_stable_unittest.py @@ -0,0 +1,112 @@ +#!/usr/bin/python + +# Copyright (c) 2012 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Unit tests for cros_mark_as_stable.py.""" + +import mox +import os +import sys +import unittest + +sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)), + '..', '..')) +from chromite.buildbot import constants +from chromite.buildbot import constants +from chromite.lib import cros_build_lib +from chromite.scripts import cros_mark_as_stable + + +# pylint: disable=W0212,R0904 +class NonClassTests(mox.MoxTestBase): + def setUp(self): + mox.MoxTestBase.setUp(self) + self.mox.StubOutWithMock(cros_build_lib, 'RunCommand') + self.mox.StubOutWithMock(cros_build_lib, 'RunCommandCaptureOutput') + self._branch = 'test_branch' + self._target_manifest_branch = 'cros/master' + + def testPushChange(self): + git_log = 'Marking test_one as stable\nMarking test_two as stable\n' + fake_description = 'Marking set of ebuilds as stable\n\n%s' % git_log + self.mox.StubOutWithMock(cros_mark_as_stable, '_DoWeHaveLocalCommits') + self.mox.StubOutWithMock(cros_mark_as_stable.GitBranch, 'CreateBranch') + self.mox.StubOutWithMock(cros_mark_as_stable.GitBranch, 'Exists') + self.mox.StubOutWithMock(cros_build_lib, 'GitPushWithRetry') + self.mox.StubOutWithMock(cros_build_lib, 'GetTrackingBranch') + self.mox.StubOutWithMock(cros_build_lib, 'SyncPushBranch') + self.mox.StubOutWithMock(cros_build_lib, 'CreatePushBranch') + self.mox.StubOutWithMock(cros_build_lib, 'RunGitCommand') + + cros_mark_as_stable._DoWeHaveLocalCommits( + self._branch, self._target_manifest_branch, '.').AndReturn(True) + cros_build_lib.GetTrackingBranch('.', for_push=True).AndReturn( + ['gerrit', 'refs/remotes/gerrit/master']) + cros_build_lib.SyncPushBranch('.', 'gerrit', 'refs/remotes/gerrit/master') + cros_mark_as_stable._DoWeHaveLocalCommits( + self._branch, 'refs/remotes/gerrit/master', '.').AndReturn(True) + result = cros_build_lib.CommandResult(output=git_log) + cros_build_lib.RunCommandCaptureOutput( + ['git', 'log', '--format=format:%s%n%n%b', + 'refs/remotes/gerrit/master..%s' % self._branch], + cwd='.').AndReturn(result) + cros_build_lib.CreatePushBranch('merge_branch', '.') + cros_build_lib.RunGitCommand('.', ['merge', '--squash', self._branch]) + cros_build_lib.RunGitCommand('.', ['commit', '-m', fake_description]) + cros_build_lib.RunGitCommand('.', ['config', 'push.default', 'tracking']) + cros_build_lib.GitPushWithRetry('merge_branch', '.', dryrun=False) + self.mox.ReplayAll() + cros_mark_as_stable.PushChange(self._branch, self._target_manifest_branch, + False, '.') + self.mox.VerifyAll() + + +class GitBranchTest(mox.MoxTestBase): + + def setUp(self): + mox.MoxTestBase.setUp(self) + # Always stub RunCommmand out as we use it in every method. + self.mox.StubOutWithMock(cros_build_lib, 'RunCommand') + self.mox.StubOutWithMock(cros_build_lib, 'RunCommandCaptureOutput') + self._branch = self.mox.CreateMock(cros_mark_as_stable.GitBranch) + self._branch_name = 'test_branch' + self._branch.branch_name = self._branch_name + self._target_manifest_branch = 'cros/test' + self._branch.tracking_branch = self._target_manifest_branch + self._branch.cwd = '.' + + def testCheckoutCreate(self): + # Test init with no previous branch existing. + self._branch.Exists(self._branch_name).AndReturn(False) + cros_build_lib.RunCommandCaptureOutput(['repo', 'start', self._branch_name, + '.'], print_cmd=False, cwd='.') + self.mox.ReplayAll() + cros_mark_as_stable.GitBranch.Checkout(self._branch) + self.mox.VerifyAll() + + def testCheckoutNoCreate(self): + # Test init with previous branch existing. + self._branch.Exists(self._branch_name).AndReturn(True) + cros_build_lib.RunCommandCaptureOutput(['git', 'checkout', '-f', + self._branch_name], print_cmd=False, + cwd='.') + self.mox.ReplayAll() + cros_mark_as_stable.GitBranch.Checkout(self._branch) + self.mox.VerifyAll() + + def testExists(self): + branch = cros_mark_as_stable.GitBranch(self._branch_name, + self._target_manifest_branch, '.') + # Test if branch exists that is created + result = cros_build_lib.CommandResult(output=self._branch_name + '\n') + cros_build_lib.RunCommandCaptureOutput(['git', 'branch'], print_cmd=False, + cwd='.').AndReturn(result) + self.mox.ReplayAll() + self.assertTrue(branch.Exists()) + self.mox.VerifyAll() + + +if __name__ == '__main__': + unittest.main() diff --git a/scripts/cros_mark_chrome_as_stable.py b/scripts/cros_mark_chrome_as_stable.py new file mode 100644 index 000000000..6bc78795a --- /dev/null +++ b/scripts/cros_mark_chrome_as_stable.py @@ -0,0 +1,509 @@ +#!/usr/bin/python + +# Copyright (c) 2012 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""This module uprevs Chrome for cbuildbot. + +After calling, it prints outs CHROME_VERSION_ATOM=(version atom string). A +caller could then use this atom with emerge to build the newly uprevved version +of Chrome e.g. + +./cros_mark_chrome_as_stable tot +Returns chrome-base/chromeos-chrome-8.0.552.0_alpha_r1 + +emerge-x86-generic =chrome-base/chromeos-chrome-8.0.552.0_alpha_r1 +""" + +import filecmp +import optparse +import os +import re +import sys +import time + +from chromite.buildbot import constants +from chromite.buildbot import portage_utilities +from chromite.lib import cros_build_lib +from chromite.lib.cros_build_lib import RunCommand, Info, Warning +from chromite.scripts import cros_mark_as_stable + +BASE_CHROME_SVN_URL = 'http://src.chromium.org/svn' + +# Helper regex's for finding ebuilds. +_CHROME_VERSION_REGEX = '\d+\.\d+\.\d+\.\d+' +_NON_STICKY_REGEX = '%s[(_rc.*)|(_alpha.*)]+' % _CHROME_VERSION_REGEX + +# Dir where all the action happens. +_CHROME_OVERLAY_DIR = ('%(srcroot)s/third_party/chromiumos-overlay' + '/chromeos-base/chromeos-chrome') + +_GIT_COMMIT_MESSAGE = ('Marking %(chrome_rev)s for chrome ebuild with version ' + '%(chrome_version)s as stable.') + +# URLs that print lists of chrome revisions between two versions of the browser. +_CHROME_VERSION_URL = ('http://omahaproxy.appspot.com/changelog?' + 'old_version=%(old)s&new_version=%(new)s') + +# Only print links when we rev these types. +_REV_TYPES_FOR_LINKS = [constants.CHROME_REV_LATEST, + constants.CHROME_REV_STICKY] + +_CHROME_SVN_TAG = 'CROS_SVN_COMMIT' + +def _GetSvnUrl(base_url): + """Returns the path to the svn url for the given chrome branch.""" + return os.path.join(base_url, 'trunk') + + +def _GetTipOfTrunkSvnRevision(base_url): + """Returns the current svn revision for the chrome tree.""" + svn_url = _GetSvnUrl(base_url) + svn_info = RunCommand(['svn', 'info', svn_url], redirect_stdout=True).output + + revision_re = re.compile('^Revision:\s+(\d+).*') + for line in svn_info.splitlines(): + match = revision_re.search(line) + if match: + svn_revision = match.group(1) + Info('Found SVN Revision %s' % svn_revision) + return svn_revision + + raise Exception('Could not find revision information from %s' % svn_url) + + +def _GetVersionContents(chrome_version_info): + """Returns the current Chromium version, from the contents of a VERSION file. + + Args: + chrome_version_info: The contents of a chromium VERSION file. + """ + chrome_version_array = [] + for line in chrome_version_info.splitlines(): + chrome_version_array.append(line.rpartition('=')[2]) + + return '.'.join(chrome_version_array) + +def _GetSpecificVersionUrl(base_url, revision, time_to_wait=600): + """Returns the Chromium version, from a repository URL and version. + + Args: + base_url: URL for the root of the chromium checkout. + revision: the SVN revision we want to use. + time_to_wait: the minimum period before abandoning our wait for the + desired revision to be present. + """ + svn_url = os.path.join(_GetSvnUrl(base_url), 'src', 'chrome', 'VERSION') + if not revision or not (int(revision) > 0): + raise Exception('Revision must be positive, got %s' % revision) + + start = time.time() + # Use the fact we are SVN, hence ordered. + # Dodge the fact it will silently ignore the revision if it is not + # yet known. (i.e. too high) + repo_version = _GetTipOfTrunkSvnRevision(base_url) + while revision > repo_version: + if time.time() - start > time_to_wait: + raise Exception('Timeout Exceeeded') + + Info('Repository only has version %s, looking for %s. Sleeping...' % + (repo_version, revision)) + time.sleep(30) + repo_version = _GetTipOfTrunkSvnRevision(base_url) + + chrome_version_info = RunCommand( + ['svn', 'cat', '-r', revision, svn_url], + redirect_stdout=True, + error_message='Could not read version file at %s revision %s.' % + (svn_url, revision)).output + + return _GetVersionContents(chrome_version_info) + + +def _GetTipOfTrunkVersionFile(root): + """Returns the current Chromium version, from a file in a checkout. + + Args: + root: path to the root of the chromium checkout. + """ + version_file = os.path.join(root, 'src', 'chrome', 'VERSION') + chrome_version_info = RunCommand( + ['cat', version_file], + redirect_stdout=True, + error_message='Could not read version file at %s.' % version_file).output + + return _GetVersionContents(chrome_version_info) + +def _GetLatestRelease(base_url, branch=None): + """Gets the latest release version from the buildspec_url for the branch. + + Args: + branch: If set, gets the latest release for branch, otherwise latest + release. + Returns: + Latest version string. + """ + buildspec_url = os.path.join(base_url, 'releases') + svn_ls = RunCommand(['svn', 'ls', buildspec_url], + redirect_stdout=True).output + sorted_ls = RunCommand(['sort', '--version-sort', '-r'], input=svn_ls, + redirect_stdout=True).output + if branch: + chrome_version_re = re.compile('^%s\.\d+.*' % branch) + else: + chrome_version_re = re.compile('^[0-9]+\..*') + + for chrome_version in sorted_ls.splitlines(): + if chrome_version_re.match(chrome_version): + deps_url = os.path.join(buildspec_url, chrome_version, 'DEPS') + deps_check = RunCommand(['svn', 'ls', deps_url], + error_ok=True, + redirect_stdout=True).output + if deps_check == 'DEPS\n': + return chrome_version.rstrip('/') + + return None + + +def _GetStickyEBuild(stable_ebuilds): + """Returns the sticky ebuild.""" + sticky_ebuilds = [] + non_sticky_re = re.compile(_NON_STICKY_REGEX) + for ebuild in stable_ebuilds: + if not non_sticky_re.match(ebuild.version): + sticky_ebuilds.append(ebuild) + + if not sticky_ebuilds: + raise Exception('No sticky ebuilds found') + elif len(sticky_ebuilds) > 1: + Warning('More than one sticky ebuild found') + + return portage_utilities.BestEBuild(sticky_ebuilds) + + +class ChromeEBuild(portage_utilities.EBuild): + """Thin sub-class of EBuild that adds a chrome_version field.""" + chrome_version_re = re.compile('.*chromeos-chrome-(%s|9999).*' % ( + _CHROME_VERSION_REGEX)) + chrome_version = '' + + def __init__(self, path): + portage_utilities.EBuild.__init__(self, path) + re_match = self.chrome_version_re.match(self.ebuild_path_no_revision) + if re_match: + self.chrome_version = re_match.group(1) + + def __str__(self): + return self.ebuild_path + + +def FindChromeCandidates(overlay_dir): + """Return a tuple of chrome's unstable ebuild and stable ebuilds. + + Args: + overlay_dir: The path to chrome's portage overlay dir. + Returns: + Tuple [unstable_ebuild, stable_ebuilds]. + Raises: + Exception: if no unstable ebuild exists for Chrome. + """ + stable_ebuilds = [] + unstable_ebuilds = [] + for path in [ + os.path.join(overlay_dir, entry) for entry in os.listdir(overlay_dir)]: + if path.endswith('.ebuild'): + ebuild = ChromeEBuild(path) + if not ebuild.chrome_version: + Warning('Poorly formatted ebuild found at %s' % path) + else: + if '9999' in ebuild.version: + unstable_ebuilds.append(ebuild) + else: + stable_ebuilds.append(ebuild) + + # Apply some sanity checks. + if not unstable_ebuilds: + raise Exception('Missing 9999 ebuild for %s' % overlay_dir) + if not stable_ebuilds: + Warning('Missing stable ebuild for %s' % overlay_dir) + + return portage_utilities.BestEBuild(unstable_ebuilds), stable_ebuilds + + +def FindChromeUprevCandidate(stable_ebuilds, chrome_rev, sticky_branch): + """Finds the Chrome uprev candidate for the given chrome_rev. + + Using the pre-flight logic, this means the stable ebuild you are uprevving + from. The difference here is that the version could be different and in + that case we want to find it to delete it. + + Args: + stable_ebuilds: A list of stable ebuilds. + chrome_rev: The chrome_rev designating which candidate to find. + sticky_branch: The the branch that is currently sticky with Major/Minor + components. For example: 9.0.553. Can be None but not if chrome_rev + is CHROME_REV_STICKY. + Returns: + Returns the EBuild, otherwise None if none found. + """ + candidates = [] + if chrome_rev in [constants.CHROME_REV_LOCAL, constants.CHROME_REV_TOT, + constants.CHROME_REV_SPEC]: + # These are labelled alpha, for historic reasons, + # not just for the fun of confusion. + chrome_branch_re = re.compile('%s.*_alpha.*' % _CHROME_VERSION_REGEX) + for ebuild in stable_ebuilds: + if chrome_branch_re.search(ebuild.version): + candidates.append(ebuild) + + elif chrome_rev == constants.CHROME_REV_STICKY: + assert sticky_branch is not None + chrome_branch_re = re.compile('%s\..*' % sticky_branch) + for ebuild in stable_ebuilds: + if chrome_branch_re.search(ebuild.version): + candidates.append(ebuild) + + else: + chrome_branch_re = re.compile('%s.*_rc.*' % _CHROME_VERSION_REGEX) + for ebuild in stable_ebuilds: + if chrome_branch_re.search(ebuild.version): + candidates.append(ebuild) + + if candidates: + return portage_utilities.BestEBuild(candidates) + else: + return None + +def _AnnotateAndPrint(text, url): + """Add buildbot trappings to print <a href='url'>text</a> in the waterfall. + + Args: + text: Anchor text for the link + url: the URL to which to link + """ + print >> sys.stderr, '\n@@@STEP_LINK@%(text)s@%(url)s@@@' % { 'text': text, + 'url': url } + +def GetChromeRevisionLinkFromVersions(old_chrome_version, chrome_version): + """Return appropriately formatted link to revision info, given versions + + Given two chrome version strings (e.g. 9.0.533.0), generate a link to a + page that prints the Chromium revisions between those two versions. + + Args: + old_chrome_version: version to diff from + chrome_version: version to which to diff + Returns: + The desired URL. + """ + return _CHROME_VERSION_URL % { 'old': old_chrome_version, + 'new': chrome_version } + +def GetChromeRevisionListLink(old_chrome, new_chrome, chrome_rev): + """Returns a link to the list of revisions between two Chromium versions + + Given two ChromeEBuilds and the kind of rev we're doing, generate a + link to a page that prints the Chromium changes between those two + revisions, inclusive. + + Args: + old_chrome: ebuild for the version to diff from + new_chrome: ebuild for the version to which to diff + chrome_rev: one of constants.VALID_CHROME_REVISIONS + Returns: + The desired URL. + """ + assert chrome_rev in _REV_TYPES_FOR_LINKS + return GetChromeRevisionLinkFromVersions(old_chrome.chrome_version, + new_chrome.chrome_version) + +def MarkChromeEBuildAsStable(stable_candidate, unstable_ebuild, chrome_rev, + chrome_version, commit, overlay_dir): + """Uprevs the chrome ebuild specified by chrome_rev. + + This is the main function that uprevs the chrome_rev from a stable candidate + to its new version. + + Args: + stable_candidate: ebuild that corresponds to the stable ebuild we are + revving from. If None, builds the a new ebuild given the version + and logic for chrome_rev type with revision set to 1. + unstable_ebuild: ebuild corresponding to the unstable ebuild for chrome. + chrome_rev: one of constants.VALID_CHROME_REVISIONS or LOCAL + constants.CHROME_REV_SPEC - Requires commit value. Revs the ebuild for + the specified version and uses the portage suffix of _alpha. + constants.CHROME_REV_TOT - Requires commit value. Revs the ebuild for + the TOT version and uses the portage suffix of _alpha. + constants.CHROME_REV_LOCAL - Requires a chrome_root. Revs the ebuild for + the local version and uses the portage suffix of _alpha. + constants.CHROME_REV_LATEST - This uses the portage suffix of _rc as they + are release candidates for the next sticky version. + constants.CHROME_REV_STICKY - Revs the sticky version. + chrome_version: The \d.\d.\d.\d version of Chrome. + commit: Used with constants.CHROME_REV_TOT. The svn revision of chrome. + overlay_dir: Path to the chromeos-chrome package dir. + Returns: + Full portage version atom (including rc's, etc) that was revved. + """ + def IsTheNewEBuildRedundant(new_ebuild, stable_ebuild): + """Returns True if the new ebuild is redundant. + + This is True if there if the current stable ebuild is the exact same copy + of the new one OR the chrome versions are the same and we're revving + constants.CHROME_REV_LATEST (as we don't care about 9999 changes for it). + """ + if not stable_ebuild: + return False + + if stable_candidate.chrome_version == new_ebuild.chrome_version: + if chrome_rev == constants.CHROME_REV_LATEST: + return True + else: + return filecmp.cmp( + new_ebuild.ebuild_path, stable_ebuild.ebuild_path, shallow=False) + + base_path = os.path.join(overlay_dir, 'chromeos-chrome-%s' % chrome_version) + # Case where we have the last stable candidate with same version just rev. + if stable_candidate and stable_candidate.chrome_version == chrome_version: + new_ebuild_path = '%s-r%d.ebuild' % ( + stable_candidate.ebuild_path_no_revision, + stable_candidate.current_revision + 1) + else: + if chrome_rev in [constants.CHROME_REV_LOCAL, constants.CHROME_REV_TOT, + constants.CHROME_REV_SPEC]: + portage_suffix = '_alpha' + else: + portage_suffix = '_rc' + + new_ebuild_path = base_path + ('%s-r1.ebuild' % portage_suffix) + + # Mark latest release and sticky branches as stable. + mark_stable = chrome_rev not in [constants.CHROME_REV_TOT, + constants.CHROME_REV_SPEC, + constants.CHROME_REV_LOCAL] + + chrome_variables = dict() + if commit: + chrome_variables[_CHROME_SVN_TAG] = commit + + portage_utilities.EBuild.MarkAsStable( + unstable_ebuild.ebuild_path, new_ebuild_path, + chrome_variables, make_stable=mark_stable) + new_ebuild = ChromeEBuild(new_ebuild_path) + + # Determine whether this is ebuild is redundant. + if IsTheNewEBuildRedundant(new_ebuild, stable_candidate): + Info('Previous ebuild with same version found and ebuild is redundant.') + os.unlink(new_ebuild_path) + return None + + if stable_candidate and chrome_rev in _REV_TYPES_FOR_LINKS: + _AnnotateAndPrint('Chromium revisions', + GetChromeRevisionListLink(stable_candidate, + new_ebuild, + chrome_rev)) + + RunCommand(['git', 'add', new_ebuild_path], cwd=overlay_dir) + if stable_candidate and not stable_candidate.IsSticky(): + RunCommand(['git', 'rm', stable_candidate.ebuild_path], cwd=overlay_dir) + + portage_utilities.EBuild.CommitChange( + _GIT_COMMIT_MESSAGE % {'chrome_rev': chrome_rev, + 'chrome_version': chrome_version}, + overlay_dir) + + return '%s-%s' % (new_ebuild.package, new_ebuild.version) + + +def ParseMaxRevision(revision_list): + """Returns the max revision from a list of url@revision string.""" + revision_re = re.compile('.*@(\d+)') + + def RevisionKey(revision): + return revision_re.match(revision).group(1) + + max_revision = max(revision_list.split(), key=RevisionKey) + return max_revision.rpartition('@')[2] + + +def main(argv): + usage_options = '|'.join(constants.VALID_CHROME_REVISIONS) + usage = '%s OPTIONS [%s]' % (__file__, usage_options) + parser = optparse.OptionParser(usage) + parser.add_option('-b', '--boards', default='x86-generic') + parser.add_option('-c', '--chrome_url', default=BASE_CHROME_SVN_URL) + parser.add_option('-f', '--force_revision', default=None) + parser.add_option('-s', '--srcroot', default=os.path.join(os.environ['HOME'], + 'trunk', 'src'), + help='Path to the src directory') + parser.add_option('-t', '--tracking_branch', default='cros/master', + help='Branch we are tracking changes against') + (options, args) = parser.parse_args() + + if len(args) != 1 or args[0] not in constants.VALID_CHROME_REVISIONS: + parser.error('Commit requires arg set to one of %s.' + % constants.VALID_CHROME_REVISIONS) + + overlay_dir = os.path.abspath(_CHROME_OVERLAY_DIR % + {'srcroot': options.srcroot}) + chrome_rev = args[0] + version_to_uprev = None + commit_to_use = None + sticky_branch = None + + (unstable_ebuild, stable_ebuilds) = FindChromeCandidates(overlay_dir) + + if chrome_rev == constants.CHROME_REV_LOCAL: + if 'CHROME_ROOT' in os.environ: + chrome_root = os.environ['CHROME_ROOT'] + else: + chrome_root = os.path.join(os.environ['HOME'], 'chrome_root') + + version_to_uprev = _GetTipOfTrunkVersionFile(chrome_root) + commit_to_use = 'Unknown' + Info('Using local source, versioning is untrustworthy.') + elif chrome_rev == constants.CHROME_REV_SPEC: + commit_to_use = options.force_revision + if '@' in commit_to_use: commit_to_use = ParseMaxRevision(commit_to_use) + version_to_uprev = _GetSpecificVersionUrl(options.chrome_url, + commit_to_use) + elif chrome_rev == constants.CHROME_REV_TOT: + commit_to_use = _GetTipOfTrunkSvnRevision(options.chrome_url) + version_to_uprev = _GetSpecificVersionUrl(options.chrome_url, + commit_to_use) + elif chrome_rev == constants.CHROME_REV_LATEST: + version_to_uprev = _GetLatestRelease(options.chrome_url) + else: + sticky_ebuild = _GetStickyEBuild(stable_ebuilds) + sticky_version = sticky_ebuild.chrome_version + sticky_branch = sticky_version.rpartition('.')[0] + version_to_uprev = _GetLatestRelease(options.chrome_url, sticky_branch) + + stable_candidate = FindChromeUprevCandidate(stable_ebuilds, chrome_rev, + sticky_branch) + + if stable_candidate: + Info('Stable candidate found %s' % stable_candidate) + else: + Info('No stable candidate found.') + + tracking_branch = 'remotes/m/%s' % os.path.basename(options.tracking_branch) + existing_branch = cros_build_lib.GetCurrentBranch(overlay_dir) + work_branch = cros_mark_as_stable.GitBranch(constants.STABLE_EBUILD_BRANCH, + tracking_branch, overlay_dir) + work_branch.CreateBranch() + + # In the case of uprevving overlays that have patches applied to them, + # include the patched changes in the stabilizing branch. + if existing_branch: + RunCommand(['git', 'rebase', existing_branch], cwd=overlay_dir) + + chrome_version_atom = MarkChromeEBuildAsStable( + stable_candidate, unstable_ebuild, chrome_rev, version_to_uprev, + commit_to_use, overlay_dir) + # Explicit print to communicate to caller. + if chrome_version_atom: + cros_mark_as_stable.CleanStalePackages(options.boards.split(':'), + [chrome_version_atom]) + print 'CHROME_VERSION_ATOM=%s' % chrome_version_atom diff --git a/scripts/cros_mark_chrome_as_stable_unittest.py b/scripts/cros_mark_chrome_as_stable_unittest.py new file mode 100755 index 000000000..89866b78b --- /dev/null +++ b/scripts/cros_mark_chrome_as_stable_unittest.py @@ -0,0 +1,326 @@ +#!/usr/bin/python + +# Copyright (c) 2012 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Unit tests for cros_mark_chrome_as_stable.py.""" + +# run with: +# cros_sdk ../../chromite/buildbot/cros_mark_chrome_as_stable_unittest.py + +import mox +import os +import shutil +import sys +import tempfile +import unittest + +sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)), + '..', '..')) +from chromite.buildbot import constants +from chromite.buildbot import portage_utilities +from chromite.scripts import cros_mark_as_stable +from chromite.scripts import cros_mark_chrome_as_stable + +# pylint: disable=W0212,R0904 +unstable_data = 'KEYWORDS=~x86 ~arm' +stable_data = 'KEYWORDS=x86 arm' +fake_svn_rev = '12345' +new_fake_svn_rev = '23456' + +def _TouchAndWrite(path, data=None): + """Writes data (if it exists) to the file specified by the path.""" + fh = open(path, 'w') + if data: + fh.write(data) + + fh.close() + + +class _StubCommandResult(object): + def __init__(self, msg): + self.output = msg + + +class CrosMarkChromeAsStable(mox.MoxTestBase): + + def setUp(self): + """Setup vars and create mock dir.""" + mox.MoxTestBase.setUp(self) + self.tmp_overlay = tempfile.mkdtemp(prefix='chromiumos-overlay') + self.mock_chrome_dir = os.path.join(self.tmp_overlay, 'chromeos-base', + 'chromeos-chrome') + os.makedirs(self.mock_chrome_dir) + + self.unstable = os.path.join(self.mock_chrome_dir, + 'chromeos-chrome-9999.ebuild') + self.sticky_branch = '8.0.224' + self.sticky_version = '%s.503' % self.sticky_branch + self.sticky = os.path.join(self.mock_chrome_dir, + 'chromeos-chrome-%s.ebuild' % + self.sticky_version) + self.sticky_rc_version = '%s.504' % self.sticky_branch + self.sticky_rc = os.path.join(self.mock_chrome_dir, + 'chromeos-chrome-%s_rc-r1.ebuild' % + self.sticky_rc_version) + self.latest_stable_version = '8.0.300.1' + self.latest_stable = os.path.join(self.mock_chrome_dir, + 'chromeos-chrome-%s_rc-r2.ebuild' % + self.latest_stable_version) + self.tot_stable_version = '9.0.305.0' + self.tot_stable = os.path.join(self.mock_chrome_dir, + 'chromeos-chrome-%s_alpha-r1.ebuild' % + self.tot_stable_version) + + self.sticky_new_rc_version = '%s.520' % self.sticky_branch + self.sticky_new_rc = os.path.join(self.mock_chrome_dir, + 'chromeos-chrome-%s_rc-r1.ebuild' % + self.sticky_new_rc_version) + self.latest_new_version = '9.0.305.1' + self.latest_new = os.path.join(self.mock_chrome_dir, + 'chromeos-chrome-%s_rc-r1.ebuild' % + self.latest_new_version) + self.tot_new_version = '9.0.306.0' + self.tot_new = os.path.join(self.mock_chrome_dir, + 'chromeos-chrome-%s_alpha-r1.ebuild' % + self.tot_new_version) + + _TouchAndWrite(self.unstable, unstable_data) + _TouchAndWrite(self.sticky, stable_data) + _TouchAndWrite(self.sticky_rc, stable_data) + _TouchAndWrite(self.latest_stable, stable_data) + _TouchAndWrite(self.tot_stable, + '\n'.join( + (stable_data, + '%s=%s' % (cros_mark_chrome_as_stable._CHROME_SVN_TAG, + fake_svn_rev)))) + + def tearDown(self): + """Cleans up mock dir.""" + shutil.rmtree(self.tmp_overlay) + + def testFindChromeCandidates(self): + """Test creation of stable ebuilds from mock dir.""" + unstable, stable_ebuilds = cros_mark_chrome_as_stable.FindChromeCandidates( + self.mock_chrome_dir) + + stable_ebuild_paths = map(lambda eb: eb.ebuild_path, stable_ebuilds) + self.assertEqual(unstable.ebuild_path, self.unstable) + self.assertEqual(len(stable_ebuilds), 4) + self.assertTrue(self.sticky in stable_ebuild_paths) + self.assertTrue(self.sticky_rc in stable_ebuild_paths) + self.assertTrue(self.latest_stable in stable_ebuild_paths) + self.assertTrue(self.tot_stable in stable_ebuild_paths) + + def _GetStableEBuilds(self): + """Common helper to create a list of stable ebuilds.""" + return [ + cros_mark_chrome_as_stable.ChromeEBuild(self.sticky), + cros_mark_chrome_as_stable.ChromeEBuild(self.sticky_rc), + cros_mark_chrome_as_stable.ChromeEBuild(self.latest_stable), + cros_mark_chrome_as_stable.ChromeEBuild(self.tot_stable), + ] + + def testTOTFindChromeUprevCandidate(self): + """Tests if we can find tot uprev candidate from our mock dir data.""" + stable_ebuilds = self._GetStableEBuilds() + + candidate = cros_mark_chrome_as_stable.FindChromeUprevCandidate( + stable_ebuilds, constants.CHROME_REV_TOT, + self.sticky_branch) + + self.assertEqual(candidate.ebuild_path, self.tot_stable) + + def testLatestFindChromeUprevCandidate(self): + """Tests if we can find latest uprev candidate from our mock dir data.""" + stable_ebuilds = self._GetStableEBuilds() + + candidate = cros_mark_chrome_as_stable.FindChromeUprevCandidate( + stable_ebuilds, constants.CHROME_REV_LATEST, + self.sticky_branch) + + self.assertEqual(candidate.ebuild_path, self.latest_stable) + + def testStickyFindChromeUprevCandidate(self): + """Tests if we can find sticky uprev candidate from our mock dir data.""" + stable_ebuilds = self._GetStableEBuilds() + + candidate = cros_mark_chrome_as_stable.FindChromeUprevCandidate( + stable_ebuilds, constants.CHROME_REV_STICKY, + self.sticky_branch) + + self.assertEqual(candidate.ebuild_path, self.sticky_rc) + + def testGetTipOfTrunkSvnRevision(self): + """Tests if we can get the latest svn revision from TOT.""" + A_URL = 'dorf://mink/delaane/forkat/sertiunu.ortg./desk' + self.mox.StubOutWithMock(cros_mark_chrome_as_stable, 'RunCommand') + cros_mark_chrome_as_stable.RunCommand( + ['svn', 'info', cros_mark_chrome_as_stable._GetSvnUrl(A_URL)], + redirect_stdout=True).AndReturn( + _StubCommandResult( + 'Some Junk 2134\nRevision: %s\nOtherInfo: test_data' % + fake_svn_rev)) + self.mox.ReplayAll() + revision = cros_mark_chrome_as_stable._GetTipOfTrunkSvnRevision(A_URL) + self.mox.VerifyAll() + self.assertEquals(revision, fake_svn_rev) + + def testGetTipOfTrunkVersion(self): + """Tests if we get the latest version from TOT.""" + ARBITRARY_URL = 'Pratooey' + path = os.path.join(cros_mark_chrome_as_stable._GetSvnUrl(ARBITRARY_URL), + 'src', 'chrome', 'VERSION') + self.mox.StubOutWithMock(cros_mark_chrome_as_stable, 'RunCommand') + cros_mark_chrome_as_stable.RunCommand( + ['svn', 'info', cros_mark_chrome_as_stable._GetSvnUrl(ARBITRARY_URL)], + redirect_stdout=True).AndReturn( + _StubCommandResult( + 'Some Junk 2134\nRevision: %s\nOtherInfo: test_data' % + fake_svn_rev)) + cros_mark_chrome_as_stable.RunCommand( + ['svn', 'cat', '-r', fake_svn_rev, path], redirect_stdout=True, + error_message=mox.IsA(str)).AndReturn( + _StubCommandResult('A=8\nB=0\nC=256\nD=0')) + + self.mox.ReplayAll() + version = cros_mark_chrome_as_stable._GetSpecificVersionUrl(ARBITRARY_URL, + fake_svn_rev) + self.mox.VerifyAll() + self.assertEquals(version, '8.0.256.0') + + def testGetLatestRelease(self): + """Tests if we can find the latest release from our mock url data.""" + ARBITRARY_URL = 'phthp://sores.chromium.org/tqs' + input_data = ['7.0.224.1/', '7.0.224.2/', '8.0.365.5/', 'LATEST.txt'] + test_data = '\n'.join(input_data) + sorted_data = '\n'.join(reversed(input_data)) + self.mox.StubOutWithMock(cros_mark_chrome_as_stable, 'RunCommand') + cros_mark_chrome_as_stable.RunCommand( + ['svn', 'ls', ARBITRARY_URL + '/releases'], + redirect_stdout=True).AndReturn(_StubCommandResult(test_data)) + cros_mark_chrome_as_stable.RunCommand( + ['sort', '--version-sort', '-r'], input=test_data, + redirect_stdout=True).AndReturn(_StubCommandResult(sorted_data)) + # pretend this one is missing to test the skipping logic. + cros_mark_chrome_as_stable.RunCommand( + ['svn', 'ls', ARBITRARY_URL + '/releases/8.0.365.5/DEPS'], + error_ok=True, redirect_stdout=True).AndReturn( + _StubCommandResult('BAH BAH BAH')) + cros_mark_chrome_as_stable.RunCommand( + ['svn', 'ls', ARBITRARY_URL + '/releases/7.0.224.2/DEPS'], + error_ok=True, redirect_stdout=True).AndReturn( + _StubCommandResult('DEPS\n')) + self.mox.ReplayAll() + release = cros_mark_chrome_as_stable._GetLatestRelease(ARBITRARY_URL) + self.mox.VerifyAll() + self.assertEqual('7.0.224.2', release) + + def testGetLatestStickyRelease(self): + """Tests if we can find the latest sticky release from our mock url data.""" + ARBITRARY_URL = 'http://src.chromium.org/svn' + test_data = '\n'.join(['7.0.222.1/', + '8.0.224.2/', + '8.0.365.5/', + 'LATEST.txt']) + self.mox.StubOutWithMock(cros_mark_chrome_as_stable, 'RunCommand') + cros_mark_chrome_as_stable.RunCommand( + ['svn', 'ls', ARBITRARY_URL + '/releases'], + redirect_stdout=True).AndReturn(_StubCommandResult('some_data')) + cros_mark_chrome_as_stable.RunCommand( + ['sort', '--version-sort', '-r'], input='some_data', + redirect_stdout=True).AndReturn(_StubCommandResult(test_data)) + cros_mark_chrome_as_stable.RunCommand( + ['svn', 'ls', ARBITRARY_URL + '/releases/8.0.224.2/DEPS'], + error_ok=True, redirect_stdout=True).AndReturn( + _StubCommandResult('DEPS\n')) + self.mox.ReplayAll() + release = cros_mark_chrome_as_stable._GetLatestRelease(ARBITRARY_URL, + '8.0.224') + self.mox.VerifyAll() + self.assertEqual('8.0.224.2', release) + + def testLatestChromeRevisionListLink(self): + """Tests that we can generate a link to the revision list between the + latest Chromium release and the last one we successfully built.""" + _TouchAndWrite(self.latest_new, stable_data) + expected = cros_mark_chrome_as_stable.GetChromeRevisionLinkFromVersions( + self.latest_stable_version, self.latest_new_version) + made = cros_mark_chrome_as_stable.GetChromeRevisionListLink( + cros_mark_chrome_as_stable.ChromeEBuild(self.latest_stable), + cros_mark_chrome_as_stable.ChromeEBuild(self.latest_new), + constants.CHROME_REV_LATEST) + self.assertEqual(expected, made) + + def testStickyEBuild(self): + """Tests if we can find the sticky ebuild from our mock directories.""" + stable_ebuilds = self._GetStableEBuilds() + sticky_ebuild = cros_mark_chrome_as_stable._GetStickyEBuild( + stable_ebuilds) + self.assertEqual(sticky_ebuild.chrome_version, self.sticky_version) + + def testChromeEBuildInit(self): + """Tests if the chrome_version is set correctly in a ChromeEBuild.""" + ebuild = cros_mark_chrome_as_stable.ChromeEBuild(self.sticky) + self.assertEqual(ebuild.chrome_version, self.sticky_version) + + def _CommonMarkAsStableTest(self, chrome_rev, new_version, old_ebuild_path, + new_ebuild_path, commit_string_indicator): + """Common function used for test functions for MarkChromeEBuildAsStable. + + This function stubs out others calls, and runs MarkChromeEBuildAsStable + with the specified args. + + Args: + chrome_rev: standard chrome_rev argument + new_version: version we are revving up to + old_ebuild_path: path to the stable ebuild + new_ebuild_path: path to the to be created path + commit_string_indicator: a string that the commit message must contain + """ + self.mox.StubOutWithMock(cros_mark_chrome_as_stable, 'RunCommand') + self.mox.StubOutWithMock(portage_utilities.EBuild, 'CommitChange') + stable_candidate = cros_mark_chrome_as_stable.ChromeEBuild(old_ebuild_path) + unstable_ebuild = cros_mark_chrome_as_stable.ChromeEBuild(self.unstable) + chrome_version = new_version + commit = None + overlay_dir = self.mock_chrome_dir + + cros_mark_chrome_as_stable.RunCommand(['git', 'add', new_ebuild_path], + cwd=overlay_dir) + cros_mark_chrome_as_stable.RunCommand(['git', 'rm', old_ebuild_path], + cwd=overlay_dir) + portage_utilities.EBuild.CommitChange( + mox.StrContains(commit_string_indicator), overlay_dir) + + self.mox.ReplayAll() + cros_mark_chrome_as_stable.MarkChromeEBuildAsStable( + stable_candidate, unstable_ebuild, chrome_rev, chrome_version, commit, + overlay_dir) + self.mox.VerifyAll() + + def testStickyMarkAsStable(self): + """Tests to see if we can mark chrome as stable for a new sticky release.""" + self._CommonMarkAsStableTest( + constants.CHROME_REV_STICKY, + self.sticky_new_rc_version, self.sticky_rc, + self.sticky_new_rc, 'stable_release') + + def testLatestMarkAsStable(self): + """Tests to see if we can mark chrome for a latest release.""" + self._CommonMarkAsStableTest( + constants.CHROME_REV_LATEST, + self.latest_new_version, self.latest_stable, + self.latest_new, 'latest_release') + + def testTotMarkAsStable(self): + """Tests to see if we can mark chrome for tot.""" + self._CommonMarkAsStableTest( + constants.CHROME_REV_TOT, + self.tot_new_version, self.tot_stable, + self.tot_new, 'tot') + + +if __name__ == '__main__': + unittest.main() |