blob: ca57930011514af24cc60908fa658acdf075155d [file] [log] [blame]
// Copyright 2011 Google Inc.
//
// 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
//
// http://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.
//
// Author: jmarantz@google.com (Joshua Marantz)
#include "pagespeed/kernel/base/waveform.h"
#include "pagespeed/kernel/base/gtest.h"
#include "pagespeed/kernel/base/mock_message_handler.h"
#include "pagespeed/kernel/base/mock_timer.h"
#include "pagespeed/kernel/base/scoped_ptr.h"
#include "pagespeed/kernel/base/statistics.h"
#include "pagespeed/kernel/base/string.h"
#include "pagespeed/kernel/base/string_util.h"
#include "pagespeed/kernel/base/string_writer.h"
#include "pagespeed/kernel/base/thread_system.h"
#include "pagespeed/kernel/util/platform.h"
#include "pagespeed/kernel/util/simple_stats.h"
namespace {
const char kCoordinateFormat[] = "[%f, %f]";
} // namespace
namespace net_instaweb {
class WaveformTest : public testing::Test {
protected:
WaveformTest()
: thread_system_(Platform::CreateThreadSystem()),
timer_(thread_system_->NewMutex(), MockTimer::kApr_5_2010_ms),
stats_(thread_system_.get()),
handler_(thread_system_->NewMutex()) {
}
GoogleString Format(int time_ms, int value) {
return StringPrintf(kCoordinateFormat, 1.0 * time_ms, 1.0 * value);
}
bool Contains(const StringPiece& html, int time_ms, int value) {
return (html.find(Format(time_ms, value)) != GoogleString::npos);
}
scoped_ptr<ThreadSystem> thread_system_;
MockTimer timer_;
SimpleStats stats_;
MockMessageHandler handler_;
};
// A basic sanity test showing that the header loads the jsapi.
TEST_F(WaveformTest, Header) {
GoogleString html;
StringWriter writer(&html);
Waveform::RenderHeader(&writer, &handler_);
EXPECT_NE(GoogleString::npos, html.find("www.google.com/jsapi"));
}
// Instantiate a waveform and make sure one of the values shows up.
TEST_F(WaveformTest, BasicGraph) {
UpDownCounter* variable = stats_.AddUpDownCounter("test1");
Waveform waveform(thread_system_.get(), &timer_, 10, variable);
timer_.SetTimeUs(MockTimer::kApr_5_2010_ms);
waveform.Add(10);
timer_.AdvanceMs(10);
waveform.Add(20);
timer_.AdvanceMs(10);
waveform.Add(10);
timer_.AdvanceMs(10);
waveform.Add(30);
timer_.AdvanceMs(10);
waveform.Add(10);
timer_.AdvanceMs(10);
waveform.Add(40);
timer_.AdvanceMs(10);
waveform.Add(10);
timer_.AdvanceMs(10);
waveform.Add(50);
timer_.AdvanceMs(10);
waveform.Add(10);
timer_.AdvanceMs(10);
waveform.Add(60);
timer_.AdvanceMs(10);
GoogleString html;
StringWriter writer(&html);
waveform.Render("My Waveform", "My Values", &writer, &handler_);
EXPECT_TRUE(Contains(html, 90, 60));
EXPECT_NE(GoogleString::npos, html.find("'My Waveform'"));
EXPECT_NE(GoogleString::npos, html.find("'My Values'"));
// The last Add call is sticky.
EXPECT_EQ(60, variable->Get());
}
// Instantiate a waveform and make sure one of the values shows up.
TEST_F(WaveformTest, Delta) {
UpDownCounter* variable = stats_.AddUpDownCounter("test1");
Waveform waveform(thread_system_.get(), &timer_, 10, variable);
timer_.SetTimeUs(MockTimer::kApr_5_2010_ms);
waveform.AddDelta(10);
timer_.AdvanceMs(10);
waveform.AddDelta(20);
timer_.AdvanceMs(10);
waveform.AddDelta(-5);
timer_.AdvanceMs(10);
GoogleString html;
StringWriter writer(&html);
waveform.Render("My Waveform", "My Values", &writer, &handler_);
EXPECT_TRUE(Contains(html, 20, 25));
EXPECT_NE(GoogleString::npos, html.find("'My Waveform'"));
EXPECT_NE(GoogleString::npos, html.find("'My Values'"));
EXPECT_EQ(10 + 20 - 5, variable->Get());
}
// Overflows the number of samples and makes sure the desired results
// are shown.
TEST_F(WaveformTest, Overflow) {
Waveform waveform(thread_system_.get(), &timer_, 10, NULL /* variable */);
// Don't overflow at first.
for (int i = 0; i < 10; ++i) {
waveform.Add(i);
timer_.AdvanceMs(10);
}
GoogleString html;
StringWriter writer(&html);
waveform.Render("My Waveform", "My Values", &writer, &handler_);
EXPECT_TRUE(Contains(html, 0, 0));
EXPECT_TRUE(Contains(html, 10, 1));
EXPECT_TRUE(Contains(html, 80, 8));
EXPECT_TRUE(Contains(html, 90, 9));
// Now overflow the first 2 entries and re-render. The first &
// second values (0,0) and (10,1) should be gone, but note that
// the time-axis is all deltas so the x-values always start at 0.
for (int i = 10; i < 12; ++i) { // knocks off first 2
waveform.Add(i);
timer_.AdvanceMs(10);
}
html.clear();
waveform.Render("My Waveform", "My Values", &writer, &handler_);
EXPECT_FALSE(Contains(html, 0, 1)); // truncated
EXPECT_FALSE(Contains(html, 10, 1)); // truncated
EXPECT_TRUE(Contains(html, 0, 2));
EXPECT_TRUE(Contains(html, 80, 10));
EXPECT_TRUE(Contains(html, 90, 11));
// The rest of the values should be present in the HTML and in order.
GoogleString::size_type prev_pos = 0;
for (int i = 0; i < 10; ++i) {
GoogleString::size_type pos = html.find(Format(10 * i, i + 2));
ASSERT_NE(GoogleString::npos, pos);
EXPECT_LT(prev_pos, pos);
prev_pos = pos;
}
}
TEST_F(WaveformTest, AvgMinMax) {
Waveform waveform(thread_system_.get(), &timer_, 10, NULL /* variable */);
for (int i = 1; i <= 1000; ++i) {
waveform.Add(i);
timer_.AdvanceMs(10);
}
// Note that the first value involved in the average is 0 due to the
// fact that we are accumulating delta_time*value quantities.
EXPECT_EQ(500.0, waveform.Average());
EXPECT_EQ(1.0, waveform.Minimum());
EXPECT_EQ(1000.0, waveform.Maximum());
}
} // namespace net_instaweb