| // 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 |