Implement SimpleChromeWorkflowTest in the Test endpoint.
BUG=chromium:999670
TEST=manual, run_tests
Change-Id: If274a040a7326a2b5c4a0cf9d81808d59b665b4b
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/chromite/+/1845358
Reviewed-by: Alex Klein <saklein@chromium.org>
Tested-by: Michael Mortensen <mmortensen@google.com>
Commit-Queue: Michael Mortensen <mmortensen@google.com>
diff --git a/api/contrib/call_templates/test__simple_chrome_workflow_test_example_input.json b/api/contrib/call_templates/test__simple_chrome_workflow_test_example_input.json
new file mode 100644
index 0000000..62490f7
--- /dev/null
+++ b/api/contrib/call_templates/test__simple_chrome_workflow_test_example_input.json
@@ -0,0 +1,14 @@
+{
+ "sysroot": {
+ "build_target": {
+ "name": "betty"
+ },
+ "path": "/build/betty"
+ },
+ "chrome_root": "/path/to/chrome",
+ "goma_config": {
+ "goma_dir": "/path/to/goma",
+ "goma_client_json": "/path/to/goma/client/json",
+ "chromeos_goma_dir": "/path/to/chromeos/goma/dir"
+ }
+}
diff --git a/api/controller/test.py b/api/controller/test.py
index 2a115d7..3b88757 100644
--- a/api/controller/test.py
+++ b/api/controller/test.py
@@ -17,6 +17,7 @@
from chromite.api import validate
from chromite.api.controller import controller_util
from chromite.api.gen.chromite.api import test_pb2
+from chromite.cbuildbot import goma_util
from chromite.lib import build_target_util
from chromite.lib import constants
from chromite.lib import cros_build_lib
@@ -111,6 +112,27 @@
return controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY
+
+@faux.all_empty
+@validate.require('sysroot.path', 'sysroot.build_target.name', 'chrome_root')
+@validate.validation_complete
+def SimpleChromeWorkflowTest(input_proto, _output_proto, _config):
+ """Run SimpleChromeWorkflow tests."""
+ if input_proto.goma_config.goma_dir:
+ chromeos_goma_dir = input_proto.goma_config.chromeos_goma_dir or None
+ goma = goma_util.Goma(
+ input_proto.goma_config.goma_dir,
+ input_proto.goma_config.goma_client_json,
+ stage_name='BuildApiTestSimpleChrome',
+ chromeos_goma_dir=chromeos_goma_dir)
+ else:
+ goma = None
+ return test.SimpleChromeWorkflowTest(input_proto.sysroot.path,
+ input_proto.sysroot.build_target.name,
+ input_proto.chrome_root,
+ goma)
+
+
@faux.all_empty
@validate.require('build_target.name', 'vm_path.path', 'test_harness',
'vm_tests')
diff --git a/api/controller/test_unittest.py b/api/controller/test_unittest.py
index 2289895..86f1b50 100644
--- a/api/controller/test_unittest.py
+++ b/api/controller/test_unittest.py
@@ -139,6 +139,75 @@
self.assertFalse(self.rc.call_count)
+class SimpleChromeWorkflowTestTest(cros_test_lib.MockTestCase,
+ api_config.ApiConfigMixin):
+ """Test the SimpleChromeWorkflowTest endpoint."""
+
+ @staticmethod
+ def _Output():
+ return test_pb2.SimpleChromeWorkflowTestResponse()
+
+ def _Input(self, sysroot_path=None, build_target=None, chrome_root=None,
+ goma_config=None):
+ proto = test_pb2.SimpleChromeWorkflowTestRequest()
+ if sysroot_path:
+ proto.sysroot.path = sysroot_path
+ if build_target:
+ proto.sysroot.build_target.name = build_target
+ if chrome_root:
+ proto.chrome_root = chrome_root
+ if goma_config:
+ proto.goma_config = goma_config
+ return proto
+
+ def setUp(self):
+ self.chrome_path = 'path/to/chrome'
+ self.sysroot_dir = 'build/board'
+ self.build_target = 'amd64'
+ self.mock_simple_chrome_workflow_test = self.PatchObject(
+ test_service, 'SimpleChromeWorkflowTest')
+
+ def testMissingBuildTarget(self):
+ """Test VmTest dies when build_target not set."""
+ input_proto = self._Input(build_target=None, sysroot_path='/sysroot/dir',
+ chrome_root='/chrome/path')
+ with self.assertRaises(cros_build_lib.DieSystemExit):
+ test_controller.SimpleChromeWorkflowTest(input_proto, None,
+ self.api_config)
+
+ def testMissingSysrootPath(self):
+ """Test VmTest dies when build_target not set."""
+ input_proto = self._Input(build_target='board', sysroot_path=None,
+ chrome_root='/chrome/path')
+ with self.assertRaises(cros_build_lib.DieSystemExit):
+ test_controller.SimpleChromeWorkflowTest(input_proto, None,
+ self.api_config)
+
+ def testMissingChromeRoot(self):
+ """Test VmTest dies when build_target not set."""
+ input_proto = self._Input(build_target='board', sysroot_path='/sysroot/dir',
+ chrome_root=None)
+ with self.assertRaises(cros_build_lib.DieSystemExit):
+ test_controller.SimpleChromeWorkflowTest(input_proto, None,
+ self.api_config)
+
+ def testSimpleChromeWorkflowTest(self):
+ """Call SimpleChromeWorkflowTest with valid args and temp dir."""
+ request = self._Input(sysroot_path='sysroot_path', build_target='board',
+ chrome_root='/path/to/chrome')
+ response = self._Output()
+
+ test_controller.SimpleChromeWorkflowTest(request, response, self.api_config)
+ self.mock_simple_chrome_workflow_test.assert_called()
+
+ def testValidateOnly(self):
+ request = self._Input(sysroot_path='sysroot_path', build_target='board',
+ chrome_root='/path/to/chrome')
+ test_controller.SimpleChromeWorkflowTest(request, self._Output(),
+ self.validate_only_config)
+ self.mock_simple_chrome_workflow_test.assert_not_called()
+
+
class VmTestTest(cros_test_lib.RunCommandTestCase, api_config.ApiConfigMixin):
"""Test the VmTest endpoint."""
diff --git a/service/test.py b/service/test.py
index 6cb831a..366855e 100644
--- a/service/test.py
+++ b/service/test.py
@@ -12,9 +12,12 @@
import os
import re
+import shutil
+from chromite.cbuildbot import commands
from chromite.lib import constants
from chromite.lib import cros_build_lib
+from chromite.lib import cros_logging as logging
from chromite.lib import failures_lib
from chromite.lib import moblab_vm
from chromite.lib import osutils
@@ -198,6 +201,163 @@
)
+def SimpleChromeWorkflowTest(sysroot_path, build_target_name, chrome_root,
+ goma):
+ """Execute SimpleChrome workflow tests
+
+ Args:
+ sysroot_path (str): The sysroot path for testing Chrome.
+ build_target_name (str): Board build target
+ chrome_root (str): Path to Chrome source root.
+ goma (goma_util.Goma): Goma object (or None).
+ """
+ board_dir = 'out_%s' % build_target_name
+
+ out_board_dir = os.path.join(chrome_root, board_dir, 'Release')
+ use_goma = goma != None
+ extra_args = []
+
+ with osutils.TempDir(prefix='chrome-sdk-cache') as tempdir:
+ sdk_cmd = _InitSimpleChromeSDK(tempdir, build_target_name, sysroot_path,
+ chrome_root, use_goma)
+
+ if goma:
+ extra_args.extend(['--nostart-goma', '--gomadir', goma.linux_goma_dir])
+
+ _BuildChrome(sdk_cmd, chrome_root, out_board_dir, goma)
+ _TestDeployChrome(sdk_cmd, out_board_dir)
+ _VMTestChrome(build_target_name, sdk_cmd)
+
+
+def _InitSimpleChromeSDK(tempdir, build_target_name, sysroot_path, chrome_root,
+ use_goma):
+ """Create ChromeSDK object for executing 'cros chrome-sdk' commands.
+
+ Args:
+ tempdir (string): Tempdir for command execution.
+ build_target_name (string): Board build target.
+ sysroot_path (string): Sysroot for Chrome to use.
+ chrome_root (string): Path to Chrome.
+ use_goma (bool): Whether to use goma.
+
+ Returns:
+ A ChromeSDK object.
+ """
+ extra_args = ['--cwd', chrome_root, '--sdk-path', sysroot_path]
+ cache_dir = os.path.join(tempdir, 'cache')
+
+ sdk_cmd = commands.ChromeSDK(
+ constants.SOURCE_ROOT, build_target_name, chrome_src=chrome_root,
+ goma=use_goma, extra_args=extra_args, cache_dir=cache_dir)
+ return sdk_cmd
+
+
+def _VerifySDKEnvironment(out_board_dir):
+ """Make sure the SDK environment is set up properly.
+
+ Args:
+ out_board_dir (str): Output SDK dir for board.
+ """
+ if not os.path.exists(out_board_dir):
+ raise AssertionError('%s not created!' % out_board_dir)
+ logging.info('ARGS.GN=\n%s',
+ osutils.ReadFile(os.path.join(out_board_dir, 'args.gn')))
+
+
+def _BuildChrome(sdk_cmd, chrome_root, out_board_dir, goma):
+ """Build Chrome with SimpleChrome environment.
+
+ Args:
+ sdk_cmd (ChromeSDK object): sdk_cmd to run cros chrome-sdk commands.
+ chrome_root (string): Path to Chrome.
+ out_board_dir (string): Path to board directory.
+ goma (goma_util.Goma): Goma object
+ """
+ # Validate fetching of the SDK and setting everything up.
+ sdk_cmd.Run(['true'])
+
+ sdk_cmd.Run(['gclient', 'runhooks'])
+
+ # Generate args.gn and ninja files.
+ gn_cmd = os.path.join(chrome_root, 'buildtools', 'linux64', 'gn')
+ gn_gen_cmd = '%s gen "%s" --args="$GN_ARGS"' % (gn_cmd, out_board_dir)
+ sdk_cmd.Run(['bash', '-c', gn_gen_cmd])
+
+ _VerifySDKEnvironment(out_board_dir)
+
+ if goma:
+ # If goma is enabled, start goma compiler_proxy here, and record
+ # several information just before building Chrome is started.
+ goma.Start()
+ extra_env = goma.GetExtraEnv()
+ ninja_env_path = os.path.join(goma.goma_log_dir, 'ninja_env')
+ sdk_cmd.Run(['env', '--null'],
+ run_args={'extra_env': extra_env,
+ 'log_stdout_to_file': ninja_env_path})
+ osutils.WriteFile(os.path.join(goma.goma_log_dir, 'ninja_cwd'),
+ sdk_cmd.cwd)
+ osutils.WriteFile(os.path.join(goma.goma_log_dir, 'ninja_command'),
+ cros_build_lib.CmdToStr(sdk_cmd.GetNinjaCommand()))
+ else:
+ extra_env = None
+
+ result = None
+ try:
+ # Build chromium.
+ result = sdk_cmd.Ninja(run_args={'extra_env': extra_env})
+ finally:
+ # In teardown, if goma is enabled, stop the goma compiler proxy,
+ # and record/copy some information to log directory, which will be
+ # uploaded to the goma's server in a later stage.
+ if goma:
+ goma.Stop()
+ ninja_log_path = os.path.join(chrome_root,
+ sdk_cmd.GetNinjaLogPath())
+ if os.path.exists(ninja_log_path):
+ shutil.copy2(ninja_log_path,
+ os.path.join(goma.goma_log_dir, 'ninja_log'))
+ if result:
+ osutils.WriteFile(os.path.join(goma.goma_log_dir, 'ninja_exit'),
+ str(result.returncode))
+
+
+def _TestDeployChrome(sdk_cmd, out_board_dir):
+ """Test SDK deployment.
+
+ Args:
+ sdk_cmd (ChromeSDK object): sdk_cmd to run cros chrome-sdk commands.
+ out_board_dir (string): Path to board directory.
+ """
+ with osutils.TempDir(prefix='chrome-sdk-stage') as tempdir:
+ # Use the TOT deploy_chrome.
+ script_path = os.path.join(
+ constants.SOURCE_ROOT, constants.CHROMITE_BIN_SUBDIR, 'deploy_chrome')
+ sdk_cmd.Run([script_path, '--build-dir', out_board_dir,
+ '--staging-only', '--staging-dir', tempdir])
+ # Verify chrome is deployed.
+ chromepath = os.path.join(tempdir, 'chrome')
+ if not os.path.exists(chromepath):
+ raise AssertionError(
+ 'deploy_chrome did not run successfully! Searched %s' % (chromepath))
+
+
+def _VMTestChrome(board, sdk_cmd):
+ """Run cros_run_test."""
+
+ # This is how generic_stages.py creates an image_dir_symlink in
+ # GetImageDirSymlink
+ image_dir_symlink = os.path.join(constants.SOURCE_ROOT, 'src', 'build',
+ 'images', board, 'latest-cbuildbot')
+
+ image_path = os.path.join(image_dir_symlink,
+ constants.VM_IMAGE_BIN)
+
+ # Run VM test for boards where we've built a VM.
+ if image_path and os.path.exists(image_path):
+ sdk_cmd.VMTest(image_path)
+
+
+
def ValidateMoblabVmTest(results_dir):
"""Determine if the VM test passed or not.
diff --git a/service/test_unittest.py b/service/test_unittest.py
index 3db48d8..8b7df29 100644
--- a/service/test_unittest.py
+++ b/service/test_unittest.py
@@ -9,9 +9,13 @@
import contextlib
import os
+import shutil
import mock
+from chromite.api.gen.chromiumos import common_pb2
+from chromite.cbuildbot import commands
+from chromite.cbuildbot import goma_util
from chromite.lib import build_target_util
from chromite.lib import chroot_lib
from chromite.lib import cros_build_lib
@@ -235,6 +239,88 @@
], enter_chroot=True, chroot_args=self.chroot.get_enter_args())
+class SimpleChromeWorkflowTestTest(cros_test_lib.MockTempDirTestCase):
+ """Unit tests for SimpleChromeWorkflowTest."""
+
+ def setUp(self):
+ self.chrome_root = '/path/to/chrome/root'
+ self.sysroot_path = '/chroot/path/sysroot/path'
+ self.build_target = 'board'
+
+ self.goma_mock = self.PatchObject(goma_util, 'Goma')
+
+ self.chrome_sdk_run_mock = self.PatchObject(commands.ChromeSDK, 'Run')
+
+ # SimpleChromeTest workflow creates directories based on objects that are
+ # mocked for this test, so patch osutils.WriteFile
+ self.write_mock = self.PatchObject(osutils, 'WriteFile')
+
+ self.PatchObject(cros_build_lib, 'CmdToStr', return_value='CmdToStr value')
+ self.PatchObject(shutil, 'copy2')
+
+ def testSimpleChromeWorkflowTest(self):
+ goma_test_dir = os.path.join(self.tempdir, 'goma_test_dir')
+ goma_test_json_string = os.path.join(self.tempdir, 'goma_json_string.txt')
+ chromeos_goma_dir = os.path.join(self.tempdir, 'chromeos_goma_dir')
+ goma_config = common_pb2.GomaConfig(goma_dir=goma_test_dir,
+ goma_client_json=goma_test_json_string)
+ osutils.SafeMakedirs(goma_test_dir)
+ osutils.SafeMakedirs(chromeos_goma_dir)
+ osutils.Touch(goma_test_json_string)
+ goma = goma_util.Goma(
+ goma_config.goma_dir,
+ goma_config.goma_client_json,
+ stage_name='BuildApiTestSimpleChrome',
+ chromeos_goma_dir=chromeos_goma_dir)
+
+ mock_goma_log_dir = os.path.join(self.tempdir, 'goma_log_dir')
+ osutils.SafeMakedirs(mock_goma_log_dir)
+ goma.goma_log_dir = mock_goma_log_dir
+
+ # For this test, we avoid running test._VerifySDKEnvironment because use of
+ # other mocks prevent creating the SDK dir that _VerifySDKEnvironment checks
+ # for
+ self.PatchObject(test, '_VerifySDKEnvironment')
+
+ self.PatchObject(os.path, 'exists', return_value=True)
+
+
+ ninja_cmd = self.PatchObject(commands.ChromeSDK, 'GetNinjaCommand',
+ return_value='ninja command')
+
+ test.SimpleChromeWorkflowTest(self.sysroot_path, self.build_target,
+ self.chrome_root, goma)
+ # Verify ninja_cmd calls.
+ ninja_calls = [mock.call(), mock.call(debug=False)]
+ ninja_cmd.assert_has_calls(ninja_calls)
+
+ # Verify calls with args to chrome_sdk_run made by service/test.py.
+ gn_dir = os.path.join(self.chrome_root, 'buildtools/linux64/gn')
+ board_out_dir = os.path.join(self.chrome_root, 'out_board/Release')
+
+ self.chrome_sdk_run_mock.assert_any_call(['gclient', 'runhooks'])
+ self.chrome_sdk_run_mock.assert_any_call(['true'])
+ self.chrome_sdk_run_mock.assert_any_call(
+ ['bash', '-c', ('%s gen "%s" --args="$GN_ARGS"'
+ % (gn_dir, board_out_dir))])
+ self.chrome_sdk_run_mock.assert_any_call(
+ ['env', '--null'], run_args=mock.ANY)
+ self.chrome_sdk_run_mock.assert_any_call('ninja command', run_args=mock.ANY)
+ self.chrome_sdk_run_mock.assert_any_call(
+ ['/mnt/host/source/chromite/bin/deploy_chrome',
+ '--build-dir', board_out_dir, '--staging-only',
+ '--staging-dir', mock.ANY])
+ self.chrome_sdk_run_mock.assert_any_call(
+ ['cros_run_test', '--copy-on-write', '--deploy', '--board=board',
+ ('--image-path=/mnt/host/source/src/build/images/'
+ 'board/latest-cbuildbot/chromiumos_qemu_image.bin'),
+ '--build-dir=out_board/Release'])
+
+ # Verify goma mock was started and stopped.
+ self.goma_mock.Start.assert_called_once()
+ self.goma_mock.Stop.assert_called_once()
+
+
class ValidateMoblabVmTestTest(MoblabVmTestCase):
"""Unit tests for ValidateMoblabVmTest."""