• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 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_nanopb.h"
16 
17 #include "gtest/gtest.h"
18 #include "pw_log/log.h"
19 #include "pw_rpc/nanopb_test_method_context.h"
20 
21 namespace pw::metric {
22 namespace {
23 
24 #define MetricMethodContext      \
25   PW_NANOPB_TEST_METHOD_CONTEXT( \
26       MetricService, Get, 4, sizeof(pw_metric_MetricResponse))
27 
TEST(MetricService,EmptyGroupAndNoMetrics)28 TEST(MetricService, EmptyGroupAndNoMetrics) {
29   // Empty root group.
30   PW_METRIC_GROUP(root, "/");
31 
32   // Run the RPC and ensure it completes.
33   MetricMethodContext context(root.metrics(), root.children());
34   context.call({});
35   EXPECT_TRUE(context.done());
36   EXPECT_EQ(OkStatus(), context.status());
37 
38   // No metrics should be in the response.
39   EXPECT_EQ(0u, context.responses().size());
40 }
41 
TEST(MetricService,FlatMetricsNoGroupsOneResponseOnly)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);
50 
51   // Run the RPC and ensure it completes.
52   MetricMethodContext context(root.metrics(), root.children());
53   context.call({});
54   EXPECT_TRUE(context.done());
55   EXPECT_EQ(OkStatus(), context.status());
56 
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);
60 }
61 
TEST(MetricService,NestedGroupsButOnlyOneBatch)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);
68 
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);
73 
74   root.Add(inner);
75 
76   // Run the RPC and ensure it completes.
77   MetricMethodContext context(root.metrics(), root.children());
78   context.call({});
79   EXPECT_TRUE(context.done());
80   EXPECT_EQ(OkStatus(), context.status());
81 
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);
85 }
86 
TEST(MetricService,NestedGroupsWithBatches)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);
93 
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);
98 
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);
106 
107   root.Add(inner_1);
108   root.Add(inner_2);
109 
110   // Run the RPC and ensure it completes.
111   MetricMethodContext context(root.metrics(), root.children());
112   context.call({});
113   EXPECT_TRUE(context.done());
114   EXPECT_EQ(OkStatus(), context.status());
115 
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);
121 
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;
127     }
128   }
129   EXPECT_EQ(78u, metric_sum);
130 
131   // TODO(keir): Properly check all the fields.
132 }
133 
TokenPathsMatch(uint32_t expected_token_path[5],const pw_metric_Metric & metric)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]) {
139     expected_length++;
140   }
141   if (expected_length != metric.token_path_count) {
142     return false;
143   }
144 
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]) {
148       return false;
149     }
150   }
151   return true;
152 }
153 
TEST(MetricService,TokenPaths)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);
158 
159   PW_METRIC_GROUP(inner_1, "inner1");
160   PW_METRIC(inner_1, x, "x", 4u);
161   PW_METRIC(inner_1, z, "z", 6u);
162 
163   PW_METRIC_GROUP(inner_2, "inner2");
164   PW_METRIC(inner_2, p, "p", 7u);
165   PW_METRIC(inner_2, u, "s", 12u);
166 
167   root.Add(inner_1);
168   root.Add(inner_2);
169 
170   // Run the RPC and ensure it completes.
171   MetricMethodContext context(root.metrics(), root.children());
172   context.call({});
173   EXPECT_TRUE(context.done());
174   EXPECT_EQ(OkStatus(), context.status());
175 
176   // The metrics should fit in one batch.
177   EXPECT_EQ(1u, context.responses().size());
178   EXPECT_EQ(5, context.responses()[0].metrics_count);
179 
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] = {
183       {a_token, 0u},
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},
188   };
189 
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])) {
198           found_matches++;
199         }
200       }
201     }
202     EXPECT_EQ(found_matches, 1);
203   }
204 }
205 
206 }  // namespace
207 }  // namespace pw::metric
208