diff options
author | Don Garrett <dgarrett@google.com> | 2016-01-06 16:01:32 -0800 |
---|---|---|
committer | Don Garrett <dgarrett@google.com> | 2016-01-08 18:24:00 -0800 |
commit | 374a65924587a284466469f2b339fd921664dc82 (patch) | |
tree | 6cd784a39045864813871f00b293788f8c622f17 | |
parent | 3a2b27fca144f018271d8f17a1b636f9f4c57320 (diff) | |
download | bbuildbot_config-374a65924587a284466469f2b339fd921664dc82.tar.gz |
Add VM tests to bbuildbot builder.
Parameterize lunch target and emulator binary names in the build config.
Add a RunLunchWrapper that setups up the lunch ENVs before invoking a
command.
Add a new stage that starts the brillo emulator as a background process,
waits for "adb devices" to report it's serial number, then runs tests
against the emulator.
BUG=chromium:570810
TEST=bin/cbuildbot --nobootstrap --noreexec --buildbot
--debug --buildroot ~/tmp/buildroot bbuildbot
Change-Id: I16e355f43275df204b0fdbb2aa0d9d00baf8d679
-rw-r--r-- | builders/brillo_builders.py | 160 | ||||
-rw-r--r-- | config_dump.json | 4 |
2 files changed, 146 insertions, 18 deletions
diff --git a/builders/brillo_builders.py b/builders/brillo_builders.py index 52b6206..5080558 100644 --- a/builders/brillo_builders.py +++ b/builders/brillo_builders.py @@ -6,21 +6,35 @@ from __future__ import print_function +import contextlib import os +import re +import subprocess +import tempfile +import time -from chromite.lib import cros_build_lib -from chromite.lib import osutils - +from chromite.cbuildbot import repository from chromite.cbuildbot.builders import generic_builders from chromite.cbuildbot.stages import generic_stages from chromite.cbuildbot.stages import sync_stages -from chromite.cbuildbot import repository +from chromite.lib import cros_build_lib +from chromite.lib import cros_logging as logging +from chromite.lib import osutils + + +class EmulatorFailedToStart(Exception): + """The emulator process isn't running after 10 seconds.""" + + +class EmulatorNotReady(Exception): + """The adb devices command did not discover a valid emulator serial number.""" class BrilloStageBase(generic_stages.BuilderStage): """Base class for all symbols build stages.""" def BrilloRoot(self): + """Root for repo checkout of Brillo.""" # Turn /mnt/data/b/cbuild/android -> /mnt/data/b/cbuild/android_brillo # We have to be OUTSIDE the build root, since this is a new repo checkout. @@ -30,10 +44,29 @@ class BrilloStageBase(generic_stages.BuilderStage): return self._run.buildroot + '_brillo' def BuildOutput(self): - # We store brillo build output in brillo's default output directory. - # (ex: brillo_root/out) + """Returns directory for brillo build output.""" return os.path.join(self.BrilloRoot(), 'out') + def FindShellCmd(self, cmd): + target = self._run.config.lunch_target + + cmd_list = [] + cmd_list.append('. build/envsetup.sh') + cmd_list.append('lunch %s' % target) + cmd_list.append('OUT_DIR=%s' % self.BuildOutput()) + cmd_list.append(' '.join(cmd)) + + return ' > /dev/null && '.join(cmd_list) + + def RunLunchCommand(self, cmd, **kwargs): + """RunCommand with lunch setup.""" + # Default directory to run in. + kwargs.setdefault('cwd', self.BrilloRoot()) + + # We use a shell invocation so environmental variables are preserved. + cmd = self.FindShellCmd(cmd) + return cros_build_lib.RunCommand(cmd, shell=True, **kwargs) + class BrilloCleanStage(BrilloStageBase): """Compile the Brillo checkout.""" @@ -50,10 +83,8 @@ class BrilloSyncStage(BrilloStageBase): """Sync Brillo code to a sub-directory.""" def PerformStage(self): - """Do the sync work.""" + """Fetch and/or update the brillo source code.""" osutils.SafeMakedirs(self.BrilloRoot()) - - # Fetch and/or update the brillo source code. brillo_repo = repository.RepoRepository( manifest_repo_url=self._run.config.brillo_manifest_url, branch=self._run.config.brillo_manifest_branch, @@ -67,15 +98,109 @@ class BrilloBuildStage(BrilloStageBase): def PerformStage(self): """Do the build work.""" - cmd_list = [] - cmd_list.append('. build/envsetup.sh') - cmd_list.append('lunch brilloemulator_arm-eng') - cmd_list.append('OUT_DIR=%s' % self.BuildOutput()) - cmd_list.append('make -j 32') + self.RunLunchCommand(['make', '-j', '32']) - # We use a shell invocation so environmental variables are preserved. - cmd = ' && '.join(cmd_list) - cros_build_lib.RunCommand(cmd, shell=True, cwd=self.BrilloRoot()) + +class BrilloVmTestStage(BrilloStageBase): + """Compile the Brillo checkout.""" + + @contextlib.contextmanager + def RunEmulator(self): + """Run an emulator process in the background, kill it on exit.""" + with tempfile.NamedTemporaryFile(prefix='emulator') as logfile: + cmd = self.FindShellCmd([self._run.config.emulator]) + logging.info('Starting emulator: %s', cmd) + p = subprocess.Popen( + args=(cmd,), + shell=True, + close_fds=True, + stdout=logfile, + stderr=subprocess.STDOUT, + cwd=self.BrilloRoot(), + ) + + + try: + # Give the emulator a little time, and make sure it's still running. + # Failure could be an crash, another copy was left running, etc. + time.sleep(10) + if p.poll() is not None: + logging.error('Emulator is not running after 10 seconds, aborting.') + raise EmulatorFailedToStart() + + yield + finally: + if p.poll() is None: + # Kill emulator, if it's still running. + logging.info('Stopping emulator.') + p.terminate() + + p.wait() + + # Read/dump the emulator output. + logging.info('*') + logging.info('* Emulator Output') + logging.info('*\n%s', osutils.ReadFile(logfile.name)) + logging.info('*') + logging.info('* Emulator End') + logging.info('*') + + def DiscoverEmulatorSerial(self): + """Query for the serial number of the emulator. + + Returns: + String containing the serial number of the emulator, or None + """ + result = self.RunLunchCommand( + ['adb', 'devices'], + redirect_stdout=True, + combine_stdout_stderr=True) + + # Command output before we are ready: + # List of devices attached + # emulator-5554 offline + + # Command output after we are ready: + # List of devices attached + # emulator-5554 device + + m = re.search(r'^([\w-]+)\tdevice$', result.output, re.MULTILINE) + if m: + return m.group(1) + return None + + def WaitForEmulatorSerial(self): + """Retry the query for the emulator serial number, until it's ready. + + Returns: + String containing the serial number of the emulator. + + Raises: + EmulatorNotReady if we timeout waiting (after several minutes). + """ + for _ in xrange(20): + result = self.DiscoverEmulatorSerial() + if result: + return result + time.sleep(10) + + raise EmulatorNotReady() + + def PerformStage(self): + """Run the VM Tests.""" + with self.RunEmulator(): + # To see the emulator, we must sometimes kill/restart the adb server. + self.RunLunchCommand(['adb', 'kill-server']) + + # Wait for the emulator to come up enough to give us a serial number. + serial = self.WaitForEmulatorSerial() + + # Run the tests. + logging.info('Running tests against %s', serial) + self.RunLunchCommand( + ['external/autotest/site_utils/test_droid.py', + '--debug', serial, 'brillo_WhitelistedGtests'], + cwd=self.BrilloRoot()) class BrilloBuilder(generic_builders.Builder): @@ -90,3 +215,4 @@ class BrilloBuilder(generic_builders.Builder): self._RunStage(BrilloCleanStage) self._RunStage(BrilloSyncStage) self._RunStage(BrilloBuildStage) + self._RunStage(BrilloVmTestStage) diff --git a/config_dump.json b/config_dump.json index 1c8c2b7..4842837 100644 --- a/config_dump.json +++ b/config_dump.json @@ -11,6 +11,8 @@ "bbuildbot": { "builder_class_name": "config.builders.brillo_builders.BrilloBuilder", "brillo_manifest_url": "https://android.googlesource.com/brillo/manifest", - "brillo_manifest_branch": "master" + "brillo_manifest_branch": "master", + "lunch_target": "brilloemulator_arm-eng", + "emulator": "brilloemulator-arm" } } |