aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDon Garrett <dgarrett@google.com>2016-01-06 16:01:32 -0800
committerDon Garrett <dgarrett@google.com>2016-01-08 18:24:00 -0800
commit374a65924587a284466469f2b339fd921664dc82 (patch)
tree6cd784a39045864813871f00b293788f8c622f17
parent3a2b27fca144f018271d8f17a1b636f9f4c57320 (diff)
downloadbbuildbot_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.py160
-rw-r--r--config_dump.json4
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"
}
}