blob: 3439347fe2cd8937d2dc3550ad7e4fe36fc24140 [file] [log] [blame]
# Copyright 2015 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 tests the brillo sdk command."""
from __future__ import print_function
import mock
import os
from chromite.cbuildbot import constants
from chromite.cbuildbot import repository
from chromite.cli import command_unittest
from chromite.cli.brillo import brillo_sdk
from chromite.lib import bootstrap_lib
from chromite.lib import commandline
from chromite.lib import cros_build_lib
from chromite.lib import cros_build_lib_unittest
from chromite.lib import cros_test_lib
from chromite.lib import gs
from chromite.lib import gs_unittest
from chromite.lib import osutils
from chromite.lib import project_sdk
from chromite.lib import workspace_lib
# Unittests often access internals.
# pylint: disable=protected-access
class MockSdkCommand(command_unittest.MockCommand):
"""Mock out the `brillo sdk` command."""
TARGET = 'chromite.cli.brillo.brillo_sdk.SdkCommand'
TARGET_CLASS = brillo_sdk.SdkCommand
COMMAND = 'sdk'
class BrilloSdkTest(cros_test_lib.MockTempDirTestCase):
"""Test class for brillo_sdk module functions."""
def setUp(self):
# Avoid self-updating for most tests.
os.environ[brillo_sdk._BRILLO_SDK_NO_UPDATE] = '1'
self.bootstrap_path = os.path.join(self.tempdir, 'bootstrap')
self.sdk_path = os.path.join(self.tempdir, 'sdk')
self.workspace_path = os.path.join(self.tempdir, 'workspace')
osutils.SafeMakedirs(self.workspace_path)
def fakeRepoRoot(d):
bootstrap_checkouts = os.path.join(self.bootstrap_path,
bootstrap_lib.SDK_CHECKOUTS)
if d.startswith(self.sdk_path) or d.startswith(bootstrap_checkouts):
return d
return None
self.PatchObject(project_sdk, 'FindRepoRoot', side_effect=fakeRepoRoot)
# Prevent actually downloading a repository.
self.mock_repo = self.PatchObject(repository, 'RepoRepository')
# Prevent actual GS interaction.
self.gs_mock = self.StartPatcher(gs_unittest.GSContextMock())
self.gs_mock.SetDefaultCmdResult()
# Looking up the 'latest' version from GS will return 4.5.6
self.latest_version = '4.5.6'
self.gs_mock.AddCmdResult(
['cat', constants.BRILLO_LATEST_RELEASE_URL],
output=self.latest_version)
def testResolveLatest(self):
"""Tests _ResolveLatest()."""
result = brillo_sdk._ResolveLatest(gs.GSContext())
self.assertEqual(self.latest_version, result)
def testUpdateWorkspaceSdk(self):
"""Tests _UpdateWorkspaceSdk() with a numeric version."""
brillo_sdk._UpdateWorkspaceSdk(
gs.GSContext(), self.bootstrap_path, self.workspace_path, '1.2.3')
# Given the explicit path and version, sync what we expect, and where.
expected = [mock.call(mock.ANY,
os.path.join(self.tempdir,
'bootstrap/sdk_checkouts/1.2.3'),
depth=1,
repo_cmd=mock.ANY),
mock.call().Sync()]
self.assertEqual(expected, self.mock_repo.mock_calls)
# Update a second time, to ensure it does nothing the second time.
brillo_sdk._UpdateWorkspaceSdk(
gs.GSContext(), self.bootstrap_path, self.workspace_path, '1.2.3')
self.assertEqual(expected, self.mock_repo.mock_calls)
def testUpdateWorkspaceSdkLatest(self):
"""Tests _UpdateWorkspaceSdk() with 'latest'."""
brillo_sdk._UpdateWorkspaceSdk(
gs.GSContext(), self.bootstrap_path, self.workspace_path, 'latest')
# Given the explicit path and version, sync what we expect, and where.
expected = [mock.call(mock.ANY,
os.path.join(self.tempdir,
'bootstrap/sdk_checkouts',
self.latest_version),
depth=1,
repo_cmd=mock.ANY),
mock.call().Sync()]
self.assertEqual(expected, self.mock_repo.mock_calls)
def testUpdateWorkspaceSdkTot(self):
"""Tests _UpdateWorkspaceSdk() with 'tot'."""
brillo_sdk._UpdateWorkspaceSdk(
gs.GSContext(), self.bootstrap_path, self.workspace_path, 'tot')
# Given the explicit path and version, sync what we expect, and where.
expected = [mock.call(constants.MANIFEST_URL,
os.path.join(self.tempdir,
'bootstrap/sdk_checkouts/tot'),
groups='project_sdk',
repo_cmd=mock.ANY),
mock.call().Sync()]
self.assertEqual(expected, self.mock_repo.mock_calls)
# Update a second time, to ensure it DOES update.
brillo_sdk._UpdateWorkspaceSdk(
gs.GSContext(), self.bootstrap_path, self.workspace_path, 'tot')
self.assertEqual(2 * expected, self.mock_repo.mock_calls)
def testDownloadSdk(self):
"""Tests DownloadSdk() with a numeric version."""
brillo_sdk._DownloadSdk(gs.GSContext(), self.sdk_path, '1.2.3')
# Given the explicit path and version, sync what we expect, and where.
expected = [mock.call(mock.ANY,
self.sdk_path,
depth=1,
repo_cmd=mock.ANY),
mock.call().Sync()]
self.assertEqual(expected, self.mock_repo.mock_calls)
# Verify that the right version number was written out.
sdk_version_file = project_sdk.VersionFile(self.sdk_path)
self.assertEqual('1.2.3', osutils.ReadFile(sdk_version_file))
def testDownloadSdkFailureCleanup(self):
"""Tests DownloadSdk() with a numeric version."""
# Create sdk_path, and put something in it so we confirm it's gone.
osutils.Touch(os.path.join(self.sdk_path, 'contents'), makedirs=True)
# Prep for failure via the repo mock.
class FakeException(Exception):
"""Raised to simulate a failure."""
self.mock_repo.side_effect = FakeException('Testing a failure.')
# Run, and fail.
with self.assertRaises(FakeException):
brillo_sdk._DownloadSdk(gs.GSContext(), self.sdk_path, '1.2.3')
# Make sure the SDK dir was cleaned up.
self.assertFalse(os.path.exists(self.sdk_path))
class SdkVersionExistsTest(cros_test_lib.WorkspaceTestCase):
"""Tests for _SdkVersionExists()."""
VALID_LOCAL_SDK_VERSION = '1.2.3'
VALID_GS_SDK_VERSION = '4.5.6'
VALID_GS_SDK_URL = brillo_sdk._GetSdkManifestUrl(VALID_GS_SDK_VERSION)
class GSContextFake(object):
"""Fake GSContext class for Exists() functionality."""
def Exists(self, url, **_kwargs):
"""Only VALID_GS_SDK_URL returns True."""
return url == SdkVersionExistsTest.VALID_GS_SDK_URL
def _VerifySdkVersionExists(self, version, expected):
"""Verifies _SdkVersionExists().
Args:
version: Version to pass in.
expected: Expected result.
"""
result = brillo_sdk._SdkVersionExists(self.GSContextFake(),
self.bootstrap_path, version)
self.assertEqual(expected, result)
def testSdkVersionExists(self):
"""Tests the expected behavior."""
self.CreateBootstrap(sdk_version=self.VALID_LOCAL_SDK_VERSION)
self._VerifySdkVersionExists('tot', True)
self._VerifySdkVersionExists('latest', True)
self._VerifySdkVersionExists(self.VALID_LOCAL_SDK_VERSION, True)
self._VerifySdkVersionExists(self.VALID_GS_SDK_VERSION, True)
self._VerifySdkVersionExists('tot2', False)
self._VerifySdkVersionExists('', False)
class BrilloSdkTestUpdateBootstrap(cros_test_lib.MockTempDirTestCase):
"""Test the bootstrap update functionality of brillo_sdk.
This is a new class, to avoid mocks interfering with each other.
"""
def setUp(self):
if brillo_sdk._BRILLO_SDK_NO_UPDATE in os.environ:
del os.environ[brillo_sdk._BRILLO_SDK_NO_UPDATE]
self.bootstrap_path = os.path.join(self.tempdir, 'bootstrap')
self.rc_mock = self.StartPatcher(cros_build_lib_unittest.RunCommandMock())
self.rc_mock.SetDefaultCmdResult()
def testUpdateBootstrap(self):
"""Tests _UpdateBootstrap()."""
with self.assertRaises(commandline.ExecRequiredError):
brillo_sdk._UpdateBootstrap(self.bootstrap_path)
# Test we did the git pull before raising....
self.rc_mock.assertCommandContains(
['git', 'pull'], cwd=self.bootstrap_path)
# Test we updated our env before raising....
self.assertIn(brillo_sdk._BRILLO_SDK_NO_UPDATE, os.environ)
def testBootstrapAlreadyUpdated(self):
"""Tests _UpdateBootstrap() doesn't run if already updated."""
# Mark that we already updated.
os.environ[brillo_sdk._BRILLO_SDK_NO_UPDATE] = '1'
# Try to update again (no exception raised).
brillo_sdk._UpdateBootstrap(self.bootstrap_path)
# Test we didn't run a git pull.
self.assertEquals(0, self.rc_mock.call_count)
class BrilloSdkCommandTest(cros_test_lib.OutputTestCase,
cros_test_lib.WorkspaceTestCase):
"""Test class for our SdkCommand class."""
def setUp(self):
if brillo_sdk._BRILLO_SDK_NO_UPDATE in os.environ:
del os.environ[brillo_sdk._BRILLO_SDK_NO_UPDATE]
self.cmd_mock = None
# Workspace is supposed to exist in advance.
self.CreateBootstrap()
self.CreateWorkspace()
# Pretend we are outside the chroot, since this command only runs there.
self.mock_inside = self.PatchObject(cros_build_lib, 'IsInsideChroot',
return_value=False)
# Need to mock this; since RunCommand() is mocked out, all the environment
# checks would fail.
self.verify_environment_mock = self.PatchObject(
project_sdk, 'VerifyEnvironment', return_value=True)
# Prevent repo operations.
self.PatchObject(repository, 'PrepManifestForRepo')
self.PatchObject(repository, 'RepoRepository')
# Default to valid SDK version.
self.sdk_version_exists_mock = self.PatchObject(
brillo_sdk, '_SdkVersionExists', return_value=True)
def SetupCommandMock(self, cmd_args):
"""Sets up the command mock."""
self.cmd_mock = MockSdkCommand(cmd_args)
self.StartPatcher(self.cmd_mock)
def testHandleSelfUpdateAndRestart(self):
"""Tests that --update causes a re-exec."""
self.SetupCommandMock(['--update', 'latest'])
with self.assertRaises(commandline.ExecRequiredError):
self.cmd_mock.inst.Run()
def testHandleVersionOnlyNoUpdate(self):
"""Tests that `cros sdk` logs a version and doesn't re-exec."""
self.SetupCommandMock([])
workspace_lib.SetActiveSdkVersion(self.workspace_path, '1.2.3')
with self.OutputCapturer():
self.cmd_mock.inst.Run()
self.AssertOutputContainsLine('1.2.3', check_stderr=True)
def testHandleSelfUpdateAfterRestart(self):
"""Tests that --update doesn't re-exec a second time."""
os.environ[brillo_sdk._BRILLO_SDK_NO_UPDATE] = '1'
self.SetupCommandMock(['--update', 'latest'])
self.cmd_mock.inst.Run()
def testVerifyEnvironmentAfterSelfUpdate(self):
"""Tests that environment verification happens after self-update."""
update_bootstrap_mock = self.PatchObject(brillo_sdk, '_UpdateBootstrap')
self.verify_environment_mock.side_effect = (
lambda: update_bootstrap_mock.called)
self.SetupCommandMock(['--update', 'latest'])
self.cmd_mock.inst.Run()
def testInvalidUpdateVersion(self):
"""Tests --update with an invalid SDK version."""
update_bootstrap_mock = self.PatchObject(brillo_sdk, '_UpdateBootstrap')
update_workspace_sdk_mock = self.PatchObject(brillo_sdk,
'_UpdateWorkspaceSdk')
self.sdk_version_exists_mock.return_value = False
self.SetupCommandMock(['--update', 'bad_version'])
with self.assertRaises(cros_build_lib.DieSystemExit):
self.cmd_mock.inst.Run()
# None of the update functions should have been called.
self.assertFalse(update_bootstrap_mock.called)
self.assertFalse(self.verify_environment_mock.called)
self.assertFalse(update_workspace_sdk_mock.called)
def testVersionOption(self):
"""Tests that --version prints to stdout."""
self.SetupCommandMock(['--version'])
workspace_lib.SetActiveSdkVersion(self.workspace_path, 'foo')
with self.OutputCapturer():
self.cmd_mock.inst.Run()
self.AssertOutputContainsLine('foo')
def testVersionOptionSdkNotFound(self):
"""Tests that --version errors out if a version can't be found."""
self.SetupCommandMock(['--version'])
with self.OutputCapturer():
with self.assertRaises(cros_build_lib.DieSystemExit):
self.cmd_mock.inst.Run()
self.AssertOutputContainsLine('This workspace does not have an SDK.',
check_stderr=True, check_stdout=False)