| # /usr/bin/env python3 |
| # |
| # Copyright (C) 2018 The Android Open Source Project |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| # use this file except in compliance with the License. You may obtain a copy of |
| # the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| # License for the specific language governing permissions and limitations under |
| # the License. |
| |
| import base64 |
| from google.protobuf import message |
| import time |
| |
| from acts.metrics.core import ProtoMetric |
| from acts.metrics.logger import MetricLogger |
| from acts_contrib.test_utils.bt.loggers.protos import bluetooth_metric_pb2 |
| |
| |
| def recursive_assign(proto, dct): |
| """Assign values in dct to proto recursively.""" |
| for metric in dir(proto): |
| if metric in dct: |
| if (isinstance(dct[metric], dict) |
| and isinstance(getattr(proto, metric), message.Message)): |
| recursive_assign(getattr(proto, metric), dct[metric]) |
| else: |
| setattr(proto, metric, dct[metric]) |
| |
| |
| class BluetoothMetricLogger(MetricLogger): |
| """A logger for gathering Bluetooth test metrics |
| |
| Attributes: |
| proto_module: Module used to store Bluetooth metrics in a proto |
| results: Stores ProtoMetrics to be published for each logger context |
| proto_map: Maps test case names to the appropriate protos for each case |
| """ |
| |
| def __init__(self, event): |
| super().__init__(event=event) |
| self.proto_module = bluetooth_metric_pb2 |
| self.results = [] |
| self.start_time = int(time.time()) |
| |
| self.proto_map = { |
| 'BluetoothPairAndConnectTest': |
| self.proto_module.BluetoothPairAndConnectTestResult(), |
| 'BluetoothReconnectTest': |
| self.proto_module.BluetoothReconnectTestResult(), |
| 'BluetoothThroughputTest': |
| self.proto_module.BluetoothDataTestResult(), |
| 'BluetoothLatencyTest': |
| self.proto_module.BluetoothDataTestResult(), |
| 'BtCodecSweepTest': |
| self.proto_module.BluetoothAudioTestResult(), |
| 'BtRangeCodecTest': |
| self.proto_module.BluetoothAudioTestResult(), |
| } |
| |
| @staticmethod |
| def get_configuration_data(device): |
| """Gets the configuration data of a device. |
| |
| Gets the configuration data of a device and organizes it in a |
| dictionary. |
| |
| Args: |
| device: The device object to get the configuration data from. |
| |
| Returns: |
| A dictionary containing configuration data of a device. |
| """ |
| # TODO(b/126931820): Genericize and move to lib when generic DUT interface is implemented |
| data = {'device_class': device.__class__.__name__} |
| |
| if device.__class__.__name__ == 'AndroidDevice': |
| # TODO(b/124066126): Add remaining config data |
| data = { |
| 'device_class': 'phone', |
| 'device_model': device.model, |
| 'android_release_id': device.build_info['build_id'], |
| 'android_build_type': device.build_info['build_type'], |
| 'android_build_number': |
| device.build_info['incremental_build_id'], |
| 'android_branch_name': 'git_qt-release', |
| 'software_version': device.build_info['build_id'] |
| } |
| |
| if device.__class__.__name__ == 'ParentDevice': |
| data = { |
| 'device_class': 'headset', |
| 'device_model': device.dut_type, |
| 'software_version': device.get_version()[1]['Fw Build Label'], |
| 'android_build_number': device.version |
| } |
| |
| return data |
| |
| def add_config_data_to_proto(self, proto, pri_device, conn_device=None): |
| """Add to configuration data field of proto. |
| |
| Adds test start time and device configuration info. |
| Args: |
| proto: protobuf to add configuration data to. |
| pri_device: some controller object. |
| conn_device: optional second controller object. |
| """ |
| pri_device_proto = proto.configuration_data.primary_device |
| conn_device_proto = proto.configuration_data.connected_device |
| proto.configuration_data.test_date_time = self.start_time |
| |
| pri_config = self.get_configuration_data(pri_device) |
| |
| for metric in dir(pri_device_proto): |
| if metric in pri_config: |
| setattr(pri_device_proto, metric, pri_config[metric]) |
| |
| if conn_device: |
| conn_config = self.get_configuration_data(conn_device) |
| |
| for metric in dir(conn_device_proto): |
| if metric in conn_config: |
| setattr(conn_device_proto, metric, conn_config[metric]) |
| |
| def get_proto_dict(self, test, proto): |
| """Return dict with proto, readable ascii proto, and test name.""" |
| return { |
| 'proto': |
| base64.b64encode(ProtoMetric(test, |
| proto).get_binary()).decode('utf-8'), |
| 'proto_ascii': |
| ProtoMetric(test, proto).get_ascii(), |
| 'test_name': |
| test |
| } |
| |
| def add_proto_to_results(self, proto, test): |
| """Adds proto as ProtoMetric object to self.results.""" |
| self.results.append(ProtoMetric(test, proto)) |
| |
| def get_results(self, results, test, pri_device, conn_device=None): |
| """Gets the metrics associated with each test case. |
| |
| Gets the test case metrics and configuration data for each test case and |
| stores them for publishing. |
| |
| Args: |
| results: A dictionary containing test metrics. |
| test: The name of the test case associated with these results. |
| pri_device: The primary AndroidDevice object for the test. |
| conn_device: The connected AndroidDevice object for the test, if |
| applicable. |
| |
| """ |
| |
| proto_result = self.proto_map[test] |
| recursive_assign(proto_result, results) |
| self.add_config_data_to_proto(proto_result, pri_device, conn_device) |
| self.add_proto_to_results(proto_result, test) |
| return self.get_proto_dict(test, proto_result) |
| |
| def end(self, event): |
| return self.publisher.publish(self.results) |