aboutsummaryrefslogtreecommitdiff
path: root/pw_metric/py/metric_parser_test.py
diff options
context:
space:
mode:
authorXin Li <delphij@google.com>2023-08-14 15:38:30 -0700
committerXin Li <delphij@google.com>2023-08-14 15:38:30 -0700
commitbddf63953e111d742b591c1c0c7c34bcda8a51c7 (patch)
tree3a93128bff4b737b24b0c9581922c0b20410f0f4 /pw_metric/py/metric_parser_test.py
parentee890da55c82b95deca3518d5f3777e3d8ca9f0e (diff)
parentfbb9890f8922aa55fde183655a0017e69127ea4b (diff)
downloadpigweed-bddf63953e111d742b591c1c0c7c34bcda8a51c7.tar.gz
Merge Android U (ab/10368041)tmp_amf_298295554
Bug: 291102124 Merged-In: I10c41adb8fe3e126cfa4ff2f49b15863fff379de Change-Id: I66f7a6cccaafc173d3924dae62a736c6c53520c7
Diffstat (limited to 'pw_metric/py/metric_parser_test.py')
-rw-r--r--pw_metric/py/metric_parser_test.py297
1 files changed, 297 insertions, 0 deletions
diff --git a/pw_metric/py/metric_parser_test.py b/pw_metric/py/metric_parser_test.py
new file mode 100644
index 000000000..3b4800f7c
--- /dev/null
+++ b/pw_metric/py/metric_parser_test.py
@@ -0,0 +1,297 @@
+#!/usr/bin/env python3
+# Copyright 2022 The Pigweed Authors
+#
+# 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
+#
+# https://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.
+"""Tests for retreiving and parsing metrics."""
+from unittest import TestCase, mock, main
+from pw_metric.metric_parser import parse_metrics
+
+from pw_metric_proto import metric_service_pb2
+from pw_status import Status
+from pw_tokenizer import detokenize, tokens
+
+DATABASE = tokens.Database(
+ [
+ tokens.TokenizedStringEntry(0x01148A48, "total_dropped"),
+ tokens.TokenizedStringEntry(0x03796798, "min_queue_remaining"),
+ tokens.TokenizedStringEntry(0x22198280, "total_created"),
+ tokens.TokenizedStringEntry(0x534A42F4, "max_queue_used"),
+ tokens.TokenizedStringEntry(0x5D087463, "pw::work_queue::WorkQueue"),
+ tokens.TokenizedStringEntry(0xA7C43965, "log"),
+ ]
+)
+
+
+class TestParseMetrics(TestCase):
+ """Test parsing metrics received from RPCs"""
+
+ def setUp(self) -> None:
+ """Creating detokenizer and mocking RPC."""
+ self.detokenize = detokenize.Detokenizer(DATABASE)
+ self.rpc_timeout_s = 1
+ self.rpcs = mock.Mock()
+ self.rpcs.pw = mock.Mock()
+ self.rpcs.pw.metric = mock.Mock()
+ self.rpcs.pw.metric.proto = mock.Mock()
+ self.rpcs.pw.metric.proto.MetricService = mock.Mock()
+ self.rpcs.pw.metric.proto.MetricService.Get = mock.Mock()
+ self.rpcs.pw.metric.proto.MetricService.Get.return_value = mock.Mock()
+ self.rpcs.pw.metric.proto.MetricService.Get.return_value.status = (
+ Status.OK
+ )
+ # Creating a group and metric name for better identification.
+ self.log = 0xA7C43965
+ self.total_created = 0x22198280
+ self.total_dropped = 0x01148A48
+ self.min_queue_remaining = 0x03796798
+ self.metric = [
+ metric_service_pb2.Metric(
+ token_path=[self.log, self.total_created],
+ string_path='N/A',
+ as_float=3.0,
+ ),
+ metric_service_pb2.Metric(
+ token_path=[self.log, self.total_dropped],
+ string_path='N/A',
+ as_float=4.0,
+ ),
+ ]
+
+ def test_invalid_detokenizer(self) -> None:
+ """Test invalid detokenizer was supplied."""
+ self.assertEqual(
+ {},
+ parse_metrics(self.rpcs, None, self.rpc_timeout_s),
+ msg='Valid detokenizer.',
+ )
+
+ def test_bad_stream_status(self) -> None:
+ """Test stream response has a status other than OK."""
+ self.rpcs.pw.metric.proto.MetricService.Get.return_value.status = (
+ Status.ABORTED
+ )
+ self.assertEqual(
+ {},
+ parse_metrics(self.rpcs, self.detokenize, self.rpc_timeout_s),
+ msg='Stream response was not aborted.',
+ )
+
+ def test_parse_metrics(self) -> None:
+ """Test metrics being parsed and recorded."""
+ # Loading metric into RPC.
+ self.rpcs.pw.metric.proto.MetricService.Get.return_value.responses = [
+ metric_service_pb2.MetricResponse(metrics=self.metric)
+ ]
+ self.assertEqual(
+ {
+ 'log': {
+ 'total_created': 3.0,
+ 'total_dropped': 4.0,
+ }
+ },
+ parse_metrics(self.rpcs, self.detokenize, self.rpc_timeout_s),
+ msg='Metrics are not equal.',
+ )
+
+ def test_three_metric_names(self) -> None:
+ """Test creating a dictionary with three paths."""
+ # Creating another leaf.
+ self.metric.append(
+ metric_service_pb2.Metric(
+ token_path=[self.log, self.min_queue_remaining],
+ string_path='N/A',
+ as_float=1.0,
+ )
+ )
+ self.rpcs.pw.metric.proto.MetricService.Get.return_value.responses = [
+ metric_service_pb2.MetricResponse(metrics=self.metric)
+ ]
+ self.assertEqual(
+ {
+ 'log': {
+ 'total_created': 3.0,
+ 'total_dropped': 4.0,
+ 'min_queue_remaining': 1.0,
+ },
+ },
+ parse_metrics(self.rpcs, self.detokenize, self.rpc_timeout_s),
+ msg='Metrics are not equal.',
+ )
+
+ def test_inserting_unknown_token(self) -> None:
+ # Inserting an unknown token as a group name.
+ self.metric.append(
+ metric_service_pb2.Metric(
+ token_path=[0x007, self.total_dropped],
+ string_path='N/A',
+ as_float=1.0,
+ )
+ )
+ self.rpcs.pw.metric.proto.MetricService.Get.return_value.responses = [
+ metric_service_pb2.MetricResponse(metrics=self.metric)
+ ]
+ self.assertEqual(
+ {
+ 'log': {
+ 'total_created': 3.0,
+ 'total_dropped': 4.0,
+ },
+ '$': {'total_dropped': 1.0},
+ },
+ parse_metrics(self.rpcs, self.detokenize, self.rpc_timeout_s),
+ msg='Metrics are not equal.',
+ )
+
+ def test_multiple_metric_response(self) -> None:
+ """Tests multiple metric responses being handled."""
+ # Adding more than one MetricResponses.
+ metric = [
+ metric_service_pb2.Metric(
+ token_path=[0x007, self.total_dropped],
+ string_path='N/A',
+ as_float=1.0,
+ )
+ ]
+ self.rpcs.pw.metric.proto.MetricService.Get.return_value.responses = [
+ metric_service_pb2.MetricResponse(metrics=self.metric),
+ metric_service_pb2.MetricResponse(metrics=metric),
+ ]
+ self.assertEqual(
+ {
+ 'log': {
+ 'total_created': 3.0,
+ 'total_dropped': 4.0,
+ },
+ '$': {
+ 'total_dropped': 1.0,
+ },
+ },
+ parse_metrics(self.rpcs, self.detokenize, self.rpc_timeout_s),
+ msg='Metrics are not equal.',
+ )
+
+ def test_paths_longer_than_two(self) -> None:
+ """Tests metric paths longer than two."""
+ # Path longer than two.
+ longest_metric = [
+ metric_service_pb2.Metric(
+ token_path=[
+ self.log,
+ self.total_created,
+ self.min_queue_remaining,
+ ],
+ string_path='N/A',
+ as_float=1.0,
+ ),
+ ]
+ self.rpcs.pw.metric.proto.MetricService.Get.return_value.responses = [
+ metric_service_pb2.MetricResponse(metrics=longest_metric),
+ ]
+ self.assertEqual(
+ {
+ 'log': {
+ 'total_created': {'min_queue_remaining': 1.0},
+ }
+ },
+ parse_metrics(self.rpcs, self.detokenize, self.rpc_timeout_s),
+ msg='Metrics are not equal.',
+ )
+ # Create a new leaf in log.
+ longest_metric.append(
+ metric_service_pb2.Metric(
+ token_path=[self.log, self.total_dropped],
+ string_path='N/A',
+ as_float=3.0,
+ )
+ )
+ metric = [
+ metric_service_pb2.Metric(
+ token_path=[0x007, self.total_dropped],
+ string_path='N/A',
+ as_float=1.0,
+ ),
+ metric_service_pb2.Metric(
+ token_path=[0x007, self.total_created],
+ string_path='N/A',
+ as_float=2.0,
+ ),
+ ]
+ self.rpcs.pw.metric.proto.MetricService.Get.return_value.responses = [
+ metric_service_pb2.MetricResponse(metrics=longest_metric),
+ metric_service_pb2.MetricResponse(metrics=metric),
+ ]
+ self.assertEqual(
+ {
+ 'log': {
+ 'total_created': {
+ 'min_queue_remaining': 1.0,
+ },
+ 'total_dropped': 3.0,
+ },
+ '$': {
+ 'total_dropped': 1.0,
+ 'total_created': 2.0,
+ },
+ },
+ parse_metrics(self.rpcs, self.detokenize, self.rpc_timeout_s),
+ msg='Metrics are not equal.',
+ )
+
+ def test_conflicting_keys(self) -> None:
+ """Tests conflicting key and value assignment."""
+ longest_metric = [
+ metric_service_pb2.Metric(
+ token_path=[
+ self.log,
+ self.total_created,
+ self.min_queue_remaining,
+ ],
+ string_path='N/A',
+ as_float=1.0,
+ ),
+ ]
+ # Creates a conflict at log/total_created, should throw an error.
+ self.rpcs.pw.metric.proto.MetricService.Get.return_value.responses = [
+ metric_service_pb2.MetricResponse(metrics=longest_metric),
+ metric_service_pb2.MetricResponse(metrics=self.metric),
+ ]
+ parse_metrics(self.rpcs, self.detokenize, self.rpc_timeout_s)
+ self.assertRaises(ValueError, msg='Expected Value Error.')
+
+ def test_conflicting_logs(self) -> None:
+ """Tests conflicting loga being streamed."""
+ longest_metric = [
+ metric_service_pb2.Metric(
+ token_path=[self.log, self.total_created],
+ string_path='N/A',
+ as_float=1.0,
+ ),
+ ]
+ # Creates a duplicate metric for log/total_created.
+ self.rpcs.pw.metric.proto.MetricService.Get.return_value.responses = [
+ metric_service_pb2.MetricResponse(metrics=longest_metric),
+ metric_service_pb2.MetricResponse(metrics=self.metric),
+ ]
+ parse_metrics(self.rpcs, self.detokenize, self.rpc_timeout_s)
+ self.assertRaises(ValueError, msg='Expected Value Error.')
+ # Duplicate metrics being loaded.
+ self.rpcs.pw.metric.proto.MetricService.Get.return_value.responses = [
+ metric_service_pb2.MetricResponse(metrics=self.metric),
+ metric_service_pb2.MetricResponse(metrics=self.metric),
+ ]
+ parse_metrics(self.rpcs, self.detokenize, self.rpc_timeout_s)
+ self.assertRaises(ValueError, msg='Expected Value Error.')
+
+
+if __name__ == '__main__':
+ main()