Implement GetCoverageRules endpoint.

- Writes inputs in the format the testplan tool expects, calls
the tool, and then parses outputs into the response.

BUG=b:182898188
TEST=./run_tests

Change-Id: If60a50069befada443e7369686044e9135faaf88
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/chromite/+/3057796
Reviewed-by: Alex Klein <saklein@chromium.org>
Reviewed-by: Jaques Clapauch <jaquesc@google.com>
Reviewed-by: Sean McAllister <smcallis@google.com>
Commit-Queue: Andrew Lamb <andrewlamb@chromium.org>
Tested-by: Andrew Lamb <andrewlamb@chromium.org>
diff --git a/api/controller/test.py b/api/controller/test.py
index 2f47f4c..0417add 100644
--- a/api/controller/test.py
+++ b/api/controller/test.py
@@ -16,6 +16,9 @@
 from chromite.api.controller import controller_util
 from chromite.api.gen.chromite.api import test_pb2
 from chromite.api.gen.chromiumos import common_pb2
+from chromite.api.gen.chromiumos.test.api import coverage_rule_pb2
+from chromite.api.gen.chromiumos.test.api import dut_attribute_pb2
+from chromite.api.gen.chromiumos.test.api import test_suite_pb2
 from chromite.cbuildbot import goma_util
 from chromite.lib import build_target_lib
 from chromite.lib import chroot_lib
@@ -27,6 +30,8 @@
 from chromite.lib.parser import package_info
 from chromite.scripts import cros_set_lsb_release
 from chromite.service import test
+from chromite.third_party.google.protobuf import json_format
+from chromite.third_party.google.protobuf import text_format
 from chromite.utils import key_value_store
 from chromite.utils import metrics
 
@@ -361,3 +366,78 @@
           })
 
   return generated
+
+
+def _GetCoverageRulesResponseSuccess(
+    _input_proto, output_proto: test_pb2.GetCoverageRulesResponse, _config):
+  output_proto.coverage_rules.append(
+      coverage_rule_pb2.CoverageRule(
+          name='kernel:4.4',
+          test_suites=[
+              test_suite_pb2.TestSuite(
+                  test_case_tag_criteria=test_suite_pb2.TestSuite
+                  .TestCaseTagCriteria(tags=['kernel']))
+          ],
+          dut_criteria=[
+              dut_attribute_pb2.DutCriterion(
+                  attribute_id=dut_attribute_pb2.DutAttribute.Id(
+                      value='system_build_target'),
+                  values=['overlayA'],
+              )
+          ],
+      ),)
+
+
+@faux.success(_GetCoverageRulesResponseSuccess)
+@faux.empty_error
+@validate.require('source_test_plans', 'dut_attribute_list',
+                  'build_metadata_list', 'flat_config_list')
+@validate.validation_complete
+def GetCoverageRules(input_proto: test_pb2.GetCoverageRulesRequest,
+                     output_proto: test_pb2.GetCoverageRulesResponse, _config):
+  """Call the testplan tool to generate CoverageRules."""
+  source_test_plans = input_proto.source_test_plans
+  dut_attributes_list = input_proto.dut_attribute_list
+  build_metadata_list = input_proto.build_metadata_list
+  flat_config_list = input_proto.flat_config_list
+
+  cmd = ['testplan', 'generate']
+
+  with osutils.TempDir(prefix='get_coverage_rules_input') as tempdir:
+    # Write all input files required by testplan, and read the output file
+    # containing CoverageRules.
+    for i, plan in enumerate(source_test_plans):
+      plan_path = os.path.join(tempdir, 'source_test_plan_%d.textpb' % i)
+      osutils.WriteFile(plan_path, text_format.MessageToString(plan))
+      cmd.extend(['-plan', plan_path])
+
+    dut_attribute_path = os.path.join(tempdir, 'dut_attribute_list.jsonpb')
+    osutils.WriteFile(dut_attribute_path,
+                      json_format.MessageToJson(dut_attributes_list))
+    cmd.extend(['-dutattributes', dut_attribute_path])
+
+    build_metadata_path = os.path.join(tempdir, 'build_metadata_list.jsonpb')
+    osutils.WriteFile(build_metadata_path,
+                      json_format.MessageToJson(build_metadata_list))
+    cmd.extend(['-buildmetadata', build_metadata_path])
+
+    flat_config_path = os.path.join(tempdir, 'flat_config_list.jsonpb')
+    osutils.WriteFile(flat_config_path,
+                      json_format.MessageToJson(flat_config_list))
+    cmd.extend(['-flatconfiglist', flat_config_path])
+
+    out_path = os.path.join(tempdir, 'out.jsonpb')
+    cmd.extend(['-out', out_path])
+
+    cros_build_lib.run(cmd)
+
+    out_text = osutils.ReadFile(out_path)
+
+  # The output file contains CoverageRules as jsonpb, separated by newlines.
+  coverage_rules = []
+  for out_line in out_text.splitlines():
+    coverage_rule = coverage_rule_pb2.CoverageRule()
+    json_format.Parse(out_line, coverage_rule)
+    coverage_rules.append(coverage_rule)
+
+  output_proto.coverage_rules.extend(coverage_rules)