Move BlackboxMappedMetricLogger to acts.metrics.loggers, and refactor BlackboxMetricLogger as a subclass of the former.

Bug: None
Test: local acts test, unit tests
Change-Id: I1f8f19432479b668b1bcc54cf69a29b5fee8600e
diff --git a/acts/framework/acts/metrics/loggers/blackbox.py b/acts/framework/acts/metrics/loggers/blackbox.py
index c615d61..8d7aeca 100644
--- a/acts/framework/acts/metrics/loggers/blackbox.py
+++ b/acts/framework/acts/metrics/loggers/blackbox.py
@@ -20,8 +20,9 @@
 from acts.metrics.logger import MetricLogger
 
 
-class BlackboxMetricLogger(MetricLogger):
-    """A MetricLogger for logging and publishing Blackbox metrics.
+class BlackboxMappedMetricLogger(MetricLogger):
+    """A MetricLogger for logging and publishing Blackbox metrics from a dict.
+    The dict maps the metric name to the metric value.
 
     The logger will publish an ActsBlackboxMetricResult message, containing
     data intended to be uploaded to Blackbox. The message itself contains only
@@ -34,15 +35,112 @@
 
     Attributes:
         proto_module: The proto module for ActsBlackboxMetricResult.
-        proto_dir: The directory in which the proto module is found.
+        metric_key: The metric key to use. If unset, the logger will use the
+                    context's identifier.
+        _metric_map: the map of metric_name -> metric_value to publish
+                to blackbox. If the metric value is set to None, the
+                metric will not be reported.
+    """
+
+    PROTO_FILE = 'protos/acts_blackbox.proto'
+
+    def __init__(self, metric_key=None, event=None, compiler_out=None):
+        """Initializes a logger for Blackbox metrics.
+
+        Args:
+            metric_key: The metric key to use. If unset, the logger will use
+                        the context's identifier.
+            event: The event triggering the creation of this logger.
+            compiler_out: The directory to store the compiled proto module.
+        """
+        super().__init__(event=event)
+        self.proto_module = self._compile_proto(self.PROTO_FILE,
+                                                compiler_out=compiler_out)
+        self.metric_key = metric_key
+        self._metric_map = {}
+
+    def _get_metric_key(self, metric_name):
+        """Gets the metric key to use.
+
+        If the metric_key is explicitly set, returns that value. Otherwise,
+        extracts an identifier from the context.
+
+        Args:
+            metric_name: The name of the metric to report.
+        """
+        if self.metric_key:
+            key = self.metric_key
+        else:
+            key = self._get_blackbox_identifier()
+        key = '%s.%s' % (key, metric_name)
+        return key
+
+    def set_metric_data(self, metric_map):
+        """Sets the map of metrics to be uploaded to Blackbox. Note that
+        this will overwrite all existing added by this function or add_metric.
+
+        Args:
+            metric_map: the map of metric_name -> metric_value to publish
+                to blackbox. If the metric value is set to None, the
+                metric will not be reported.
+        """
+        self._metric_map = metric_map
+
+    def add_metric(self, metric_name, metric_value):
+        """Adds a metric value to be published later.
+
+        Note that if the metric name has already been added, the metric value
+        will be overwritten.
+
+        Args:
+            metric_name: the name of the metric.
+            metric_value: the value of the metric.
+        """
+        self._metric_map[metric_name] = metric_value
+
+    def _get_blackbox_identifier(self):
+        """Returns the testcase identifier, as expected by Blackbox."""
+        # b/119787228: Blackbox requires function names to look like Java
+        # functions.
+        identifier = self.context.identifier
+        parts = identifier.rsplit('.', 1)
+        return '#'.join(parts)
+
+    def end(self, _):
+        """Creates and publishes a ProtoMetric with blackbox data.
+
+        Builds a list of ActsBlackboxMetricResult messages from the set
+        metric data, and sends them to the publisher.
+        """
+        metrics = []
+        for metric_name, metric_value in self._metric_map.items():
+            if metric_value is None:
+                continue
+            result = self.proto_module.ActsBlackboxMetricResult()
+            result.test_identifier = self._get_blackbox_identifier()
+            result.metric_key = self._get_metric_key(metric_name)
+            result.metric_value = metric_value
+
+            metrics.append(
+                ProtoMetric(name='blackbox_%s' % metric_name, data=result))
+
+        return self.publisher.publish(metrics)
+
+
+class BlackboxMetricLogger(BlackboxMappedMetricLogger):
+    """A MetricLogger for logging and publishing individual Blackbox metrics.
+
+    For additional information on reporting to Blackbox, see
+    BlackboxMappedMetricLogger.
+
+    Attributes:
+        proto_module: The proto module for ActsBlackboxMetricResult.
         metric_name: The name of the metric, used to determine output filename.
         metric_key: The metric key to use. If unset, the logger will use the
                     context's identifier.
         metric_value: The metric value.
     """
 
-    PROTO_FILE = 'protos/acts_blackbox.proto'
-
     def __init__(self, metric_name, metric_key=None, event=None,
                  compiler_out=None):
         """Initializes a logger for Blackbox metrics.
@@ -54,54 +152,17 @@
             event: The event triggering the creation of this logger.
             compiler_out: The directory to store the compiled proto module
         """
-        super().__init__(event=event)
-        self.proto_module = self._compile_proto(self.PROTO_FILE,
-                                                compiler_out=compiler_out)
+        super().__init__(metric_key=metric_key, event=event,
+                         compiler_out=compiler_out)
         if not metric_name:
             raise ValueError("metric_name must be supplied.")
         self.metric_name = metric_name
-        self.metric_key = metric_key
         self.metric_value = None
 
-    def _get_metric_key(self):
-        """Gets the metric key to use.
+    @property
+    def metric_value(self):
+        return self._metric_map[self.metric_name]
 
-        If the metric_key is explicitly set, returns that value. Otherwise,
-        extracts an identifier from the context.
-        """
-        if self.metric_key:
-            key = self.metric_key
-        else:
-            key = self._get_blackbox_identifier()
-        key = '%s.%s' % (key, self.metric_name)
-        return key
-
-    def _get_file_name(self):
-        """Gets the base file name to publish to."""
-        return 'blackbox_%s' % self.metric_name
-
-    def _get_blackbox_identifier(self):
-        """Returns the testcase identifier, as expected by Blackbox."""
-        # b/119787228: Blackbox requires function names to look like Java
-        # functions.
-        identifier = self.context.identifier
-        parts = identifier.rsplit('.', 1)
-        return '#'.join(parts)
-
-    def end(self, event):
-        """Creates and publishes a ProtoMetric with blackbox data.
-
-        Builds an ActsBlackboxMetricResult message based on the result
-        generated, and passes it off to the publisher.
-
-        Args:
-            event: The triggering event.
-        """
-        result = self.proto_module.ActsBlackboxMetricResult()
-        result.test_identifier = self._get_blackbox_identifier()
-        result.metric_key = self._get_metric_key()
-        if self.metric_value is not None:
-            result.metric_value = self.metric_value
-
-        metric = ProtoMetric(name=self._get_file_name(), data=result)
-        return self.publisher.publish(metric)
+    @metric_value.setter
+    def metric_value(self, value):
+        self.add_metric(self.metric_name, value)
diff --git a/acts/framework/acts/test_utils/wifi/wifi_performance_test_utils.py b/acts/framework/acts/test_utils/wifi/wifi_performance_test_utils.py
index 4d4f4f1..e2bcea9 100644
--- a/acts/framework/acts/test_utils/wifi/wifi_performance_test_utils.py
+++ b/acts/framework/acts/test_utils/wifi/wifi_performance_test_utils.py
@@ -187,102 +187,6 @@
             self.llstats_cumulative)
 
 
-# Dashboard utilities
-class BlackboxMappedMetricLogger(MetricLogger):
-    """A MetricLogger for logging and publishing Blackbox metrics from a dict.
-
-    The dict maps the metric name to the metric value. For additional
-    information on reporting to Blackbox, see BlackBoxMetricLogger.
-
-    Attributes:
-        proto_module: The proto module for ActsBlackboxMetricResult.
-        metric_key: The metric key to use. If unset, the logger will use the
-                    context's identifier.
-    """
-
-    PROTO_FILE = '../../metrics/loggers/protos/acts_blackbox.proto'
-
-    def __init__(self, metric_key=None, event=None):
-        """Initializes a logger for Blackbox metrics.
-
-        Args:
-            metric_key: The metric key to use. If unset, the logger will use
-                        the context's identifier.
-            event: The event triggering the creation of this logger.
-        """
-        super().__init__(event=event)
-        self.proto_module = self._compile_proto(self.PROTO_FILE)
-        self.metric_key = metric_key
-        self._metric_map = {}
-
-    def _get_metric_key(self, metric_name):
-        """Gets the metric key to use.
-
-        If the metric_key is explicitly set, returns that value. Otherwise,
-        extracts an identifier from the context.
-
-        Args:
-            metric_name: The name of the metric to report.
-        """
-        if self.metric_key:
-            key = self.metric_key
-        else:
-            key = self._get_blackbox_identifier()
-        key = '%s.%s' % (key, metric_name)
-        return key
-
-    def set_metric_data(self, metric_map):
-        """Sets the map of metrics to be uploaded to Blackbox. Note that
-        this will overwrite all existing added by this function or add_metric.
-
-        Args:
-            metric_map: the map of metric_name -> metric_value to publish
-                to blackbox. If the metric value is set to None, the
-                metric will not be reported.
-        """
-        self._metric_map = metric_map
-
-    def add_metric(self, metric_name, metric_value):
-        """Adds a metric value to be published later.
-
-        Note that if the metric name has already been added, the metric value
-        will be overwritten.
-
-        Args:
-            metric_name: the name of the metric.
-            metric_value: the value of the metric.
-        """
-        self._metric_map[metric_name] = metric_value
-
-    def _get_blackbox_identifier(self):
-        """Returns the testcase identifier, as expected by Blackbox."""
-        # b/119787228: Blackbox requires function names to look like Java
-        # functions.
-        identifier = self.context.identifier
-        parts = identifier.rsplit('.', 1)
-        return '#'.join(parts)
-
-    def end(self, _):
-        """Creates and publishes a ProtoMetric with blackbox data.
-
-        Builds a list of ActsBlackboxMetricResult messages from the set
-        metric data, and sends them to the publisher.
-        """
-        metrics = []
-        for metric_name, metric_value in self._metric_map.items():
-            if metric_value is None:
-                continue
-            result = self.proto_module.ActsBlackboxMetricResult()
-            result.test_identifier = self._get_blackbox_identifier()
-            result.metric_key = self._get_metric_key(metric_name)
-            result.metric_value = metric_value
-
-            metrics.append(
-                ProtoMetric(name='blackbox_%s' % metric_name, data=result))
-
-        return self.publisher.publish(metrics)
-
-
 # JSON serializer
 def serialize_dict(input_dict):
     """Function to serialize dicts to enable JSON output"""
diff --git a/acts/framework/tests/metrics/loggers/blackbox_test.py b/acts/framework/tests/metrics/loggers/blackbox_test.py
index aa0d535..d40b00d 100644
--- a/acts/framework/tests/metrics/loggers/blackbox_test.py
+++ b/acts/framework/tests/metrics/loggers/blackbox_test.py
@@ -122,6 +122,7 @@
         logger.context = self.context
         logger.publisher = self.publisher
         logger._get_blackbox_identifier = self._get_blackbox_identifier
+        logger.metric_value = 'foo'
 
         logger.end(self.event)
 
@@ -141,13 +142,14 @@
         logger.context = self.context
         logger.publisher = self.publisher
         logger._get_blackbox_identifier = self._get_blackbox_identifier
+        logger.metric_value = 'foo'
 
         logger.end(self.event)
 
         proto_metric_cls.assert_called_once_with(
             name=self.TEST_FILE_NAME, data=result)
         self.publisher.publish.assert_called_once_with(
-            proto_metric_cls.return_value)
+            [proto_metric_cls.return_value])
 
 
 class _BaseTestClassWithCleanup(BaseTestClass):
@@ -212,7 +214,7 @@
 
         args_list = publisher_cls().publish.call_args_list
         self.assertEqual(len(args_list), 1)
-        metric = self.__get_only_arg(args_list[0])
+        metric = self.__get_only_arg(args_list[0])[0]
         self.assertEqual(metric.name, 'blackbox_my_metric')
         self.assertEqual(metric.data.test_identifier, 'MyTest#test_case')
         self.assertEqual(metric.data.metric_key, 'MyTest#test_case.my_metric')
@@ -241,7 +243,7 @@
 
         args_list = publisher_cls().publish.call_args_list
         self.assertEqual(len(args_list), 2)
-        metrics = [self.__get_only_arg(args) for args in args_list]
+        metrics = [self.__get_only_arg(args)[0] for args in args_list]
         self.assertEqual({metric.name
                           for metric in metrics},
                          {'blackbox_my_metric_1', 'blackbox_my_metric_2'})
@@ -262,18 +264,18 @@
             def __init__(self, controllers):
                 super().__init__(controllers)
                 self.tests = ('test_case', )
-                BlackboxMetricLogger.for_test_case(
+                self.metrics = BlackboxMetricLogger.for_test_case(
                     'my_metric', metric_key='my_metric_key',
                     compiler_out=self.proto_dir)
 
             def test_case(self):
-                self.result = result
+                self.metrics.metric_value = result
 
         self.run_acts_test(MyTest)
 
         args_list = publisher_cls().publish.call_args_list
         self.assertEqual(len(args_list), 1)
-        metric = self.__get_only_arg(args_list[0])
+        metric = self.__get_only_arg(args_list[0])[0]
         self.assertEqual(metric.data.metric_key, 'my_metric_key.my_metric')
 
     @patch('acts.metrics.logger.ProtoMetricPublisher')
@@ -305,7 +307,7 @@
 
         args_list = publisher_cls().publish.call_args_list
         self.assertEqual(len(args_list), 1)
-        metric = self.__get_only_arg(args_list[0])
+        metric = self.__get_only_arg(args_list[0])[0]
         self.assertEqual(metric.data.metric_value, result_1 + result_2)
         self.assertEqual(metric.data.test_identifier, MyTest.__name__)
 
diff --git a/acts/tests/google/wifi/WifiPingTest.py b/acts/tests/google/wifi/WifiPingTest.py
index 659a756..8de6959 100644
--- a/acts/tests/google/wifi/WifiPingTest.py
+++ b/acts/tests/google/wifi/WifiPingTest.py
@@ -25,6 +25,7 @@
 from acts import base_test
 from acts import utils
 from acts.controllers.utils_lib import ssh
+from acts.metrics.loggers.blackbox import BlackboxMappedMetricLogger
 from acts.test_utils.wifi import ota_chamber
 from acts.test_utils.wifi import wifi_performance_test_utils as wputils
 from acts.test_utils.wifi import wifi_retail_ap as retail_ap
@@ -57,9 +58,9 @@
     def __init__(self, controllers):
         base_test.BaseTestClass.__init__(self, controllers)
         self.testcase_metric_logger = (
-            wputils.BlackboxMappedMetricLogger.for_test_case())
+            BlackboxMappedMetricLogger.for_test_case())
         self.testclass_metric_logger = (
-            wputils.BlackboxMappedMetricLogger.for_test_class())
+            BlackboxMappedMetricLogger.for_test_class())
         self.publish_testcase_metrics = True
 
         self.tests = self.generate_test_cases(
@@ -567,9 +568,9 @@
     def __init__(self, controllers):
         base_test.BaseTestClass.__init__(self, controllers)
         self.testcase_metric_logger = (
-            wputils.BlackboxMappedMetricLogger.for_test_case())
+            BlackboxMappedMetricLogger.for_test_case())
         self.testclass_metric_logger = (
-            wputils.BlackboxMappedMetricLogger.for_test_class())
+            BlackboxMappedMetricLogger.for_test_class())
         self.publish_testcase_metrics = False
 
     def setup_class(self):
diff --git a/acts/tests/google/wifi/WifiRssiTest.py b/acts/tests/google/wifi/WifiRssiTest.py
index a8e8890..ed74d96 100644
--- a/acts/tests/google/wifi/WifiRssiTest.py
+++ b/acts/tests/google/wifi/WifiRssiTest.py
@@ -28,6 +28,7 @@
 from acts import utils
 from acts.controllers.utils_lib import ssh
 from acts.controllers import iperf_server as ipf
+from acts.metrics.loggers.blackbox import BlackboxMappedMetricLogger
 from acts.test_utils.wifi import ota_chamber
 from acts.test_utils.wifi import wifi_performance_test_utils as wputils
 from acts.test_utils.wifi import wifi_retail_ap as retail_ap
@@ -55,9 +56,9 @@
     def __init__(self, controllers):
         base_test.BaseTestClass.__init__(self, controllers)
         self.testcase_metric_logger = (
-            wputils.BlackboxMappedMetricLogger.for_test_case())
+            BlackboxMappedMetricLogger.for_test_case())
         self.testclass_metric_logger = (
-            wputils.BlackboxMappedMetricLogger.for_test_class())
+            BlackboxMappedMetricLogger.for_test_class())
         self.publish_test_metrics = True
 
     def setup_class(self):
@@ -881,9 +882,9 @@
     def __init__(self, controllers):
         base_test.BaseTestClass.__init__(self, controllers)
         self.testcase_metric_logger = (
-            wputils.BlackboxMappedMetricLogger.for_test_case())
+            BlackboxMappedMetricLogger.for_test_case())
         self.testclass_metric_logger = (
-            wputils.BlackboxMappedMetricLogger.for_test_class())
+            BlackboxMappedMetricLogger.for_test_class())
         self.publish_test_metrics = False
 
     def setup_class(self):
diff --git a/acts/tests/google/wifi/WifiRvrTest.py b/acts/tests/google/wifi/WifiRvrTest.py
index 00ffbfc..fa008d0 100644
--- a/acts/tests/google/wifi/WifiRvrTest.py
+++ b/acts/tests/google/wifi/WifiRvrTest.py
@@ -25,6 +25,7 @@
 from acts import utils
 from acts.controllers import iperf_server as ipf
 from acts.controllers.utils_lib import ssh
+from acts.metrics.loggers.blackbox import BlackboxMappedMetricLogger
 from acts.test_utils.wifi import ota_chamber
 from acts.test_utils.wifi import wifi_performance_test_utils as wputils
 from acts.test_utils.wifi import wifi_retail_ap as retail_ap
@@ -50,9 +51,9 @@
     def __init__(self, controllers):
         base_test.BaseTestClass.__init__(self, controllers)
         self.testcase_metric_logger = (
-            wputils.BlackboxMappedMetricLogger.for_test_case())
+            BlackboxMappedMetricLogger.for_test_case())
         self.testclass_metric_logger = (
-            wputils.BlackboxMappedMetricLogger.for_test_class())
+            BlackboxMappedMetricLogger.for_test_class())
         self.publish_testcase_metrics = True
 
     def setup_class(self):
@@ -692,9 +693,9 @@
     def __init__(self, controllers):
         base_test.BaseTestClass.__init__(self, controllers)
         self.testcase_metric_logger = (
-            wputils.BlackboxMappedMetricLogger.for_test_case())
+            BlackboxMappedMetricLogger.for_test_case())
         self.testclass_metric_logger = (
-            wputils.BlackboxMappedMetricLogger.for_test_class())
+            BlackboxMappedMetricLogger.for_test_class())
         self.publish_testcase_metrics = False
 
     def setup_class(self):
diff --git a/acts/tests/google/wifi/WifiSensitivityTest.py b/acts/tests/google/wifi/WifiSensitivityTest.py
index 9d6ba59..a64fe58 100644
--- a/acts/tests/google/wifi/WifiSensitivityTest.py
+++ b/acts/tests/google/wifi/WifiSensitivityTest.py
@@ -27,6 +27,7 @@
 from acts import utils
 from acts.controllers import iperf_client
 from acts.controllers.utils_lib import ssh
+from acts.metrics.loggers.blackbox import BlackboxMappedMetricLogger
 from acts.test_utils.wifi import ota_chamber
 from acts.test_utils.wifi import wifi_performance_test_utils as wputils
 from acts.test_utils.wifi import wifi_test_utils as wutils
@@ -126,9 +127,9 @@
     def __init__(self, controllers):
         base_test.BaseTestClass.__init__(self, controllers)
         self.testcase_metric_logger = (
-            wputils.BlackboxMappedMetricLogger.for_test_case())
+            BlackboxMappedMetricLogger.for_test_case())
         self.testclass_metric_logger = (
-            wputils.BlackboxMappedMetricLogger.for_test_class())
+            BlackboxMappedMetricLogger.for_test_class())
         self.publish_testcase_metrics = True
 
     def setup_class(self):
@@ -623,9 +624,9 @@
     def __init__(self, controllers):
         base_test.BaseTestClass.__init__(self, controllers)
         self.testcase_metric_logger = (
-            wputils.BlackboxMappedMetricLogger.for_test_case())
+            BlackboxMappedMetricLogger.for_test_case())
         self.testclass_metric_logger = (
-            wputils.BlackboxMappedMetricLogger.for_test_class())
+            BlackboxMappedMetricLogger.for_test_class())
         self.publish_testcase_metrics = False
 
     def setup_class(self):
diff --git a/acts/tests/google/wifi/WifiSoftApPerformanceTest.py b/acts/tests/google/wifi/WifiSoftApPerformanceTest.py
index f764b9f..a31812f 100644
--- a/acts/tests/google/wifi/WifiSoftApPerformanceTest.py
+++ b/acts/tests/google/wifi/WifiSoftApPerformanceTest.py
@@ -21,7 +21,7 @@
 from acts import utils
 from acts.controllers import iperf_server as ipf
 from acts.controllers import iperf_client as ipc
-from acts.metrics.loggers.blackbox import BlackboxMetricLogger
+from acts.metrics.loggers.blackbox import BlackboxMappedMetricLogger
 from acts.test_utils.wifi import wifi_test_utils as wutils
 from acts.test_utils.wifi import wifi_performance_test_utils as wputils
 from WifiRvrTest import WifiRvrTest
@@ -36,9 +36,9 @@
         self.tests = ("test_rvr_TCP_DL_2GHz", "test_rvr_TCP_UL_2GHz",
                       "test_rvr_TCP_DL_5GHz", "test_rvr_TCP_UL_5GHz")
         self.testcase_metric_logger = (
-            wputils.BlackboxMappedMetricLogger.for_test_case())
+            BlackboxMappedMetricLogger.for_test_case())
         self.testclass_metric_logger = (
-            wputils.BlackboxMappedMetricLogger.for_test_class())
+            BlackboxMappedMetricLogger.for_test_class())
         self.publish_testcase_metrics = True
 
     def setup_class(self):
diff --git a/acts/tests/google/wifi/WifiThroughputStabilityTest.py b/acts/tests/google/wifi/WifiThroughputStabilityTest.py
index ce6a18d..df2b6e1 100644
--- a/acts/tests/google/wifi/WifiThroughputStabilityTest.py
+++ b/acts/tests/google/wifi/WifiThroughputStabilityTest.py
@@ -27,6 +27,7 @@
 from acts import utils
 from acts.controllers import iperf_server as ipf
 from acts.controllers.utils_lib import ssh
+from acts.metrics.loggers.blackbox import BlackboxMappedMetricLogger
 from acts.test_utils.wifi import ota_chamber
 from acts.test_utils.wifi import wifi_performance_test_utils as wputils
 from acts.test_utils.wifi import wifi_retail_ap as retail_ap
@@ -52,9 +53,9 @@
         base_test.BaseTestClass.__init__(self, controllers)
         # Define metrics to be uploaded to BlackBox
         self.testcase_metric_logger = (
-            wputils.BlackboxMappedMetricLogger.for_test_case())
+            BlackboxMappedMetricLogger.for_test_case())
         self.testclass_metric_logger = (
-            wputils.BlackboxMappedMetricLogger.for_test_class())
+            BlackboxMappedMetricLogger.for_test_class())
         self.publish_testcase_metrics = True
         # Generate test cases
         self.tests = self.generate_test_cases(
@@ -456,9 +457,9 @@
         base_test.BaseTestClass.__init__(self, controllers)
         # Define metrics to be uploaded to BlackBox
         self.testcase_metric_logger = (
-            wputils.BlackboxMappedMetricLogger.for_test_case())
+            BlackboxMappedMetricLogger.for_test_case())
         self.testclass_metric_logger = (
-            wputils.BlackboxMappedMetricLogger.for_test_class())
+            BlackboxMappedMetricLogger.for_test_class())
         self.publish_testcase_metrics = False
 
     def setup_class(self):