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