1 // Copyright 2020 The Pigweed Authors
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
7 // https://www.apache.org/licenses/LICENSE-2.0
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
15 #include "pw_metric/metric_service_nanopb.h"
17 #include "gtest/gtest.h"
18 #include "pw_log/log.h"
19 #include "pw_rpc/nanopb_test_method_context.h"
21 namespace pw::metric {
24 #define MetricMethodContext \
25 PW_NANOPB_TEST_METHOD_CONTEXT( \
26 MetricService, Get, 4, sizeof(pw_metric_MetricResponse))
28 TEST(MetricService, EmptyGroupAndNoMetrics) {
30 PW_METRIC_GROUP(root, "/");
32 // Run the RPC and ensure it completes.
33 MetricMethodContext context(root.metrics(), root.children());
35 EXPECT_TRUE(context.done());
36 EXPECT_EQ(OkStatus(), context.status());
38 // No metrics should be in the response.
39 EXPECT_EQ(0u, context.responses().size());
42 TEST(MetricService, FlatMetricsNoGroupsOneResponseOnly) {
43 // Set up a one-group suite of metrics.
44 PW_METRIC_GROUP(root, "/");
45 PW_METRIC(root, a, "a", 1.0);
46 PW_METRIC(root, b, "b", 1.0);
47 PW_METRIC(root, c, "c", 1.0);
48 PW_METRIC(root, d, "d", 1.0);
49 PW_METRIC(root, e, "e", 1.0);
51 // Run the RPC and ensure it completes.
52 MetricMethodContext context(root.metrics(), root.children());
54 EXPECT_TRUE(context.done());
55 EXPECT_EQ(OkStatus(), context.status());
57 // All of the responses should have fit in one proto.
58 EXPECT_EQ(1u, context.responses().size());
59 EXPECT_EQ(5, context.responses()[0].metrics_count);
62 TEST(MetricService, NestedGroupsButOnlyOneBatch) {
63 // Set up a nested group of metrics that will fit in the default batch (10).
64 PW_METRIC_GROUP(root, "/");
65 PW_METRIC(root, a, "a", 1.0);
66 PW_METRIC(root, b, "b", 1.0);
67 PW_METRIC(root, c, "c", 1.0);
69 PW_METRIC_GROUP(inner, "inner");
70 PW_METRIC(inner, x, "x", 1.0);
71 PW_METRIC(inner, y, "y", 1.0);
72 PW_METRIC(inner, z, "z", 1.0);
76 // Run the RPC and ensure it completes.
77 MetricMethodContext context(root.metrics(), root.children());
79 EXPECT_TRUE(context.done());
80 EXPECT_EQ(OkStatus(), context.status());
82 // All of the responses should fit in one proto.
83 EXPECT_EQ(1u, context.responses().size());
84 EXPECT_EQ(6, context.responses()[0].metrics_count);
87 TEST(MetricService, NestedGroupsWithBatches) {
88 // Set up a nested group of metrics that will not fit in a single batch.
89 PW_METRIC_GROUP(root, "/");
90 PW_METRIC(root, a, "a", 1u);
91 PW_METRIC(root, d, "d", 2u);
92 PW_METRIC(root, f, "f", 3u);
94 PW_METRIC_GROUP(inner_1, "inner1");
95 PW_METRIC(inner_1, x, "x", 4u);
96 PW_METRIC(inner_1, y, "y", 5u);
97 PW_METRIC(inner_1, z, "z", 6u);
99 PW_METRIC_GROUP(inner_2, "inner2");
100 PW_METRIC(inner_2, p, "p", 7u);
101 PW_METRIC(inner_2, q, "q", 8u);
102 PW_METRIC(inner_2, r, "r", 9u);
103 PW_METRIC(inner_2, s, "s", 10u); // Note: Max # per response is 10.
104 PW_METRIC(inner_2, t, "s", 11u);
105 PW_METRIC(inner_2, u, "s", 12u);
110 // Run the RPC and ensure it completes.
111 MetricMethodContext context(root.metrics(), root.children());
113 EXPECT_TRUE(context.done());
114 EXPECT_EQ(OkStatus(), context.status());
116 // The response had to be split into two parts; check that they have the
117 // appropriate sizes.
118 EXPECT_EQ(2u, context.responses().size());
119 EXPECT_EQ(10, context.responses()[0].metrics_count);
120 EXPECT_EQ(2, context.responses()[1].metrics_count);
122 // The metrics are the numbers 1..12; sum them and compare.
123 uint32_t metric_sum = 0;
124 for (const auto& response : context.responses()) {
125 for (unsigned i = 0; i < response.metrics_count; ++i) {
126 metric_sum += response.metrics[i].value.as_int;
129 EXPECT_EQ(78u, metric_sum);
131 // TODO(keir): Properly check all the fields.
134 bool TokenPathsMatch(uint32_t expected_token_path[5],
135 const pw_metric_Metric& metric) {
136 // Calculate length of expected token & compare.
137 int expected_length = 0;
138 while (expected_token_path[expected_length]) {
141 if (expected_length != metric.token_path_count) {
145 // Lengths match; so search the tokens themselves.
146 for (int i = 0; i < expected_length; ++i) {
147 if (expected_token_path[i] != metric.token_path[i]) {
154 TEST(MetricService, TokenPaths) {
155 // Set up a nested group of metrics that will not fit in a single batch.
156 PW_METRIC_GROUP(root, "/");
157 PW_METRIC(root, a, "a", 1u);
159 PW_METRIC_GROUP(inner_1, "inner1");
160 PW_METRIC(inner_1, x, "x", 4u);
161 PW_METRIC(inner_1, z, "z", 6u);
163 PW_METRIC_GROUP(inner_2, "inner2");
164 PW_METRIC(inner_2, p, "p", 7u);
165 PW_METRIC(inner_2, u, "s", 12u);
170 // Run the RPC and ensure it completes.
171 MetricMethodContext context(root.metrics(), root.children());
173 EXPECT_TRUE(context.done());
174 EXPECT_EQ(OkStatus(), context.status());
176 // The metrics should fit in one batch.
177 EXPECT_EQ(1u, context.responses().size());
178 EXPECT_EQ(5, context.responses()[0].metrics_count);
180 // Declare the token paths we expect to find.
181 // Note: This depends on the token variables from the PW_METRIC*() macros.
182 uint32_t expected_token_paths[5][5] = {
184 {inner_1_token, x_token, 0u},
185 {inner_1_token, z_token, 0u},
186 {inner_2_token, p_token, 0u},
187 {inner_2_token, u_token, 0u},
190 // For each expected token, search through all returned metrics to find it.
191 // The search is necessary since there is no guarantee of metric ordering.
192 for (auto& expected_token_path : expected_token_paths) {
193 int found_matches = 0;
194 // Note: There should only be 1 response.
195 for (const auto& response : context.responses()) {
196 for (unsigned m = 0; m < response.metrics_count; ++m) {
197 if (TokenPathsMatch(expected_token_path, response.metrics[m])) {
202 EXPECT_EQ(found_matches, 1);
207 } // namespace pw::metric