xref: /aosp_15_r20/external/pigweed/pw_metric/metric_service_pwpb_test.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2022 The Pigweed Authors
2 //
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
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
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
13 // the License.
14 
15 #include "pw_metric/metric_service_pwpb.h"
16 
17 #include "pw_log/log.h"
18 #include "pw_metric_proto/metric_service.pwpb.h"
19 #include "pw_protobuf/decoder.h"
20 #include "pw_rpc/pwpb/test_method_context.h"
21 #include "pw_rpc/raw/test_method_context.h"
22 #include "pw_span/span.h"
23 #include "pw_unit_test/framework.h"
24 
25 namespace pw::metric {
26 namespace {
27 
28 #define MetricMethodContext \
29   PW_PWPB_TEST_METHOD_CONTEXT(MetricService, Get, 4, 256)
30 
CountEncodedMetrics(ConstByteSpan serialized_path)31 size_t CountEncodedMetrics(ConstByteSpan serialized_path) {
32   protobuf::Decoder decoder(serialized_path);
33   size_t num_metrics = 0;
34   while (decoder.Next().ok()) {
35     switch (decoder.FieldNumber()) {
36       case static_cast<uint32_t>(
37           pw::metric::proto::pwpb::MetricResponse::Fields::kMetrics): {
38         num_metrics++;
39       }
40     }
41   }
42   return num_metrics;
43 }
44 
SumMetricInts(ConstByteSpan serialized_path)45 size_t SumMetricInts(ConstByteSpan serialized_path) {
46   protobuf::Decoder decoder(serialized_path);
47   size_t metrics_sum = 0;
48   while (decoder.Next().ok()) {
49     switch (decoder.FieldNumber()) {
50       case static_cast<uint32_t>(
51           pw::metric::proto::pwpb::Metric::Fields::kAsInt): {
52         uint32_t metric_value;
53         EXPECT_EQ(OkStatus(), decoder.ReadUint32(&metric_value));
54         metrics_sum += metric_value;
55       }
56     }
57   }
58   return metrics_sum;
59 }
60 
GetMetricsSum(ConstByteSpan serialized_metric_buffer)61 size_t GetMetricsSum(ConstByteSpan serialized_metric_buffer) {
62   protobuf::Decoder decoder(serialized_metric_buffer);
63   size_t metrics_sum = 0;
64   while (decoder.Next().ok()) {
65     switch (decoder.FieldNumber()) {
66       case static_cast<uint32_t>(
67           pw::metric::proto::pwpb::MetricResponse::Fields::kMetrics): {
68         ConstByteSpan metric_buffer;
69         EXPECT_EQ(OkStatus(), decoder.ReadBytes(&metric_buffer));
70         metrics_sum += SumMetricInts(metric_buffer);
71       }
72     }
73   }
74   return metrics_sum;
75 }
76 
TEST(MetricService,EmptyGroupAndNoMetrics)77 TEST(MetricService, EmptyGroupAndNoMetrics) {
78   // Empty root group.
79   PW_METRIC_GROUP(root, "/");
80 
81   // Run the RPC and ensure it completes.
82 
83   PW_RAW_TEST_METHOD_CONTEXT(MetricService, Get)
84   ctx{root.metrics(), root.children()};
85   ctx.call({});
86   EXPECT_TRUE(ctx.done());
87   EXPECT_EQ(OkStatus(), ctx.status());
88 
89   // No metrics should be in the response.
90   EXPECT_EQ(0u, ctx.responses().size());
91 }
92 
TEST(MetricService,OneGroupOneMetric)93 TEST(MetricService, OneGroupOneMetric) {
94   // One root group with one metric.
95   PW_METRIC_GROUP(root, "/");
96   PW_METRIC(root, a, "a", 3u);
97 
98   // Run the RPC and ensure it completes.
99 
100   PW_RAW_TEST_METHOD_CONTEXT(MetricService, Get)
101   ctx{root.metrics(), root.children()};
102   ctx.call({});
103   EXPECT_TRUE(ctx.done());
104   EXPECT_EQ(OkStatus(), ctx.status());
105 
106   // One metric should be in the response.
107   EXPECT_EQ(1u, ctx.responses().size());
108 
109   // Sum should be 3.
110   EXPECT_EQ(3u, GetMetricsSum(ctx.responses()[0]));
111 }
112 
TEST(MetricService,OneGroupFiveMetrics)113 TEST(MetricService, OneGroupFiveMetrics) {
114   // One root group with five metrics.
115   PW_METRIC_GROUP(root, "/");
116   PW_METRIC(root, a, "a", 1u);
117   PW_METRIC(root, b, "b", 2u);  // Note: Max # per response is 3.
118   PW_METRIC(root, c, "c", 3u);
119   PW_METRIC(root, x, "x", 4u);
120   PW_METRIC(root, y, "y", 5u);
121 
122   // Run the RPC and ensure it completes.
123 
124   PW_RAW_TEST_METHOD_CONTEXT(MetricService, Get)
125   ctx{root.metrics(), root.children()};
126   ctx.call({});
127   EXPECT_TRUE(ctx.done());
128   EXPECT_EQ(OkStatus(), ctx.status());
129 
130   // Two metrics should be in the response.
131   EXPECT_EQ(2u, ctx.responses().size());
132   EXPECT_EQ(3u, CountEncodedMetrics(ctx.responses()[0]));
133   EXPECT_EQ(2u, CountEncodedMetrics(ctx.responses()[1]));
134 
135   // The metrics are the numbers 1..5; sum them and compare.
136   EXPECT_EQ(
137       15u,
138       GetMetricsSum(ctx.responses()[0]) + GetMetricsSum(ctx.responses()[1]));
139 }
140 
TEST(MetricService,NestedGroupFiveMetrics)141 TEST(MetricService, NestedGroupFiveMetrics) {
142   // Set up a nested group of metrics.
143   PW_METRIC_GROUP(root, "/");
144   PW_METRIC(root, a, "a", 1u);
145   PW_METRIC(root, b, "b", 2u);
146 
147   PW_METRIC_GROUP(inner, "inner");
148   PW_METRIC(root, x, "x", 3u);  // Note: Max # per response is 3.
149   PW_METRIC(inner, y, "y", 4u);
150   PW_METRIC(inner, z, "z", 5u);
151 
152   root.Add(inner);
153 
154   // Run the RPC and ensure it completes.
155 
156   PW_RAW_TEST_METHOD_CONTEXT(MetricService, Get)
157   ctx{root.metrics(), root.children()};
158   ctx.call({});
159   EXPECT_TRUE(ctx.done());
160   EXPECT_EQ(OkStatus(), ctx.status());
161 
162   // Two metrics should be in the response.
163   EXPECT_EQ(2u, ctx.responses().size());
164   EXPECT_EQ(3u, CountEncodedMetrics(ctx.responses()[0]));
165   EXPECT_EQ(2u, CountEncodedMetrics(ctx.responses()[1]));
166 
167   EXPECT_EQ(
168       15u,
169       GetMetricsSum(ctx.responses()[0]) + GetMetricsSum(ctx.responses()[1]));
170 }
171 
TEST(MetricService,NestedGroupsWithBatches)172 TEST(MetricService, NestedGroupsWithBatches) {
173   // Set up a nested group of metrics that will not fit in a single batch.
174   PW_METRIC_GROUP(root, "/");
175   PW_METRIC(root, a, "a", 1u);
176   PW_METRIC(root, d, "d", 2u);
177   PW_METRIC(root, f, "f", 3u);
178 
179   PW_METRIC_GROUP(inner_1, "inner1");
180   PW_METRIC(inner_1, x, "x", 4u);
181   PW_METRIC(inner_1, y, "y", 5u);
182   PW_METRIC(inner_1, z, "z", 6u);
183 
184   PW_METRIC_GROUP(inner_2, "inner2");
185   PW_METRIC(inner_2, p, "p", 7u);
186   PW_METRIC(inner_2, q, "q", 8u);
187   PW_METRIC(inner_2, r, "r", 9u);
188   PW_METRIC(inner_2, s, "s", 10u);  // Note: Max # per response is 3.
189   PW_METRIC(inner_2, t, "t", 11u);
190   PW_METRIC(inner_2, u, "u", 12u);
191 
192   root.Add(inner_1);
193   root.Add(inner_2);
194 
195   // Run the RPC and ensure it completes.
196   PW_RAW_TEST_METHOD_CONTEXT(MetricService, Get)
197   ctx{root.metrics(), root.children()};
198   ctx.call({});
199   EXPECT_TRUE(ctx.done());
200   EXPECT_EQ(OkStatus(), ctx.status());
201 
202   // The response had to be split into four parts; check that they have the
203   // appropriate sizes.
204   EXPECT_EQ(4u, ctx.responses().size());
205   EXPECT_EQ(3u, CountEncodedMetrics(ctx.responses()[0]));
206   EXPECT_EQ(3u, CountEncodedMetrics(ctx.responses()[1]));
207   EXPECT_EQ(3u, CountEncodedMetrics(ctx.responses()[2]));
208   EXPECT_EQ(3u, CountEncodedMetrics(ctx.responses()[3]));
209 
210   EXPECT_EQ(78u,
211             GetMetricsSum(ctx.responses()[0]) +
212                 GetMetricsSum(ctx.responses()[1]) +
213                 GetMetricsSum(ctx.responses()[2]) +
214                 GetMetricsSum(ctx.responses()[3]));
215 }
216 
TEST(MetricService,MaxDepth4)217 TEST(MetricService, MaxDepth4) {
218   // MetricWalker internally uses: Vector<Token, /*capacity=*/4> path_;
219   // pw.metric.proto.Metric.token_path max_count:4
220 
221   IntrusiveList<Group> global_groups;    // Simulate pw::metric::global_groups
222   IntrusiveList<Metric> global_metrics;  // Simulate pw::metric::global_metrics
223 
224   PW_METRIC_GROUP(global_group_lvl1, "level1");
225   global_groups.push_back(global_group_lvl1);
226 
227   PW_METRIC_GROUP(global_group_lvl1, group_lvl2, "level2");
228   PW_METRIC_GROUP(group_lvl2, group_lvl3, "level3");
229 
230   // Note: kMaxNumPackedEntries = 3
231   PW_METRIC(group_lvl3, metric_a, "metric A", 1u);
232   PW_METRIC(group_lvl3, metric_b, "metric B", 2u);
233   PW_METRIC(group_lvl3, metric_c, "metric C", 3u);
234 
235   // Run the RPC and ensure it completes.
236   PW_RAW_TEST_METHOD_CONTEXT(MetricService, Get)
237   ctx{global_metrics, global_groups};
238   ctx.call({});
239   EXPECT_TRUE(ctx.done());
240   EXPECT_EQ(OkStatus(), ctx.status());
241 
242   // Verify the response
243   EXPECT_EQ(1u, ctx.responses().size());
244   EXPECT_EQ(3u, CountEncodedMetrics(ctx.responses()[0]));
245   EXPECT_EQ(6u, GetMetricsSum(ctx.responses()[0]));
246 }
247 
248 }  // namespace
249 }  // namespace pw::metric
250