blob: a5fd8285b895ee2585ecd0f0187f5a7e415dcd57 [file] [log] [blame]
#!/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.
"""Unittests for the gs.py module."""
import os
import sys
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(
os.path.abspath(__file__)))))
import mox
import unittest
from chromite.lib import cros_build_lib
from chromite.lib import cros_test_lib
from chromite.lib import gs
from chromite.lib import osutils
#pylint: disable=E1101,W0212
class GSContextTest(cros_test_lib.TempDirMixin, mox.MoxTestBase):
"""Tests for GSContext()"""
_GSResponsePreconditionFailed = """
[Setting Content-Type=text/x-python]
GSResponseError:: status=412, code=PreconditionFailed,
reason=Precondition Failed."""
def setUp(self):
cros_test_lib.TempDirMixin.setUp(self)
for attr in ('gsutil_bin', 'boto_file', 'acl_file'):
path = os.path.join(self.tempdir, attr)
osutils.WriteFile(path, '')
setattr(self, attr, path)
self.defaults = {'DEFAULT_BOTO_FILE': self.boto_file,
'DEFAULT_GSUTIL_BIN': self.gsutil_bin}
self.bad_path = os.path.join(self.tempdir, 'nonexistent')
self._boto_config_env = os.environ.pop("BOTO_CONFIG", None)
mox.MoxTestBase.setUp(self)
# No command should be ran w/out us explicitly asseting it.
self.mox.StubOutWithMock(cros_build_lib, 'RunCommand')
def tearDown(self):
mox.MoxTestBase.tearDown(self)
cros_test_lib.TempDirMixin.tearDown(self)
if self._boto_config_env is not None:
os.environ['BOTO_CONFIG'] = self._boto_config_env
def MkContext(self, *args, **kwds):
# Note we derive on the fly here so we can ensure that
# there always is a valid fallback for boto_file; we
# do so in a way that allows us to also check that
# behaviour when the default is missing.
class Context(gs.GSContext):
# This bit of voodoo is basically a way to scrape
# default overrides out of keywords into the class
# scope; used for testing the defaults.
# First inject in our defaults, then the overrides.
locals().update(self.defaults)
locals().update((key, kwds.pop(key)) for key in list(kwds)
if key.startswith("DEFAULT_"))
suppress_DoCommand = kwds.pop('suppress_DoCommand', True)
context = Context(*args, **kwds)
if suppress_DoCommand:
self.mox.StubOutWithMock(context, '_DoCommand')
return context
def testInitGsutilBin(self):
"""Test we use the given gsutil binary, erroring where appropriate."""
self.assertEqual(self.MkContext().gsutil_bin, self.gsutil_bin)
self.assertRaises(gs.GSContextException, self.MkContext,
DEFAULT_BOTO_FILE=self.bad_path)
self.assertEqual(
self.MkContext(gsutil_bin=self.boto_file).gsutil_bin,
self.boto_file)
self.assertRaises(gs.GSContextException, self.MkContext,
gsutil_bin=self.bad_path)
def testInitBotoFile(self):
"""Test we use the given boto_file, erroring where appropriate."""
self.assertEqual(self.MkContext().boto_file, self.boto_file)
self.assertRaises(gs.GSContextException, self.MkContext,
DEFAULT_BOTO_FILE=self.bad_path)
# Check env usage next; no need to cleanup, teardown handles it,
# and we want the env var to persist for the next part of this test.
os.environ['BOTO_CONFIG'] = self.bad_path
self.assertRaises(gs.GSContextException, self.MkContext)
os.environ['BOTO_CONFIG'] = self.gsutil_bin
self.assertEqual(self.MkContext().boto_file, self.gsutil_bin)
self.assertEqual(self.MkContext(boto_file=self.acl_file).boto_file,
self.acl_file)
self.assertRaises(gs.GSContextException, self.MkContext,
boto_file=self.bad_path)
def testInitAclFile(self):
self.assertEqual(self.MkContext().acl_file, None)
self.assertEqual(self.MkContext(acl_file=self.acl_file).acl_file,
self.acl_file)
self.assertRaises(gs.GSContextException, self.MkContext,
acl_file=self.bad_path)
def assertGSCommand(self, context, cmd, **kwds):
return context._DoCommand(cmd, **kwds)
def testCopy(self, functor=None, filename=None):
"""Test CopyTo functionality."""
local_path = lambda x: '/tmp/file%s' % x
if filename is not None:
given_remote = lambda x: 'gs://test/path/%s' % x
expected_remote = lambda x: 'gs://test/path/%s/%s' % (x, filename)
else:
given_remote = expected_remote = lambda x: 'gs://test/path/file%s' % x
ctx1 = self.MkContext()
self.assertGSCommand(ctx1, ['cp', '--', local_path(1), expected_remote(1)])
ctx2 = self.MkContext(acl_file=self.acl_file)
self.assertGSCommand(ctx2, ['cp', '-a', self.acl_file, '--',
local_path(2), expected_remote(2)])
ctx3 = self.MkContext()
self.assertGSCommand(ctx3, ['cp', '-a', self.gsutil_bin, '--',
local_path('brass'),
expected_remote('brass')])
ctx4 = self.MkContext(acl_file=self.gsutil_bin)
self.assertGSCommand(ctx4, ['cp', '-a', self.acl_file, '--',
local_path('monkey'),
expected_remote('monkey')])
ctx5 = self.MkContext()
self.assertGSCommand(ctx5, ['cp', '--', local_path(5),
expected_remote(5)],
headers=['x-goog-if-sequence-number-match:0'])
ctx6 = self.MkContext()
self.assertGSCommand(ctx6, ['cp', '--', self.gsutil_bin,
expected_remote(6)],
headers=['x-goog-if-sequence-number-match:6'])
ctx7 = self.MkContext()
ret = self.assertGSCommand(ctx7, ['cp', '--', local_path(7),
expected_remote(7)])
ret.AndRaise(cros_build_lib.RunCommandError(
'blah', cros_build_lib.CommandResult(returncode=1, output='')))
ctx8 = self.MkContext()
ret = self.assertGSCommand(ctx8, ['cp', '--', local_path(8),
expected_remote(8)])
ret.AndRaise(cros_build_lib.RunCommandError(
'blah', cros_build_lib.CommandResult(returncode=1,
output='code=PreconditionFailed')))
self.mox.ReplayAll()
if functor is None:
functor = lambda ctx:ctx.Copy
functor(ctx1)(local_path(1), given_remote(1))
functor(ctx2)(local_path(2), given_remote(2))
functor(ctx3)(local_path('brass'), given_remote('brass'),
acl=self.gsutil_bin)
functor(ctx4)(local_path('monkey'), given_remote('monkey'),
acl=self.acl_file)
functor(ctx5)(local_path(5), given_remote(5), version=0)
functor(ctx6)(self.gsutil_bin, given_remote(6), version=6)
self.assertRaises(cros_build_lib.RunCommandError,
functor(ctx7), local_path(7), given_remote(7))
self.assertRaises(gs.GSContextException,
functor(ctx8), local_path(8), given_remote(8))
self.mox.VerifyAll()
def testCopyInto(self):
def functor(ctx):
return lambda *args, **kwds:ctx.CopyInto(*args, filename='blah', **kwds)
self.testCopy(functor=functor, filename='blah')
def testDoCommand(self):
"""Verify the internal DoCommand function works correctly."""
self.mox.StubOutWithMock(cros_build_lib, 'RetryCommand')
ctx1 = self.MkContext(suppress_DoCommand=False)
cros_build_lib.RetryCommand(
cros_build_lib.RunCommandCaptureOutput, ctx1._retries,
[self.gsutil_bin, 'cp', '--', '/blah', 'gs://foon'],
sleep=ctx1._sleep_time, extra_env={'BOTO_CONFIG':self.boto_file})
ctx2 = self.MkContext(suppress_DoCommand=False, retries=2, sleep=1)
cros_build_lib.RetryCommand(
cros_build_lib.RunCommandCaptureOutput, ctx2._retries,
[self.gsutil_bin, '-h', 'x-goog-if-sequence-number-match:1',
'cp', '--', '/blah', 'gs://foon'],
sleep=ctx2._sleep_time, extra_env={'BOTO_CONFIG':self.boto_file})
self.mox.ReplayAll()
ctx1.Copy('/blah', 'gs://foon')
ctx2.Copy('/blah', 'gs://foon', version=1)
self.mox.VerifyAll()
def testSetAcl(self):
ctx1 = self.MkContext()
self.assertGSCommand(ctx1, ['setacl', 'monkeys', 'gs://abc/1'])
ctx2 = self.MkContext(acl_file=self.acl_file)
self.assertGSCommand(ctx2, ['setacl', self.acl_file, 'gs://abc/2'])
# Ensure it blows up if there isn't an acl specified in any fashion.
ctx3 = self.MkContext()
self.mox.ReplayAll()
ctx1.SetACL('gs://abc/1', 'monkeys')
ctx2.SetACL('gs://abc/2')
self.assertRaises(gs.GSContextException, ctx3.SetACL, 'gs://abc/3')
self.mox.VerifyAll()
if __name__ == '__main__':
cros_build_lib.SetupBasicLogging()
unittest.main()