/* * Copyright 2009, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "build/build_config.h" #ifndef OS_WIN #include #endif // Metrics report unit testing #include #include #include "gtest/gtest.h" #include "metrics.h" #ifdef OS_WIN #define SLEEP_ONE_SECOND() ::Sleep(1000) #else #define SLEEP_ONE_SECOND() ::sleep(1) #endif DECLARE_METRIC_count(count); DEFINE_METRIC_count(count); DECLARE_METRIC_timing(timing); DEFINE_METRIC_timing(timing); DECLARE_METRIC_integer(integer); DEFINE_METRIC_integer(integer); DECLARE_METRIC_bool(bool); DEFINE_METRIC_bool(bool); using ::stats_report::BoolMetric; using ::stats_report::CountMetric; using ::stats_report::IntegerMetric; using ::stats_report::MetricBase; using ::stats_report::MetricCollection; using ::stats_report::MetricCollectionBase; using ::stats_report::MetricIterator; using ::stats_report::TimingMetric; using ::stats_report::TimingSample; using ::stats_report::kBoolType; using ::stats_report::kCountType; using ::stats_report::kIntegerType; using ::stats_report::kTimingType; class MetricsTest: public testing::Test { protected: MetricCollection coll_; }; class MetricsEnumTest: public MetricsTest { public: virtual void SetUp() { coll_.Initialize(); } virtual void TearDown() { coll_.Uninitialize(); } protected: MetricsEnumTest(): count_("count", &coll_), timing_("timing", &coll_), integer_("integer", &coll_), bool_("bool", &coll_) { } CountMetric count_; TimingMetric timing_; IntegerMetric integer_; BoolMetric bool_; }; // Validates that the above-declared metrics are available // in the expected namespace TEST_F(MetricsTest, Globals) { EXPECT_EQ(0u, ::metric_count.Reset()); TimingMetric::TimingData data = ::metric_timing.Reset(); EXPECT_EQ(0u, data.count); EXPECT_EQ(0u, data.maximum); EXPECT_EQ(0u, data.minimum); EXPECT_EQ(0u, data.sum); EXPECT_EQ(0u, ::metric_integer.value()); EXPECT_EQ(BoolMetric::kBoolUnset, ::metric_bool.Reset()); // Check for correct initialization EXPECT_STREQ("count", metric_count.name()); EXPECT_STREQ("timing", metric_timing.name()); EXPECT_STREQ("integer", metric_integer.name()); EXPECT_STREQ("bool", metric_bool.name()); } // make GUnit happy inline std::ostream &operator << (std::ostream &str, const MetricIterator &it) { str << std::hex << reinterpret_cast(*it); return str; } TEST_F(MetricsTest, CollectionInitialization) { // The global MetricCollection is aliased to zero memory so as to ensure // no initialization order snafus. If an initialized MetricCollection // sets any of its storage to non-zero, there's a good chance that e.g. a // vtbl has snuck in there, which must not happen char buf1[sizeof(MetricCollection)] = { 0 }; char buf2[sizeof(MetricCollection)] = { 0 }; // Placement new a MetricCollection to one of the buffers new (buf1) MetricCollection(); // and check they're still equivalent EXPECT_EQ(0, memcmp(buf1, buf2, sizeof(MetricCollection))); // MetricCollection must not extend MetricCollectionBase in size EXPECT_EQ(sizeof(MetricCollection), sizeof(MetricCollectionBase)); } TEST_F(MetricsTest, Count) { CountMetric foo("foo", &coll_); EXPECT_EQ(0u, foo.Reset()); EXPECT_EQ(kCountType, foo.type()); ASSERT_TRUE(NULL != foo.AsCount()); ASSERT_TRUE(NULL == foo.AsTiming()); ASSERT_TRUE(NULL == foo.AsInteger()); ASSERT_TRUE(NULL == foo.AsBool()); ++foo; EXPECT_EQ(1u, foo.value()); foo++; EXPECT_EQ(2u, foo.value()); foo += 100; EXPECT_EQ(102u, foo.value()); } TEST_F(MetricsTest, Timing) { TimingMetric foo("foo", &coll_); EXPECT_EQ(kTimingType, foo.type()); ASSERT_TRUE(NULL == foo.AsCount()); ASSERT_TRUE(NULL != foo.AsTiming()); ASSERT_TRUE(NULL == foo.AsInteger()); ASSERT_TRUE(NULL == foo.AsBool()); foo.AddSample(100); foo.AddSample(50); EXPECT_EQ(2u, foo.count()); EXPECT_EQ(150u, foo.sum()); EXPECT_EQ(100u, foo.maximum()); EXPECT_EQ(50u, foo.minimum()); EXPECT_EQ(75u, foo.average()); TimingMetric::TimingData data = foo.Reset(); EXPECT_EQ(2u, data.count); EXPECT_EQ(150u, data.sum); EXPECT_EQ(100u, data.maximum); EXPECT_EQ(50u, data.minimum); EXPECT_EQ(0u, foo.count()); EXPECT_EQ(0u, foo.sum()); EXPECT_EQ(0u, foo.maximum()); EXPECT_EQ(0u, foo.minimum()); EXPECT_EQ(0u, foo.average()); // Test counted samples foo.AddSamples(10u, 1000); foo.AddSamples(10u, 500); EXPECT_EQ(20u, foo.count()); EXPECT_EQ(1500u, foo.sum()); EXPECT_EQ(100u, foo.maximum()); EXPECT_EQ(50u, foo.minimum()); EXPECT_EQ(75u, foo.average()); } TEST_F(MetricsTest, TimingSample) { TimingMetric foo("foo", &coll_); // add a sample to foo { TimingSample sample(&foo); SLEEP_ONE_SECOND(); } TimingMetric::TimingData data = foo.Reset(); // Should be precisely one sample in there EXPECT_EQ(1u, data.count); // again, this time with a non-unity count { TimingSample sample(&foo, 2); EXPECT_EQ(2u, sample.count()); SLEEP_ONE_SECOND(); } data = foo.Reset(); // Should be precisely two samples in there EXPECT_EQ(2u, data.count); // now with zero count { TimingSample sample(&foo, 0); } data = foo.Reset(); // Should be no samples in there EXPECT_EQ(0u, data.count); } TEST_F(MetricsTest, Integer) { IntegerMetric foo("foo", &coll_); EXPECT_EQ(kIntegerType, foo.type()); ASSERT_TRUE(NULL == foo.AsCount()); ASSERT_TRUE(NULL == foo.AsTiming()); ASSERT_TRUE(NULL != foo.AsInteger()); ASSERT_TRUE(NULL == foo.AsBool()); EXPECT_EQ(0u, foo.value()); foo.Set(1005); EXPECT_EQ(1005u, foo.value()); foo = 1009UL; EXPECT_EQ(1009u, foo.value()); foo.Set(0); ++foo; EXPECT_EQ(1u, foo.value()); foo++; EXPECT_EQ(2u, foo.value()); foo += 100; EXPECT_EQ(102u, foo.value()); foo -= 100; EXPECT_EQ(2u, foo.value()); foo--; EXPECT_EQ(1u, foo.value()); --foo; EXPECT_EQ(0u, foo.value()); } TEST_F(MetricsTest, Bool) { BoolMetric foo("foo", &coll_); EXPECT_EQ(kBoolType, foo.type()); ASSERT_TRUE(NULL == foo.AsCount()); ASSERT_TRUE(NULL == foo.AsTiming()); ASSERT_TRUE(NULL == foo.AsInteger()); ASSERT_TRUE(NULL != foo.AsBool()); EXPECT_EQ(BoolMetric::kBoolUnset, foo.Reset()); foo.Set(true); EXPECT_EQ(BoolMetric::kBoolTrue, foo.Reset()); foo.Set(false); EXPECT_EQ(BoolMetric::kBoolFalse, foo.Reset()); EXPECT_EQ(BoolMetric::kBoolUnset, foo.Reset()); } TEST_F(MetricsEnumTest, Enumeration) { MetricBase *metrics[] = { &count_, &timing_, &integer_, &bool_, }; int limit = sizeof(metrics) / sizeof(metrics[0]); for (int i = 0; i < limit; ++i) { MetricBase *stat = metrics[i]; MetricBase *curr = coll_.first(); for (; NULL != curr; curr = curr->next()) { if (stat == curr) break; } // if NULL, we didn't find our counter EXPECT_TRUE(NULL != curr); } } TEST_F(MetricsEnumTest, Iterator) { typedef MetricBase *MetricBasePtr; MetricBasePtr metrics[] = { &count_, &timing_, &integer_, &bool_, }; int num_stats = arraysize(metrics); MetricIterator it(coll_), end; EXPECT_NE(it, end); // copy construction EXPECT_EQ(it, MetricIterator(it)); EXPECT_EQ(end, MetricIterator(end)); // # of iterations int i = 0; while (it++ != end) ++i; ASSERT_EQ(num_stats, i); ASSERT_EQ(end, it); // increment past end is idempotent ++it; ASSERT_EQ(end, it); // Check that we return no garbage or nonsense for (it = MetricIterator(coll_); it != end; ++it) { MetricBasePtr *stats_end = &metrics[num_stats]; EXPECT_NE(stats_end, std::find(metrics, stats_end, *it)); } // and that all metrics can be found int limit = sizeof(metrics) / sizeof(metrics[0]); for (int i = 0; i < limit; ++i) { MetricBase *stat = metrics[i]; EXPECT_EQ(stat, *std::find(MetricIterator(coll_), end, stat)); } } TEST_F(MetricsTest, SimpleConstruction) { const CountMetric c("c", 100); EXPECT_EQ(100u, c.value()); EXPECT_EQ(kCountType, c.type()); EXPECT_STREQ("c", c.name()); EXPECT_TRUE(NULL == c.next()); TimingMetric::TimingData data = { 10, 0, 1000, 10, 500 }; const TimingMetric t("t", data); EXPECT_EQ(10u, t.count()); EXPECT_EQ(1000u, t.sum()); EXPECT_EQ(10u, t.minimum()); EXPECT_EQ(500u, t.maximum()); EXPECT_EQ(kTimingType, t.type()); EXPECT_STREQ("t", t.name()); EXPECT_TRUE(NULL == t.next()); const IntegerMetric i("i", 200); EXPECT_EQ(200u, i.value()); EXPECT_EQ(kIntegerType, i.type()); EXPECT_STREQ("i", i.name()); EXPECT_TRUE(NULL == i.next()); const BoolMetric bool_true("bool_true", BoolMetric::kBoolTrue); EXPECT_EQ(BoolMetric::kBoolTrue, bool_true.value()); EXPECT_EQ(kBoolType, bool_true.type()); EXPECT_STREQ("bool_true", bool_true.name()); EXPECT_TRUE(NULL == bool_true.next()); const BoolMetric bool_false("bool_false", BoolMetric::kBoolFalse); EXPECT_EQ(BoolMetric::kBoolFalse, bool_false.value()); EXPECT_EQ(kBoolType, bool_false.type()); EXPECT_STREQ("bool_false", bool_false.name()); EXPECT_TRUE(NULL == bool_false.next()); }