afdo: Implement chrome-release-afdo-verify builder.

This patch implements a builder to verify the most recent
Chrome afdo profiles (benchmark and CWP). Once verified
that Chrome can be built, it publishes the new profiles
to metadata for PUpr to pick them up.

The patch mostly reuses the functionalities for kernel
afdo verification builder.

BUG=chromium:990855
TEST=https://ci.chromium.org/p/chromeos/builders/general/Try/b8903115280723980960

Change-Id: I16328ac9ea1937183e6066ec199c01e8d03773d5
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/chromite/+/1782961
Tested-by: Tiancong Wang <tcwang@google.com>
Reviewed-by: Manoj Gupta <manojgupta@chromium.org>
Reviewed-by: Evan Hernandez <evanhernandez@chromium.org>
Reviewed-by: Alex Klein <saklein@chromium.org>
Commit-Queue: Tiancong Wang <tcwang@google.com>
diff --git a/api/controller/toolchain.py b/api/controller/toolchain.py
index a88f859..5c69958 100644
--- a/api/controller/toolchain.py
+++ b/api/controller/toolchain.py
@@ -11,11 +11,24 @@
 from chromite.api.gen.chromite.api import toolchain_pb2
 from chromite.lib import toolchain_util
 
-_VALID_ARTIFACT_TYPES = [toolchain_pb2.ORDERFILE, toolchain_pb2.KERNEL_AFDO]
+_NAMES_FOR_ARTIFACTS = {
+    toolchain_pb2.ORDERFILE: 'orderfile',
+    toolchain_pb2.KERNEL_AFDO: 'kernel_afdo',
+    toolchain_pb2.CHROME_AFDO: 'chrome_afdo'
+}
+
+# Using a function instead of a dict because we need to mock these
+# functions in unittest, and mock doesn't play well with a dict definition.
+def _GetMethodForUpdatingArtifacts(artifact_type):
+  return {
+      toolchain_pb2.ORDERFILE: toolchain_util.OrderfileUpdateChromeEbuild,
+      toolchain_pb2.KERNEL_AFDO: toolchain_util.AFDOUpdateKernelEbuild,
+      toolchain_pb2.CHROME_AFDO: toolchain_util.AFDOUpdateChromeEbuild
+  }[artifact_type]
 
 
 @validate.require('build_target.name')
-@validate.is_in('artifact_type', _VALID_ARTIFACT_TYPES)
+@validate.is_in('artifact_type', _NAMES_FOR_ARTIFACTS.keys())
 @validate.validation_complete
 def UpdateEbuildWithAFDOArtifacts(input_proto, output_proto, _config):
   """Update Chrome or kernel ebuild with most recent unvetted artifacts.
@@ -27,16 +40,12 @@
   """
 
   board = input_proto.build_target.name
-  artifact_type = input_proto.artifact_type
-  if artifact_type is toolchain_pb2.ORDERFILE:
-    status = toolchain_util.OrderfileUpdateChromeEbuild(board)
-  else:
-    status = toolchain_util.AFDOUpdateKernelEbuild(board)
-  output_proto.status = status
+  update_method = _GetMethodForUpdatingArtifacts(input_proto.artifact_type)
+  output_proto.status = update_method(board)
 
 
 @validate.require('build_target.name')
-@validate.is_in('artifact_type', _VALID_ARTIFACT_TYPES)
+@validate.is_in('artifact_type', _NAMES_FOR_ARTIFACTS.keys())
 @validate.validation_complete
 def UploadVettedAFDOArtifacts(input_proto, output_proto, _config):
   """Upload a vetted orderfile to GS bucket.
@@ -47,10 +56,6 @@
     _config (api_config.ApiConfig): The API call config.
   """
   board = input_proto.build_target.name
-  if input_proto.artifact_type is toolchain_pb2.ORDERFILE:
-    artifact_type = 'orderfile'
-  else:
-    artifact_type = 'kernel_afdo'
-
+  artifact_type = _NAMES_FOR_ARTIFACTS[input_proto.artifact_type]
   output_proto.status = toolchain_util.UploadAndPublishVettedAFDOArtifacts(
       artifact_type, board)
diff --git a/api/controller/toolchain_unittest.py b/api/controller/toolchain_unittest.py
index 7d66651..709b379 100644
--- a/api/controller/toolchain_unittest.py
+++ b/api/controller/toolchain_unittest.py
@@ -32,6 +32,8 @@
         toolchain_util, 'OrderfileUpdateChromeEbuild', return_value=True)
     self.kernel_command = self.PatchObject(
         toolchain_util, 'AFDOUpdateKernelEbuild', return_value=True)
+    self.chrome_command = self.PatchObject(
+        toolchain_util, 'AFDOUpdateChromeEbuild', return_value=True)
     self.PatchObject(cros_build_lib, 'Die', new=self.mock_die)
 
   def _GetRequest(self, build_target=None, artifact_type=None):
@@ -67,6 +69,7 @@
                                             self.api_config)
     self.orderfile_command.assert_called_once_with(self.board)
     self.kernel_command.assert_not_called()
+    self.chrome_command.assert_not_called()
 
   def testKernelAFDOSuccess(self):
     """Test the command is called correctly with kernel afdo."""
@@ -76,10 +79,20 @@
                                             self.api_config)
     self.kernel_command.assert_called_once_with(self.board)
     self.orderfile_command.assert_not_called()
+    self.chrome_command.assert_not_called()
+
+  def testChromeAFDOSuccess(self):
+    """Test the command is called correctly with Chrome afdo."""
+    request = self._GetRequest(
+        build_target=self.board, artifact_type=toolchain_pb2.CHROME_AFDO)
+    toolchain.UpdateEbuildWithAFDOArtifacts(request, self.response,
+                                            self.api_config)
+    self.chrome_command.assert_called_once_with(self.board)
+    self.orderfile_command.assert_not_called()
+    self.kernel_command.assert_not_called()
 
 
-class UploadVettedFDOArtifactsTest(
-    UpdateEbuildWithAFDOArtifactsTest):
+class UploadVettedFDOArtifactsTest(UpdateEbuildWithAFDOArtifactsTest):
   """Unittests for UploadVettedAFDOArtifacts."""
 
   @staticmethod
@@ -127,3 +140,10 @@
         build_target=self.board, artifact_type=toolchain_pb2.KERNEL_AFDO)
     toolchain.UploadVettedAFDOArtifacts(request, self.response, self.api_config)
     self.command.assert_called_once_with('kernel_afdo', self.board)
+
+  def testChromeAFDOSuccess(self):
+    """Test the command is called correctly with Chrome afdo."""
+    request = self._GetRequest(
+        build_target=self.board, artifact_type=toolchain_pb2.CHROME_AFDO)
+    toolchain.UploadVettedAFDOArtifacts(request, self.response, self.api_config)
+    self.command.assert_called_once_with('chrome_afdo', self.board)