xref: /aosp_15_r20/external/pigweed/pw_metric/metric_test.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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.h"
16 
17 #include "pw_log/log.h"
18 #include "pw_unit_test/framework.h"
19 
20 namespace pw::metric {
21 namespace {
22 
TEST(Metric,FloatFromObject)23 TEST(Metric, FloatFromObject) {
24   // Note leading bit is 1; it is stripped from the name to store the type.
25   Token token = 0xf1223344;
26 
27   TypedMetric<float> m(token, 1.5f);
28   EXPECT_EQ(m.name(), 0x71223344u);
29   EXPECT_TRUE(m.is_float());
30   EXPECT_FALSE(m.is_int());
31   EXPECT_EQ(m.value(), 1.5f);
32 
33   m.Set(55.1f);
34   EXPECT_EQ(m.value(), 55.1f);
35 
36   // No increment operation for float.
37 }
38 
TEST(Metric,IntFromObject)39 TEST(Metric, IntFromObject) {
40   // Note leading bit is 1; it is stripped from the name to store the type.
41   Token token = 0xf1223344;
42 
43   TypedMetric<uint32_t> m(token, static_cast<uint32_t>(31337u));
44   EXPECT_EQ(m.name(), 0x71223344u);
45   EXPECT_TRUE(m.is_int());
46   EXPECT_FALSE(m.is_float());
47   EXPECT_EQ(m.value(), 31337u);
48 
49   m.Set(414u);
50   EXPECT_EQ(m.value(), 414u);
51 
52   m.Increment();
53   EXPECT_EQ(m.value(), 415u);
54 
55   m.Increment(11u);
56   EXPECT_EQ(m.value(), 426u);
57 }
58 
TEST(Metric,IntFromMacroLocal)59 TEST(Metric, IntFromMacroLocal) {
60   PW_METRIC(m, "some_metric", 14u);
61   EXPECT_TRUE(m.is_int());
62   EXPECT_EQ(m.value(), 14u);
63 }
64 
TEST(Metric,FloatFromMacroLocal)65 TEST(Metric, FloatFromMacroLocal) {
66   PW_METRIC(m, "some_metric", 3.14f);
67   EXPECT_TRUE(m.is_float());
68   EXPECT_EQ(m.value(), 3.14f);
69 }
70 
TEST(Metric,ExposedToken)71 TEST(Metric, ExposedToken) {
72   PW_METRIC(m, "some_metric", 0u);
73   static constexpr Token kToken = PW_METRIC_TOKEN("some_metric");
74   EXPECT_EQ(m.name(), kToken);
75 }
76 
TEST(Metric,GroupMacroInFunctionContext)77 TEST(Metric, GroupMacroInFunctionContext) {
78   PW_METRIC_GROUP(group, "fancy_subsystem");
79   PW_METRIC(group, x, "x", 5555u);
80   PW_METRIC(group, y, "y", 6.0f);
81 
82   // These calls are needed to satisfy GCC, otherwise GCC warns about an unused
83   // variable (even though it is used and passed to the group, which adds it):
84   //
85   //   metric_test.cc:72:20: error: variable 'x' set but not used
86   //   [-Werror=unused-but-set-variable]
87   //
88   x.Increment(10);
89   y.Set(5.0f);
90 
91   group.Dump();
92   EXPECT_EQ(group.metrics().size(), 2u);
93 }
94 
95 // The below are compile tests to ensure the macros work at global scope.
96 
97 // Case 1: No group specified.
98 PW_METRIC(global_x, "global_x", 5555u);
99 PW_METRIC(global_y, "global_y", 6.0f);
100 
101 // Case 2: Group specified.
102 PW_METRIC_GROUP(global_group, "a_global_group");
103 PW_METRIC(global_group, global_z, "global_x", 5555u);
104 PW_METRIC(global_group, global_w, "global_y", 6.0f);
105 
TEST(Metric,GlobalScope)106 TEST(Metric, GlobalScope) {
107   EXPECT_EQ(global_x.value(), 5555u);
108   EXPECT_EQ(global_y.value(), 6.0f);
109 }
110 
111 // A fake object to illustrate the API and show nesting metrics.
112 // This also tests creating metrics as members inside a class.
113 class I2cBus {
114  public:
Transaction()115   void Transaction() {
116     // An entirely unconvincing fake I2C transaction implementation.
117     transactions_.Increment();
118     bytes_sent_.Increment(5);
119   }
120 
stats()121   Group& stats() { return metrics_; }
122 
123  private:
124   // Test a group with metrics in it, as a class member.
125   // Note that in many cases, the group would be passed in externally instead.
126   PW_METRIC_GROUP(metrics_, "i2c");
127   PW_METRIC(metrics_, bus_errors_, "bus_errors", 0u);
128   PW_METRIC(metrics_, transactions_, "transactions", 0u);
129   PW_METRIC(metrics_, bytes_sent_, "bytes_sent", 0u);
130 
131   // Test metrics without a group, as a class member.
132   PW_METRIC(a, "a", 0u);
133   PW_METRIC(b, "b", 10.0f);
134   PW_METRIC(c, "c", 525u);
135 };
136 
137 class Gyro {
138  public:
Gyro(I2cBus & i2c_bus,Group & parent_metrics)139   Gyro(I2cBus& i2c_bus, Group& parent_metrics) : i2c_bus_(i2c_bus) {
140     // Make the gyro a child of the I2C bus. Note that the other arrangement,
141     // where the i2c bus is a child of the gyro, doesn't work if there are
142     // multiple objects on the I2C bus due to the intrusive list mechanism.
143     parent_metrics.Add(metrics_);
144   }
145 
Init()146   void Init() {
147     i2c_bus_.Transaction();
148     initialized_.Increment();
149   }
150 
ReadAngularVelocity()151   void ReadAngularVelocity() {
152     // Pretend to be doing some transactions and pulling angular velocity.
153     // Pretend this gyro is inefficient and requires multiple transactions.
154     i2c_bus_.Transaction();
155     i2c_bus_.Transaction();
156     i2c_bus_.Transaction();
157     num_samples_.Increment();
158   }
159 
stats()160   Group& stats() { return metrics_; }
161 
162  private:
163   I2cBus& i2c_bus_;
164 
165   // In this case, "gyro" groups the relevant metrics, but it is possible to
166   // have freestanding metrics directly without a group; however, those
167   // free-standing metrics must be added to a group or list supplied elsewhere
168   // for collection.
169   PW_METRIC_GROUP(metrics_, "gyro");
170   PW_METRIC(metrics_, num_samples_, "num_samples", 1u);
171   PW_METRIC(metrics_, init_time_us_, "init_time_us", 1.0f);
172   PW_METRIC(metrics_, initialized_, "initialized", 0u);
173 };
174 
175 // The below test produces output like:
176 //
177 //   "$6doqFw==": {
178 //     "$05OCZw==": {
179 //       "$VpPfzg==": 1,
180 //       "$LGPMBQ==": 1.000000,
181 //       "$+iJvUg==": 5,
182 //     }
183 //     "$9hPNxw==": 65,
184 //     "$oK7HmA==": 13,
185 //     "$FCM4qQ==": 0,
186 //   }
187 //
188 // Note the metric names are tokenized with base64. Decoding requires using the
189 // Pigweed detokenizer. With a detokenizing-enabled logger, you would get:
190 //
191 //   "i2c": {
192 //     "gyro": {
193 //       "num_sampleses": 1,
194 //       "init_time_us": 1.000000,
195 //       "initialized": 5,
196 //     }
197 //     "bus_errors": 65,
198 //     "transactions": 13,
199 //     "bytes_sent": 0,
200 //   }
201 //
TEST(Metric,InlineConstructionWithGroups)202 TEST(Metric, InlineConstructionWithGroups) {
203   I2cBus i2c_bus;
204   Gyro gyro(i2c_bus, i2c_bus.stats());
205 
206   gyro.Init();
207   gyro.ReadAngularVelocity();
208   gyro.ReadAngularVelocity();
209   gyro.ReadAngularVelocity();
210   gyro.ReadAngularVelocity();
211 
212   // This "test" doesn't really test anything, and more illustrates how to the
213   // metrics could be instantiated in an object tree.
214   //
215   // Unfortunatlely, testing dump is difficult since we don't have log
216   // redirection for tests.
217   i2c_bus.stats().Dump();
218 }
219 
220 // PW_METRIC_STATIC doesn't support class scopes, since a definition must be
221 // provided outside of the class body.
222 // TODO(keir): add support for class scopes and enable this test
223 #if 0
224 class MetricTest: public ::testing::Test {
225   public:
226     void Increment() {
227       metric_.Increment();
228     }
229 
230   private:
231     PW_METRIC_STATIC(metric_, "metric", 0u);
232 };
233 
234 TEST_F(MetricTest, StaticWithinAClass) {
235   Increment();
236 }
237 #endif
238 
StaticMetricIncrement()239 Metric* StaticMetricIncrement() {
240   PW_METRIC_STATIC(metric, "metric", 0u);
241   metric.Increment();
242   return &metric;
243 }
244 
TEST(Metric,StaticWithinAFunction)245 TEST(Metric, StaticWithinAFunction) {
246   Metric* metric = StaticMetricIncrement();
247   EXPECT_EQ(metric->as_int(), 1u);
248   StaticMetricIncrement();
249   EXPECT_EQ(metric->as_int(), 2u);
250 }
251 
252 }  // namespace
253 }  // namespace pw::metric
254