xbuddy: Fix linter errors.
Also replace mox
BUG=chromium:403086
TEST=unittests, cros flash
Change-Id: I6052be6b45f1c480732925bf490ff5ecf38e39ca
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/dev-util/+/1822077
Tested-by: Achuith Bhandarkar <achuith@chromium.org>
Reviewed-by: Amin Hassani <ahassani@chromium.org>
Reviewed-by: Achuith Bhandarkar <achuith@chromium.org>
Commit-Queue: Achuith Bhandarkar <achuith@chromium.org>
diff --git a/xbuddy.py b/xbuddy.py
index d575d9a..7747ef7 100644
--- a/xbuddy.py
+++ b/xbuddy.py
@@ -7,10 +7,8 @@
from __future__ import print_function
-import cherrypy
-import ConfigParser
import datetime
-import distutils.version
+import distutils.version # pylint: disable=import-error,no-name-in-module
import operator
import os
import re
@@ -18,6 +16,9 @@
import time
import threading
+import cherrypy # pylint: disable=import-error
+from six.moves import configparser
+
import artifact_info
import build_artifact
import build_util
@@ -60,10 +61,10 @@
FACTORY_SHIM = 'factory_shim'
# Local build constants
-ANY = "ANY"
-LATEST = "latest"
-LOCAL = "local"
-REMOTE = "remote"
+ANY = 'ANY'
+LATEST = 'latest'
+LOCAL = 'local'
+REMOTE = 'remote'
# TODO(sosa): Fix a lot of assumptions about these aliases. There is too much
# implicit logic here that's unnecessary. What should be done:
@@ -133,9 +134,9 @@
GS_ALIAS_TO_FILENAME = dict(zip(GS_ALIASES, GS_FILE_NAMES))
GS_ALIAS_TO_ARTIFACT = dict(zip(GS_ALIASES, ARTIFACTS))
-LATEST_OFFICIAL = "latest-official"
+LATEST_OFFICIAL = 'latest-official'
-RELEASE = "-release"
+RELEASE = '-release'
class XBuddyException(Exception):
@@ -143,8 +144,6 @@
pass
-# no __init__ method
-#pylint: disable=W0232
class Timestamp(object):
"""Class to translate build path strings and timestamp filenames."""
@@ -163,12 +162,11 @@
def UpdateTimestamp(timestamp_dir, build_id):
"""Update timestamp file of build with build_id."""
common_util.MkDirP(timestamp_dir)
- _Log("Updating timestamp for %s", build_id)
+ _Log('Updating timestamp for %s', build_id)
time_file = os.path.join(timestamp_dir,
Timestamp.BuildToTimestamp(build_id))
- with file(time_file, 'a'):
+ with open(time_file, 'a'):
os.utime(time_file, None)
-#pylint: enable=W0232
class XBuddy(build_util.BuildObject):
@@ -245,7 +243,7 @@
Raises:
XBuddyException if the config file is missing.
"""
- xbuddy_config = ConfigParser.ConfigParser()
+ xbuddy_config = configparser.ConfigParser()
config_file = os.path.join(self.devserver_dir, CONFIG_FILE)
if os.path.exists(config_file):
xbuddy_config.read(config_file)
@@ -263,7 +261,7 @@
_Log('Using shadow config file stored at %s', shadow_config_file)
if os.path.exists(shadow_config_file):
- shadow_xbuddy_config = ConfigParser.ConfigParser()
+ shadow_xbuddy_config = configparser.ConfigParser()
shadow_xbuddy_config.read(shadow_config_file)
# Merge shadow config in.
@@ -282,14 +280,14 @@
"""Checks if xBuddy is managing local builds using the current config."""
try:
return self.ParseBoolean(self.config.get(GENERAL, 'manage_builds'))
- except ConfigParser.Error:
+ except configparser.Error:
return False
def _Capacity(self):
"""Gets the xbuddy capacity from the current config."""
try:
return int(self.config.get(GENERAL, 'capacity'))
- except ConfigParser.Error:
+ except configparser.Error:
return 5
def LookupAlias(self, alias, board=None, version=None):
@@ -312,12 +310,12 @@
"""
try:
suffix = self.config.get(LOCATION_SUFFIXES, alias)
- except ConfigParser.Error:
+ except configparser.Error:
suffix = RELEASE
try:
val = self.config.get(PATH_REWRITES, alias)
- except ConfigParser.Error:
+ except configparser.Error:
# No alias lookup found. Return original path.
val = None
@@ -326,12 +324,12 @@
else:
# The found value is not an empty string.
# Fill in the board and version.
- val = val.replace("BOARD", "%(board)s")
- val = val.replace("VERSION", "%(version)s")
+ val = val.replace('BOARD', '%(board)s')
+ val = val.replace('VERSION', '%(version)s')
val = val % {'board': board or self._board,
'version': version or self._version or LATEST}
- _Log("Path is %s, location suffix is %s", val, suffix)
+ _Log('Path is %s, location suffix is %s', val, suffix)
return val, suffix
@staticmethod
@@ -351,7 +349,7 @@
def _LookupOfficial(self, board, suffix, image_dir=None):
"""Check LATEST-master for the version number of interest."""
- _Log("Checking gs for latest %s-%s image", board, suffix)
+ _Log('Checking gs for latest %s-%s image', board, suffix)
image_dir = XBuddy._ResolveImageDir(image_dir)
latest_addr = (devserver_constants.GS_LATEST_MASTER %
{'image_dir': image_dir,
@@ -398,9 +396,9 @@
list_result = self._LS(path, list_subdirectory=list_subdirectory)
dir_names = [os.path.basename(p.rstrip('/')) for p in list_result]
try:
- filter_re = re.compile(devserver_constants.VERSION_RE if with_release
- else devserver_constants.VERSION)
- versions = filter(filter_re.match, dir_names)
+ versions_re = re.compile(devserver_constants.VERSION_RE if with_release
+ else devserver_constants.VERSION)
+ versions = [d for d in dir_names if versions_re.match(d)]
latest_version = max(versions, key=distutils.version.LooseVersion)
except ValueError:
raise gs.GSContextException(
@@ -523,8 +521,8 @@
if re.match(devserver_constants.VERSION_RE, version):
return self._RemoteBuildId(board, suffix, version), None
elif re.match(devserver_constants.VERSION, version):
- raise XBuddyException('\'%s\' is not valid. Should provide the fully '
- 'qualified version with a version prefix \'RX-\' '
+ raise XBuddyException('"%s" is not valid. Should provide the fully '
+ 'qualified version with a version prefix "RX-" '
'due to crbug.com/585914' % version)
elif version == LATEST_OFFICIAL:
# latest-official --> LATEST build in board-release
@@ -551,7 +549,7 @@
@staticmethod
def _Symlink(link, target):
"""Symlinks link to target, and removes whatever link was there before."""
- _Log("Linking to %s from %s", link, target)
+ _Log('Linking to %s from %s', link, target)
if os.path.lexists(link):
os.unlink(link)
os.symlink(target, link)
@@ -609,7 +607,7 @@
Raises:
XBuddyException: if the path can't be resolved into valid components
"""
- path_list = filter(None, path.split('/'))
+ path_list = [p for p in path.split('/') if p]
# Do the stuff that is well known first. We know that if paths have a
# image_type, it must be one of the GS/LOCAL aliases and it must be at the
@@ -647,8 +645,8 @@
version = path_list.pop(0)
if path_list:
- raise XBuddyException("Path isn't valid. Could not figure out how to "
- "parse remaining components: %s." % path_list)
+ raise XBuddyException('Path is not valid. Could not figure out how to '
+ 'parse remaining components: %s.' % path_list)
_Log("Get artifact '%s' with board %s and version %s'. Locally? %s",
image_type, board, version, is_local)
@@ -703,7 +701,7 @@
build_id = Timestamp.TimestampToBuild(f)
stale_time = datetime.timedelta(seconds=(time.time() - last_accessed))
build_dict[build_id] = stale_time
- return_tup = sorted(build_dict.iteritems(), key=operator.itemgetter(1))
+ return_tup = sorted(build_dict.items(), key=operator.itemgetter(1))
return return_tup
def _Download(self, gs_url, artifacts, build_id):
@@ -716,7 +714,7 @@
with XBuddy._staging_thread_count_lock:
XBuddy._staging_thread_count += 1
try:
- _Log("Downloading %s from %s", artifacts, gs_url)
+ _Log('Downloading %s from %s', artifacts, gs_url)
dl = downloader.GoogleStorageDownloader(self.static_dir, gs_url, build_id)
factory = build_artifact.ChromeOSArtifactFactory(
dl.GetBuildDir(), artifacts, [], dl.GetBuild())
diff --git a/xbuddy_unittest.py b/xbuddy_unittest.py
index bcee666..bd39abb 100755
--- a/xbuddy_unittest.py
+++ b/xbuddy_unittest.py
@@ -8,34 +8,33 @@
from __future__ import print_function
-import ConfigParser
import os
import shutil
import tempfile
import time
import unittest
-import mox
+import mock
+from six.moves import configparser
import xbuddy
# Make sure that chromite is available to import.
-import setup_chromite # pylint: disable=unused-import
+import setup_chromite # pylint: disable=unused-import
try:
from chromite.lib import gs
except ImportError as e:
gs = None
-#pylint: disable=W0212
-#pylint: disable=no-value-for-parameter
+# pylint: disable=protected-access
+# pylint: disable=no-value-for-parameter
GS_ALTERNATE_DIR = 'gs://chromeos-alternate-archive/'
-class xBuddyTest(mox.MoxTestBase):
+class xBuddyTest(unittest.TestCase):
"""Regression tests for xbuddy."""
def setUp(self):
- mox.MoxTestBase.setUp(self)
self.static_image_dir = tempfile.mkdtemp('xbuddy_unittest_static')
@@ -61,7 +60,6 @@
def testGetLatestVersionFromGsDir(self):
"""Test that we can get the most recent version from gsutil calls."""
- self.mox.StubOutWithMock(self.mock_xb, '_LS')
mock_data1 = """gs://chromeos-releases/stable-channel/parrot/3701.96.0/
gs://chromeos-releases/stable-channel/parrot/3701.98.0/
gs://chromeos-releases/stable-channel/parrot/3912.100.0/
@@ -73,134 +71,116 @@
gs://chromeos-image-archive/parrot-release/R27-3912.101.0
gs://chromeos-image-archive/parrot-release/R28-3912.101.0"""
- self.mock_xb._LS(mox.IgnoreArg(), list_subdirectory=False).AndReturn(
- mock_data1.splitlines())
- self.mock_xb._LS(mox.IgnoreArg(), list_subdirectory=True).AndReturn(
- mock_data2.splitlines())
-
- self.mox.ReplayAll()
- url = ''
- self.assertEqual(
- self.mock_xb._GetLatestVersionFromGsDir(url, with_release=False),
- '3912.101.0')
- self.assertEqual(
- self.mock_xb._GetLatestVersionFromGsDir(url, list_subdirectory=True,
- with_release=True),
- 'R28-3912.101.0')
- self.mox.VerifyAll()
+ with mock.patch.object(
+ self.mock_xb, '_LS',
+ side_effect=[mock_data1.splitlines(),
+ mock_data2.splitlines()]) as ls_mock:
+ self.assertEqual(
+ self.mock_xb._GetLatestVersionFromGsDir('url1', with_release=False),
+ '3912.101.0')
+ ls_mock.assert_called_with('url1', list_subdirectory=False)
+ self.assertEqual(
+ self.mock_xb._GetLatestVersionFromGsDir(
+ 'url2', list_subdirectory=True, with_release=True),
+ 'R28-3912.101.0')
+ ls_mock.assert_called_with('url2', list_subdirectory=True)
def testLookupOfficial(self):
"""Basic test of _LookupOfficial. Checks that a given suffix is handled."""
- self.mox.StubOutWithMock(gs.GSContext, 'Cat')
- gs.GSContext.Cat(mox.IgnoreArg()).AndReturn('v')
- expected = 'b-s/v'
- self.mox.ReplayAll()
- self.assertEqual(self.mock_xb._LookupOfficial('b', suffix='-s'), expected)
- self.mox.VerifyAll()
+ with mock.patch.object(gs.GSContext, 'Cat', return_value='v') as cat_mock:
+ self.assertEqual(self.mock_xb._LookupOfficial('b', suffix='-s'), 'b-s/v')
+ cat_mock.assert_called_with(
+ 'gs://chromeos-image-archive/b-s/LATEST-master')
- def testLookupChannel(self):
+ @mock.patch('xbuddy.XBuddy._GetLatestVersionFromGsDir',
+ side_effect=['4100.68.0', 'R28-4100.68.0'])
+ def testLookupChannel(self, version_mock):
"""Basic test of _LookupChannel. Checks that a given suffix is handled."""
- self.mox.StubOutWithMock(self.mock_xb, '_GetLatestVersionFromGsDir')
- mock_data1 = '4100.68.0'
- self.mock_xb._GetLatestVersionFromGsDir(
- mox.IgnoreArg(), with_release=False).AndReturn(mock_data1)
- mock_data2 = 'R28-4100.68.0'
- self.mock_xb._GetLatestVersionFromGsDir(
- mox.IgnoreArg(), list_subdirectory=True).AndReturn(mock_data2)
- self.mox.ReplayAll()
- expected = 'b-release/R28-4100.68.0'
self.assertEqual(self.mock_xb._LookupChannel('b', '-release'),
- expected)
- self.mox.VerifyAll()
+ 'b-release/R28-4100.68.0')
+ version_mock.assert_called_with(
+ 'gs://chromeos-image-archive/b-release/R*4100.68.0',
+ list_subdirectory=True)
def testLookupAliasPathRewrite(self):
"""Tests LookupAlias of path rewrite, including keyword substitution."""
- alias = 'foobar'
path = 'remote/BOARD/VERSION/test'
- self.mox.StubOutWithMock(self.mock_xb.config, 'get')
- self.mock_xb.config.get('LOCATION_SUFFIXES', alias).AndRaise(
- ConfigParser.Error())
- self.mock_xb.config.get('PATH_REWRITES', alias).AndReturn(path)
- self.mox.ReplayAll()
- self.assertEqual(('remote/parrot/1.2.3/test', '-release'),
- self.mock_xb.LookupAlias(alias, board='parrot',
- version='1.2.3'))
+ with mock.patch.object(
+ self.mock_xb.config, 'get',
+ side_effect=[configparser.Error, path]) as get_mock:
+ self.assertEqual(('remote/parrot/1.2.3/test', '-release'),
+ self.mock_xb.LookupAlias('foobar', board='parrot',
+ version='1.2.3'))
+ get_mock.assert_called_with('PATH_REWRITES', 'foobar')
def testLookupAliasSuffix(self):
"""Tests LookupAlias of location suffix."""
- alias = 'foobar'
- suffix = '-random'
- self.mox.StubOutWithMock(self.mock_xb.config, 'get')
- self.mock_xb.config.get('LOCATION_SUFFIXES', alias).AndReturn(suffix)
- self.mock_xb.config.get('PATH_REWRITES', alias).AndRaise(
- ConfigParser.Error())
- self.mox.ReplayAll()
- self.assertEqual((alias, suffix),
- self.mock_xb.LookupAlias(alias, board='parrot',
- version='1.2.3'))
+ with mock.patch.object(
+ self.mock_xb.config, 'get',
+ side_effect=['-random', configparser.Error]) as get_mock:
+ self.assertEqual(('foobar', '-random'),
+ self.mock_xb.LookupAlias('foobar', board='parrot',
+ version='1.2.3'))
+ get_mock.assert_called_with('PATH_REWRITES', 'foobar')
def testLookupAliasPathRewriteAndSuffix(self):
"""Tests LookupAlias with both path rewrite and suffix."""
- alias = 'foobar'
path = 'remote/BOARD/VERSION/test'
- suffix = '-random'
- self.mox.StubOutWithMock(self.mock_xb.config, 'get')
- self.mock_xb.config.get('LOCATION_SUFFIXES', alias).AndReturn(suffix)
- self.mock_xb.config.get('PATH_REWRITES', alias).AndReturn(path)
- self.mox.ReplayAll()
- self.assertEqual(('remote/parrot/1.2.3/test', suffix),
- self.mock_xb.LookupAlias(alias, board='parrot',
- version='1.2.3'))
+ with mock.patch.object(
+ self.mock_xb.config, 'get',
+ side_effect=['-random', path]) as get_mock:
+ self.assertEqual(('remote/parrot/1.2.3/test', '-random'),
+ self.mock_xb.LookupAlias('foobar', board='parrot',
+ version='1.2.3'))
+ get_mock.assert_called_with('PATH_REWRITES', 'foobar')
- def testResolveVersionToBuildIdAndChannel_Official(self):
+ @mock.patch('xbuddy.XBuddy._LookupOfficial')
+ def testResolveVersionToBuildIdAndChannel_Official(self, lookup_mock):
"""Check _ResolveVersionToBuildIdAndChannel support for official build."""
- board = 'b'
+ board = 'chell'
suffix = '-s'
- # aliases that should be redirected to LookupOfficial
-
- self.mox.StubOutWithMock(self.mock_xb, '_LookupOfficial')
- self.mock_xb._LookupOfficial(board, suffix, image_dir=None)
- self.mock_xb._LookupOfficial(board, suffix,
- image_dir=GS_ALTERNATE_DIR)
- self.mock_xb._LookupOfficial(board, 'paladin', image_dir=None)
- self.mock_xb._LookupOfficial(board, 'paladin',
- image_dir=GS_ALTERNATE_DIR)
-
- self.mox.ReplayAll()
version = 'latest-official'
self.mock_xb._ResolveVersionToBuildIdAndChannel(board, suffix, version)
+ lookup_mock.assert_called_with('chell', '-s', image_dir=None)
+
self.mock_xb._ResolveVersionToBuildIdAndChannel(board, suffix, version,
image_dir=GS_ALTERNATE_DIR)
+ lookup_mock.assert_called_with('chell', '-s',
+ image_dir='gs://chromeos-alternate-archive/')
+
version = 'latest-official-paladin'
self.mock_xb._ResolveVersionToBuildIdAndChannel(board, suffix, version)
+ lookup_mock.assert_called_with('chell', 'paladin', image_dir=None)
+
self.mock_xb._ResolveVersionToBuildIdAndChannel(board, suffix, version,
image_dir=GS_ALTERNATE_DIR)
- self.mox.VerifyAll()
+ lookup_mock.assert_called_with('chell', 'paladin',
+ image_dir='gs://chromeos-alternate-archive/')
- def testResolveVersionToBuildIdAndChannel_Channel(self):
+ @mock.patch('xbuddy.XBuddy._LookupChannel')
+ def testResolveVersionToBuildIdAndChannel_Channel(self, lookup_mock):
"""Check _ResolveVersionToBuildIdAndChannel support for channels."""
- board = 'b'
+ board = 'chell'
suffix = '-s'
- # aliases that should be redirected to LookupChannel
- self.mox.StubOutWithMock(self.mock_xb, '_LookupChannel')
- self.mock_xb._LookupChannel(board, suffix, image_dir=None)
- self.mock_xb._LookupChannel(board, suffix, image_dir=GS_ALTERNATE_DIR)
- self.mock_xb._LookupChannel(board, suffix, channel='dev', image_dir=None)
- self.mock_xb._LookupChannel(board, suffix, channel='dev',
- image_dir=GS_ALTERNATE_DIR)
-
- self.mox.ReplayAll()
version = 'latest'
self.mock_xb._ResolveVersionToBuildIdAndChannel(board, suffix, version)
+ lookup_mock.assert_called_with('chell', '-s', image_dir=None)
+
self.mock_xb._ResolveVersionToBuildIdAndChannel(board, suffix, version,
image_dir=GS_ALTERNATE_DIR)
+ lookup_mock.assert_called_with('chell', '-s',
+ image_dir='gs://chromeos-alternate-archive/')
+
version = 'latest-dev'
self.mock_xb._ResolveVersionToBuildIdAndChannel(board, suffix, version)
+ lookup_mock.assert_called_with('chell', '-s', channel='dev', image_dir=None)
+
self.mock_xb._ResolveVersionToBuildIdAndChannel(board, suffix, version,
image_dir=GS_ALTERNATE_DIR)
- self.mox.VerifyAll()
+ lookup_mock.assert_called_with('chell', '-s', channel='dev',
+ image_dir='gs://chromeos-alternate-archive/')
# TODO(dgarrett): Re-enable when crbug.com/585914 is fixed.
# def testResolveVersionToBuildId_BaseVersion(self):
@@ -329,56 +309,57 @@
result = self.mock_xb._ListBuildTimes()
self.assertEqual(len(result), 4)
- ############### Public Methods
def testXBuddyCaching(self):
"""Caching & replacement of timestamp files."""
- path_a = ('remote', 'a', 'R0', 'test')
- path_b = ('remote', 'b', 'R0', 'test')
- self.mox.StubOutWithMock(gs.GSContext, 'LS')
- self.mox.StubOutWithMock(self.mock_xb, '_Download')
- for _ in range(8):
- self.mock_xb._Download(mox.IsA(str), mox.In(mox.IsA(str)), mox.IsA(str))
- # All non-release urls are invalid so we can meet expectations.
- gs.GSContext.LS(
- mox.Not(mox.StrContains('-release'))).MultipleTimes().AndRaise(
- gs.GSContextException('bad url'))
- gs.GSContext.LS(mox.StrContains('-release')).MultipleTimes()
+ def _ReleaseOnly(name):
+ # All non-release URLs are invalid so we can meet expectations.
+ if name.find('-release') == -1:
+ raise gs.GSContextException('bad URL')
+ return name
- self.mox.ReplayAll()
+ with mock.patch.object(
+ gs.GSContext, 'LS', side_effect=_ReleaseOnly) as ls_mock:
+ with mock.patch.object(
+ self.mock_xb, '_Download') as download_mock:
+ version = '%s-release/R0'
- # requires default capacity
- self.assertEqual(self.mock_xb.Capacity(), '5')
+ def _Download(image):
+ gs_image = 'gs://chromeos-image-archive/' + version
+ self.mock_xb.Get(('remote', image, 'R0', 'test'))
+ ls_mock.assert_called_with(gs_image % image)
+ download_mock.assert_called_with(gs_image % image, ['test_image'],
+ version % image)
+ time.sleep(0.05)
- # Get 6 different images: a,b,c,d,e,f
- images = ['a', 'b', 'c', 'd', 'e', 'f']
- for c in images:
- self.mock_xb.Get(('remote', c, 'R0', 'test'))
- time.sleep(0.05)
+ # Requires default capacity.
+ self.assertEqual(self.mock_xb.Capacity(), '5')
- # check that b,c,d,e,f are still stored
- result = self.mock_xb._ListBuildTimes()
- self.assertEqual(len(result), 5)
+ # Get 6 different images: a,b,c,d,e,f.
+ images = ['a', 'b', 'c', 'd', 'e', 'f']
+ for image in images:
+ _Download(image)
- # Flip the list to get reverse chronological order
- images.reverse()
- for i in range(5):
- self.assertEqual(result[i][0], '%s-release/R0' % images[i])
+ # Check that b,c,d,e,f are still stored.
+ result = self.mock_xb._ListBuildTimes()
+ self.assertEqual(len(result), 5)
- # Get b,a
- self.mock_xb.Get(path_b)
- time.sleep(0.05)
- self.mock_xb.Get(path_a)
- time.sleep(0.05)
+ # Flip the list to get reverse chronological order
+ images.reverse()
+ for i in range(5):
+ self.assertEqual(result[i][0], version % images[i])
- # check that d,e,f,b,a are still stored
- result = self.mock_xb._ListBuildTimes()
- self.assertEqual(len(result), 5)
- images_expected = ['a', 'b', 'f', 'e', 'd']
- for i in range(5):
- self.assertEqual(result[i][0], '%s-release/R0' % images_expected[i])
+ # Get b,a.
+ images = ['b', 'a']
+ for image in images:
+ _Download(image)
- self.mox.VerifyAll()
+ # Check that d,e,f,b,a are still stored.
+ result = self.mock_xb._ListBuildTimes()
+ self.assertEqual(len(result), 5)
+ images_expected = ['a', 'b', 'f', 'e', 'd']
+ for i in range(5):
+ self.assertEqual(result[i][0], '%s-release/R0' % images_expected[i])
if __name__ == '__main__':