• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2#
3#   Copyright 2016 - The Android Open Source Project
4#
5#   Licensed under the Apache License, Version 2.0 (the "License");
6#   you may not use this file except in compliance with the License.
7#   You may obtain a copy of the License at
8#
9#       http://www.apache.org/licenses/LICENSE-2.0
10#
11#   Unless required by applicable law or agreed to in writing, software
12#   distributed under the License is distributed on an "AS IS" BASIS,
13#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14#   See the License for the specific language governing permissions and
15#   limitations under the License.
16
17from acts.libs.proto.proto_utils import md5_proto
18from acts.metrics.core import ProtoMetric
19from acts.metrics.logger import MetricLogger
20from acts.metrics.loggers.protos.gen import acts_blackbox_pb2
21
22
23class BlackboxMappedMetricLogger(MetricLogger):
24    """A MetricLogger for logging and publishing Blackbox metrics from a dict.
25    The dict maps the metric name to the metric value.
26
27    The logger will publish an ActsBlackboxMetricResult message, containing
28    data intended to be uploaded to Blackbox. The message itself contains only
29    minimal information specific to the metric, with the intention being that
30    all other metadata is extracted from the test_summary.yaml.
31
32    This logger will extract an attribute from the test class as the metric
33    result. The metric key will be either the context's identifier or a custom
34    value assigned to this class.
35
36    Attributes:
37        metric_key: The metric key to use. If unset, the logger will use the
38                    context's identifier.
39        _metric_map: the map of metric_name -> metric_value to publish
40                to blackbox. If the metric value is set to None, the
41                metric will not be reported.
42    """
43
44    def __init__(self, metric_key=None, event=None):
45        """Initializes a logger for Blackbox metrics.
46
47        Args:
48            metric_key: The metric key to use. If unset, the logger will use
49                        the context's identifier.
50            event: The event triggering the creation of this logger.
51        """
52        super().__init__(event=event)
53        self.metric_key = metric_key
54        self._metric_map = {}
55
56    def _get_metric_key(self, metric_name):
57        """Gets the metric key to use.
58
59        If the metric_key is explicitly set, returns that value. Otherwise,
60        extracts an identifier from the context.
61
62        Args:
63            metric_name: The name of the metric to report.
64        """
65        if self.metric_key:
66            key = self.metric_key
67        else:
68            key = self._get_blackbox_identifier()
69        key = '%s.%s' % (key, metric_name)
70        return key
71
72    def set_metric_data(self, metric_map):
73        """Sets the map of metrics to be uploaded to Blackbox. Note that
74        this will overwrite all existing added by this function or add_metric.
75
76        Args:
77            metric_map: the map of metric_name -> metric_value to publish
78                to blackbox. If the metric value is set to None, the
79                metric will not be reported.
80        """
81        self._metric_map = metric_map
82
83    def add_metric(self, metric_name, metric_value):
84        """Adds a metric value to be published later.
85
86        Note that if the metric name has already been added, the metric value
87        will be overwritten.
88
89        Args:
90            metric_name: the name of the metric.
91            metric_value: the value of the metric.
92        """
93        self._metric_map[metric_name] = metric_value
94
95    def _get_blackbox_identifier(self):
96        """Returns the testcase identifier, as expected by Blackbox."""
97        # b/119787228: Blackbox requires function names to look like Java
98        # functions.
99        identifier = self.context.identifier
100        parts = identifier.rsplit('.', 1)
101        return '#'.join(parts)
102
103    def end(self, _):
104        """Creates and publishes a ProtoMetric with blackbox data.
105
106        Builds a list of ActsBlackboxMetricResult messages from the set
107        metric data, and sends them to the publisher.
108        """
109        bundle = acts_blackbox_pb2.ActsBlackboxMetricResultsBundle()
110        for metric_name, metric_value in self._metric_map.items():
111            if metric_value is None:
112                continue
113            result = acts_blackbox_pb2.ActsBlackboxMetricResult()
114            result.test_identifier = self._get_blackbox_identifier()
115            result.metric_key = self._get_metric_key(metric_name)
116            result.metric_value = metric_value
117            bundle.acts_blackbox_metric_results.append(result)
118
119        # Since there could technically be more than one concurrent logger
120        # instance we add a hash for files to not override each other. We use a
121        # static hash for repeatability.
122        bundle_name = 'blackbox_metrics_bundle.' + md5_proto(bundle)[0:8]
123        return self.publisher.publish(
124            [ProtoMetric(name=bundle_name, data=bundle)])
125
126
127class BlackboxMetricLogger(BlackboxMappedMetricLogger):
128    """A MetricLogger for logging and publishing individual Blackbox metrics.
129
130    For additional information on reporting to Blackbox, see
131    BlackboxMappedMetricLogger.
132
133    Attributes:
134        metric_name: The name of the metric, used to determine output filename.
135        metric_key: The metric key to use. If unset, the logger will use the
136                    context's identifier.
137        metric_value: The metric value.
138    """
139
140    def __init__(self, metric_name, metric_key=None, event=None):
141        """Initializes a logger for Blackbox metrics.
142
143        Args:
144            metric_name: The name of the metric.
145            metric_key: The metric key to use. If unset, the logger will use
146                        the context's identifier.
147            event: The event triggering the creation of this logger.
148        """
149        super().__init__(metric_key=metric_key, event=event)
150        if not metric_name:
151            raise ValueError("metric_name must be supplied.")
152        self.metric_name = metric_name
153        self.metric_value = None
154
155    @property
156    def metric_value(self):
157        return self._metric_map[self.metric_name]
158
159    @metric_value.setter
160    def metric_value(self, value):
161        self.add_metric(self.metric_name, value)
162