blob: f935ec784794a8035824b78132c32d1b3063faed [file] [log] [blame]
# -*- coding: utf-8 -*-
# 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.
"""Unittests for simpler builders."""
from __future__ import print_function
import copy
import mock
import os
from chromite.cbuildbot import cbuildbot_run
from chromite.cbuildbot.builders import generic_builders
from chromite.cbuildbot.builders import simple_builders
from chromite.cbuildbot.stages import completion_stages
from chromite.cbuildbot.stages import generic_stages
from chromite.cbuildbot.stages import handle_changes_stages
from chromite.cbuildbot.stages import tast_test_stages
from chromite.cbuildbot.stages import vm_test_stages
from chromite.lib import config_lib
from chromite.lib import constants
from chromite.lib import cros_test_lib
from chromite.lib import failures_lib
from chromite.lib import osutils
from chromite.lib import parallel
from chromite.lib.buildstore import FakeBuildStore
from chromite.scripts import cbuildbot
# pylint: disable=protected-access
class SimpleBuilderTest(cros_test_lib.MockTempDirTestCase):
"""Tests for the main code paths in simple_builders.SimpleBuilder"""
def setUp(self):
# List of all stages that would have been called as part of this run.
self.called_stages = []
# Map from stage class to exception to be raised when stage is run.
self.stage_exceptions = {}
# VM test stages that are run by SimpleBuilder._RunVMTests.
self.all_vm_test_stages = [vm_test_stages.VMTestStage,
tast_test_stages.TastVMTestStage]
self.buildstore = FakeBuildStore()
# Simple new function that redirects RunStage to record all stages to be
# run rather than mock them completely. These can be used in a test to
# assert something has been called.
def run_stage(_class_instance, stage_name, *args, **_kwargs):
# It's more useful to record the actual stage that's wrapped within
# RepeatStage or RetryStage.
if stage_name in [generic_stages.RepeatStage, generic_stages.RetryStage]:
stage_name = args[1]
self.called_stages.append(stage_name)
if stage_name in self.stage_exceptions:
raise self.stage_exceptions[stage_name]
# Parallel version.
def run_parallel_stages(_class_instance, *_args):
# Since parallel stages are forked processes, we can't actually
# update anything here unless we want to do interprocesses comms.
pass
self.buildroot = os.path.join(self.tempdir, 'buildroot')
chroot_path = os.path.join(self.buildroot, constants.DEFAULT_CHROOT_DIR)
osutils.SafeMakedirs(os.path.join(chroot_path, 'tmp'))
self.PatchObject(generic_builders.Builder, '_RunStage',
new=run_stage)
self.PatchObject(simple_builders.SimpleBuilder, '_RunParallelStages',
new=run_parallel_stages)
self.PatchObject(cbuildbot_run._BuilderRunBase, 'GetVersion',
return_value='R32-1234.0.0')
self._manager = parallel.Manager()
self._manager.__enter__()
def tearDown(self):
# Mimic exiting a 'with' statement.
self._manager.__exit__(None, None, None)
def _initConfig(
self, bot_id, master=False, extra_argv=None, override_hw_test_config=None,
models=None):
"""Return normal options/build_config for |bot_id|"""
site_config = config_lib.GetConfig()
build_config = copy.deepcopy(site_config[bot_id])
build_config['master'] = master
build_config['important'] = False
if models:
build_config['models'] = models
# Use the cbuildbot parser to create properties and populate default values.
parser = cbuildbot._CreateParser()
argv = (['-r', self.buildroot, '--buildbot', '--debug', '--nochromesdk'] +
(extra_argv if extra_argv else []) + [bot_id])
options = cbuildbot.ParseCommandLine(parser, argv)
# Yikes.
options.managed_chrome = build_config['sync_chrome']
# Iterate through override and update HWTestConfig attributes.
if override_hw_test_config:
for key, val in override_hw_test_config.iteritems():
for hw_test in build_config.hw_tests:
setattr(hw_test, key, val)
return cbuildbot_run.BuilderRun(
options, site_config, build_config, self._manager)
def _RunVMTests(self):
"""Helper method that runs VM tests and returns exceptions.
Returns:
List of exception classes in CompoundFailure.
"""
board = 'betty-release'
builder_run = self._initConfig(board)
exception_types = []
try:
simple_builders.SimpleBuilder(builder_run, self.buildstore)._RunVMTests(
builder_run, board)
except failures_lib.CompoundFailure as f:
exception_types = [e.type for e in f.exc_infos]
return exception_types
def testRunStagesPreCQ(self):
"""Verify RunStages for PRE_CQ_LAUNCHER_TYPE builders"""
builder_run = self._initConfig('pre-cq-launcher')
simple_builders.SimpleBuilder(builder_run, self.buildstore).RunStages()
def testRunStagesBranchUtil(self):
"""Verify RunStages for CREATE_BRANCH_TYPE builders"""
extra_argv = ['--branch-name', 'foo', '--version', '1234']
builder_run = self._initConfig(constants.BRANCH_UTIL_CONFIG,
extra_argv=extra_argv)
simple_builders.SimpleBuilder(builder_run, self.buildstore).RunStages()
def testRunStagesChrootBuilder(self):
"""Verify RunStages for CHROOT_BUILDER_TYPE builders"""
builder_run = self._initConfig('chromiumos-sdk')
simple_builders.SimpleBuilder(builder_run, self.buildstore).RunStages()
def testRunStagesDefaultBuild(self):
"""Verify RunStages for standard board builders"""
builder_run = self._initConfig('amd64-generic-full')
builder_run.attrs.chrome_version = 'TheChromeVersion'
simple_builders.SimpleBuilder(builder_run, self.buildstore).RunStages()
def testRunStagesDefaultBuildCompileCheck(self):
"""Verify RunStages for standard board builders (compile only)"""
extra_argv = ['--compilecheck']
builder_run = self._initConfig('amd64-generic-full', extra_argv=extra_argv)
builder_run.attrs.chrome_version = 'TheChromeVersion'
simple_builders.SimpleBuilder(builder_run, self.buildstore).RunStages()
def testRunStagesDefaultBuildHwTests(self):
"""Verify RunStages for boards w/hwtests"""
extra_argv = ['--hwtest']
builder_run = self._initConfig('eve-release', extra_argv=extra_argv)
builder_run.attrs.chrome_version = 'TheChromeVersion'
simple_builders.SimpleBuilder(builder_run, self.buildstore).RunStages()
def testThatWeScheduleHWTestsRegardlessOfBlocking(self):
"""Verify RunStages for boards w/hwtests (blocking).
Make sure the same stages get scheduled regardless of whether their hwtest
suites are marked blocking or not.
"""
extra_argv = ['--hwtest']
builder_run_without_blocking = self._initConfig(
'eve-release', extra_argv=extra_argv,
override_hw_test_config=dict(blocking=False))
builder_run_with_blocking = self._initConfig(
'eve-release', extra_argv=extra_argv,
override_hw_test_config=dict(blocking=True))
builder_run_without_blocking.attrs.chrome_version = 'TheChromeVersion'
builder_run_with_blocking.attrs.chrome_version = 'TheChromeVersion'
simple_builders.SimpleBuilder(builder_run_without_blocking,
self.buildstore).RunStages()
without_blocking_stages = list(self.called_stages)
self.called_stages = []
simple_builders.SimpleBuilder(builder_run_with_blocking,
self.buildstore).RunStages()
self.assertEqual(without_blocking_stages, self.called_stages)
def testUnifiedBuildsRunHwTestsForAllModels(self):
"""Verify hwtests run for model fanout with unified builds"""
extra_argv = ['--hwtest']
unified_build = self._initConfig(
'eve-release',
extra_argv=extra_argv,
models=[config_lib.ModelTestConfig('model1', 'model1'),
config_lib.ModelTestConfig(
'model2', 'model2', ['sanity', 'bvt-inline'])])
unified_build.attrs.chrome_version = 'TheChromeVersion'
simple_builders.SimpleBuilder(unified_build, self.buildstore).RunStages()
def testAllVMTestStagesSucceed(self):
"""Verify all VM test stages are run."""
self.assertEquals([], self._RunVMTests())
self.assertEquals(self.all_vm_test_stages, self.called_stages)
def testAllVMTestStagesFail(self):
"""Verify failures are reported when all VM test stages fail."""
self.stage_exceptions = {
vm_test_stages.VMTestStage: failures_lib.InfrastructureFailure(),
tast_test_stages.TastVMTestStage: failures_lib.TestFailure(),
}
self.assertEquals(
[failures_lib.InfrastructureFailure, failures_lib.TestFailure],
self._RunVMTests())
self.assertEquals(self.all_vm_test_stages, self.called_stages)
def testVMTestStageFails(self):
"""Verify TastVMTestStage is still run when VMTestStage fails."""
self.stage_exceptions = {
vm_test_stages.VMTestStage: failures_lib.TestFailure(),
}
self.assertEquals([failures_lib.TestFailure], self._RunVMTests())
self.assertEquals(self.all_vm_test_stages, self.called_stages)
def testTastVMTestStageFails(self):
"""Verify VMTestStage is still run when TastVMTestStage fails."""
self.stage_exceptions = {
tast_test_stages.TastVMTestStage: failures_lib.TestFailure(),
}
self.assertEquals([failures_lib.TestFailure], self._RunVMTests())
self.assertEquals(self.all_vm_test_stages, self.called_stages)
class DistributedBuilderTests(SimpleBuilderTest):
"""Tests for DistributedBuilder."""
def testRunStagesCommitQueueMaster(self):
"""Verify RunStages for master-paladin builder."""
builder_run = self._initConfig('master-paladin', master=True)
builder = simple_builders.DistributedBuilder(builder_run, self.buildstore)
builder.sync_stage = mock.Mock()
builder.completion_stage_class = mock.Mock()
builder.RunStages()
self.assertTrue(handle_changes_stages.CommitQueueHandleChangesStage
in self.called_stages)
def testRunStagesCommitQueueMasterWithImportantBuilderFailedException(self):
"""Verify RunStages for CQ-master with ImportantBuilderFailedException."""
builder_run = self._initConfig('master-paladin', master=True)
builder = simple_builders.DistributedBuilder(builder_run, self.buildstore)
builder.sync_stage = mock.Mock()
builder.completion_stage_class = (
completion_stages.CommitQueueCompletionStage)
self.PatchObject(
completion_stages.CommitQueueCompletionStage, '__init__',
return_value=None)
self.PatchObject(
completion_stages.CommitQueueCompletionStage, 'Run',
side_effect=completion_stages.ImportantBuilderFailedException)
self.assertRaises(completion_stages.ImportantBuilderFailedException,
builder.RunStages)
self.assertTrue(handle_changes_stages.CommitQueueHandleChangesStage
in self.called_stages)
def testRunStagesCommitQueueMasterWithStepFailure(self):
"""Verify RunStages for CQ-master with StepFailure."""
builder_run = self._initConfig('master-paladin', master=True)
builder = simple_builders.DistributedBuilder(builder_run, self.buildstore)
builder.sync_stage = mock.Mock()
builder.completion_stage_class = (
completion_stages.CommitQueueCompletionStage)
self.PatchObject(
completion_stages.CommitQueueCompletionStage, '__init__',
return_value=None)
self.PatchObject(
completion_stages.CommitQueueCompletionStage, 'Run',
side_effect=failures_lib.StepFailure)
self.assertRaises(failures_lib.StepFailure, builder.RunStages)
self.assertFalse(handle_changes_stages.CommitQueueHandleChangesStage
in self.called_stages)
def testRunStagesReleaseMaster(self):
"""Verify RunStages for master-release builder."""
builder_run = self._initConfig('master-release', master=True)
builder = simple_builders.DistributedBuilder(builder_run, self.buildstore)
builder.sync_stage = mock.Mock()
builder.completion_stage_class = mock.Mock()
builder.RunStages()
self.assertFalse(handle_changes_stages.CommitQueueHandleChangesStage
in self.called_stages)