You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
241 lines
6.6 KiB
241 lines
6.6 KiB
// Copyright 2020 The Pigweed Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
|
// use this file except in compliance with the License. You may obtain a copy of
|
|
// the License at
|
|
//
|
|
// https://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
// License for the specific language governing permissions and limitations under
|
|
// the License.
|
|
|
|
#include "pw_metric/metric.h"
|
|
|
|
#include "gtest/gtest.h"
|
|
#include "pw_log/log.h"
|
|
|
|
namespace pw::metric {
|
|
|
|
TEST(Metric, FloatFromObject) {
|
|
// Note leading bit is 1; it is stripped from the name to store the type.
|
|
Token token = 0xf1223344;
|
|
|
|
TypedMetric<float> m(token, 1.5f);
|
|
EXPECT_EQ(m.name(), 0x71223344u);
|
|
EXPECT_TRUE(m.is_float());
|
|
EXPECT_FALSE(m.is_int());
|
|
EXPECT_EQ(m.value(), 1.5f);
|
|
|
|
m.Set(55.1f);
|
|
EXPECT_EQ(m.value(), 55.1f);
|
|
|
|
// No increment operation for float.
|
|
}
|
|
|
|
TEST(Metric, IntFromObject) {
|
|
// Note leading bit is 1; it is stripped from the name to store the type.
|
|
Token token = 0xf1223344;
|
|
|
|
TypedMetric<uint32_t> m(token, static_cast<uint32_t>(31337u));
|
|
EXPECT_EQ(m.name(), 0x71223344u);
|
|
EXPECT_TRUE(m.is_int());
|
|
EXPECT_FALSE(m.is_float());
|
|
EXPECT_EQ(m.value(), 31337u);
|
|
|
|
m.Set(414u);
|
|
EXPECT_EQ(m.value(), 414u);
|
|
|
|
m.Increment();
|
|
EXPECT_EQ(m.value(), 415u);
|
|
|
|
m.Increment(11u);
|
|
EXPECT_EQ(m.value(), 426u);
|
|
}
|
|
|
|
TEST(m, IntFromMacroLocal) {
|
|
PW_METRIC(m, "some_metric", 14u);
|
|
EXPECT_TRUE(m.is_int());
|
|
EXPECT_EQ(m.value(), 14u);
|
|
}
|
|
|
|
TEST(Metric, FloatFromMacroLocal) {
|
|
PW_METRIC(m, "some_metric", 3.14f);
|
|
EXPECT_TRUE(m.is_float());
|
|
EXPECT_EQ(m.value(), 3.14f);
|
|
}
|
|
|
|
TEST(Metric, GroupMacroInFunctionContext) {
|
|
PW_METRIC_GROUP(group, "fancy_subsystem");
|
|
PW_METRIC(group, x, "x", 5555u);
|
|
PW_METRIC(group, y, "y", 6.0f);
|
|
|
|
// These calls are needed to satisfy GCC, otherwise GCC warns about an unused
|
|
// variable (even though it is used and passed to the group, which adds it):
|
|
//
|
|
// metric_test.cc:72:20: error: variable 'x' set but not used
|
|
// [-Werror=unused-but-set-variable]
|
|
//
|
|
x.Increment(10);
|
|
y.Set(5.0f);
|
|
|
|
group.Dump();
|
|
EXPECT_EQ(group.metrics().size(), 2u);
|
|
}
|
|
|
|
// The below are compile tests to ensure the macros work at global scope.
|
|
|
|
// Case 1: No group specified.
|
|
PW_METRIC(global_x, "global_x", 5555u);
|
|
PW_METRIC(global_y, "global_y", 6.0f);
|
|
|
|
// Case 2: Group specified.
|
|
PW_METRIC_GROUP(global_group, "a_global_group");
|
|
PW_METRIC(global_group, global_z, "global_x", 5555u);
|
|
PW_METRIC(global_group, global_w, "global_y", 6.0f);
|
|
|
|
// A fake object to illustrate the API and show nesting metrics.
|
|
// This also tests creating metrics as members inside a class.
|
|
class I2cBus {
|
|
public:
|
|
void Transaction() {
|
|
// An entirely unconvincing fake I2C transaction implementation.
|
|
transactions_.Increment();
|
|
bytes_sent_.Increment(5);
|
|
}
|
|
|
|
Group& stats() { return metrics_; }
|
|
|
|
private:
|
|
// Test a group with metrics in it, as a class member.
|
|
// Note that in many cases, the group would be passed in externally instead.
|
|
PW_METRIC_GROUP(metrics_, "i2c");
|
|
PW_METRIC(metrics_, bus_errors_, "bus_errors", 0u);
|
|
PW_METRIC(metrics_, transactions_, "transactions", 0u);
|
|
PW_METRIC(metrics_, bytes_sent_, "bytes_sent", 0u);
|
|
|
|
// Test metrics without a group, as a class member.
|
|
PW_METRIC(a, "a", 0u);
|
|
PW_METRIC(b, "b", 10.0f);
|
|
PW_METRIC(c, "c", 525u);
|
|
};
|
|
|
|
class Gyro {
|
|
public:
|
|
Gyro(I2cBus& i2c_bus, Group& parent_metrics) : i2c_bus_(i2c_bus) {
|
|
// Make the gyro a child of the I2C bus. Note that the other arrangement,
|
|
// where the i2c bus is a child of the gyro, doesn't work if there are
|
|
// multiple objects on the I2C bus due to the intrusive list mechanism.
|
|
parent_metrics.Add(metrics_);
|
|
}
|
|
|
|
void Init() {
|
|
i2c_bus_.Transaction();
|
|
initialized_.Increment();
|
|
}
|
|
|
|
void ReadAngularVelocity() {
|
|
// Pretend to be doing some transactions and pulling angular velocity.
|
|
// Pretend this gyro is inefficient and requires multiple transactions.
|
|
i2c_bus_.Transaction();
|
|
i2c_bus_.Transaction();
|
|
i2c_bus_.Transaction();
|
|
num_samples_.Increment();
|
|
}
|
|
|
|
Group& stats() { return metrics_; }
|
|
|
|
private:
|
|
I2cBus& i2c_bus_;
|
|
|
|
// In this case, "gyro" groups the relevant metrics, but it is possible to
|
|
// have freestanding metrics directly without a group; however, those
|
|
// free-standing metrics must be added to a group or list supplied elsewhere
|
|
// for collection.
|
|
PW_METRIC_GROUP(metrics_, "gyro");
|
|
PW_METRIC(metrics_, num_samples_, "num_samples", 1u);
|
|
PW_METRIC(metrics_, init_time_us_, "init_time_us", 1.0f);
|
|
PW_METRIC(metrics_, initialized_, "initialized", 0u);
|
|
};
|
|
|
|
// The below test produces output like:
|
|
//
|
|
// "$6doqFw==": {
|
|
// "$05OCZw==": {
|
|
// "$VpPfzg==": 1,
|
|
// "$LGPMBQ==": 1.000000,
|
|
// "$+iJvUg==": 5,
|
|
// }
|
|
// "$9hPNxw==": 65,
|
|
// "$oK7HmA==": 13,
|
|
// "$FCM4qQ==": 0,
|
|
// }
|
|
//
|
|
// Note the metric names are tokenized with base64. Decoding requires using the
|
|
// Pigweed detokenizer. With a detokenizing-enabled logger, you would get:
|
|
//
|
|
// "i2c": {
|
|
// "gyro": {
|
|
// "num_sampleses": 1,
|
|
// "init_time_us": 1.000000,
|
|
// "initialized": 5,
|
|
// }
|
|
// "bus_errors": 65,
|
|
// "transactions": 13,
|
|
// "bytes_sent": 0,
|
|
// }
|
|
//
|
|
TEST(Metric, InlineConstructionWithGroups) {
|
|
I2cBus i2c_bus;
|
|
Gyro gyro(i2c_bus, i2c_bus.stats());
|
|
|
|
gyro.Init();
|
|
gyro.ReadAngularVelocity();
|
|
gyro.ReadAngularVelocity();
|
|
gyro.ReadAngularVelocity();
|
|
gyro.ReadAngularVelocity();
|
|
|
|
// This "test" doesn't really test anything, and more illustrates how to the
|
|
// metrics could be instantiated in an object tree.
|
|
//
|
|
// Unfortunatlely, testing dump is difficult since we don't have log
|
|
// redirection for tests.
|
|
i2c_bus.stats().Dump();
|
|
}
|
|
|
|
// PW_METRIC_STATIC doesn't support class scopes, since a definition must be
|
|
// provided outside of the class body.
|
|
// TODO(paulmathieu): add support for class scopes and enable this test
|
|
#if 0
|
|
class MetricTest: public ::testing::Test {
|
|
public:
|
|
void Increment() {
|
|
metric_.Increment();
|
|
}
|
|
|
|
private:
|
|
PW_METRIC_STATIC(metric_, "metric", 0u);
|
|
};
|
|
|
|
TEST_F(MetricTest, StaticWithinAClass) {
|
|
Increment();
|
|
}
|
|
#endif
|
|
|
|
Metric* StaticMetricIncrement() {
|
|
PW_METRIC_STATIC(metric, "metric", 0u);
|
|
metric.Increment();
|
|
return &metric;
|
|
}
|
|
|
|
TEST(Metric, StaticWithinAFunction) {
|
|
Metric* metric = StaticMetricIncrement();
|
|
EXPECT_EQ(metric->as_int(), 1u);
|
|
StaticMetricIncrement();
|
|
EXPECT_EQ(metric->as_int(), 2u);
|
|
}
|
|
|
|
} // namespace pw::metric
|