[Spellcheck] Prevent re-recording of non-changed values.

The values recorded in RecordWordCounts() are all strictly monotonic. As such,
re-recording them if they haven't changed distorts our metrics.

Also added framework for unittests for metrics object, plus some basic tests.

[email protected]
[email protected]
BUG=none


Review URL: https://chromiumcodereview.appspot.com/11419178

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@169804 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/spellchecker/spellcheck_host_metrics.cc b/chrome/browser/spellchecker/spellcheck_host_metrics.cc
index 67dd088..2eb6944 100644
--- a/chrome/browser/spellchecker/spellcheck_host_metrics.cc
+++ b/chrome/browser/spellchecker/spellcheck_host_metrics.cc
@@ -9,9 +9,14 @@
 
 SpellCheckHostMetrics::SpellCheckHostMetrics()
     : misspelled_word_count_(0),
+      last_misspelled_word_count_(-1),
       spellchecked_word_count_(0),
+      last_spellchecked_word_count_(-1),
       suggestion_show_count_(0),
+      last_suggestion_show_count_(-1),
       replaced_word_count_(0),
+      last_replaced_word_count_(-1),
+      last_unique_word_count_(-1),
       start_time_(base::TimeTicks::Now()) {
   const uint64 kHistogramTimerDurationInMinutes = 30;
   recording_timer_.Start(FROM_HERE,
@@ -105,9 +110,33 @@
 }
 
 void SpellCheckHostMetrics::RecordWordCounts() {
-  UMA_HISTOGRAM_COUNTS("SpellCheck.CheckedWords", spellchecked_word_count_);
-  UMA_HISTOGRAM_COUNTS("SpellCheck.MisspelledWords", misspelled_word_count_);
-  UMA_HISTOGRAM_COUNTS("SpellCheck.ReplacedWords", replaced_word_count_);
-  UMA_HISTOGRAM_COUNTS("SpellCheck.UniqueWords", checked_word_hashes_.size());
-  UMA_HISTOGRAM_COUNTS("SpellCheck.ShownSuggestions", suggestion_show_count_);
+  if (spellchecked_word_count_ != last_spellchecked_word_count_) {
+    DCHECK(spellchecked_word_count_ > last_spellchecked_word_count_);
+    UMA_HISTOGRAM_COUNTS("SpellCheck.CheckedWords", spellchecked_word_count_);
+    last_spellchecked_word_count_ = spellchecked_word_count_;
+  }
+
+  if (misspelled_word_count_ != last_misspelled_word_count_) {
+    DCHECK(misspelled_word_count_ > last_misspelled_word_count_);
+    UMA_HISTOGRAM_COUNTS("SpellCheck.MisspelledWords", misspelled_word_count_);
+    last_misspelled_word_count_ = misspelled_word_count_;
+  }
+
+  if (replaced_word_count_ != last_replaced_word_count_) {
+    DCHECK(replaced_word_count_ > last_replaced_word_count_);
+    UMA_HISTOGRAM_COUNTS("SpellCheck.ReplacedWords", replaced_word_count_);
+    last_replaced_word_count_ = replaced_word_count_;
+  }
+
+  if (((int)checked_word_hashes_.size()) != last_unique_word_count_) {
+    DCHECK((int)checked_word_hashes_.size() > last_unique_word_count_);
+    UMA_HISTOGRAM_COUNTS("SpellCheck.UniqueWords", checked_word_hashes_.size());
+    last_unique_word_count_ = checked_word_hashes_.size();
+  }
+
+  if (suggestion_show_count_ != last_suggestion_show_count_) {
+    DCHECK(suggestion_show_count_ > last_suggestion_show_count_);
+    UMA_HISTOGRAM_COUNTS("SpellCheck.ShownSuggestions", suggestion_show_count_);
+    last_suggestion_show_count_ = suggestion_show_count_;
+  }
 }
diff --git a/chrome/browser/spellchecker/spellcheck_host_metrics.h b/chrome/browser/spellchecker/spellcheck_host_metrics.h
index d8c9629..14644931 100644
--- a/chrome/browser/spellchecker/spellcheck_host_metrics.h
+++ b/chrome/browser/spellchecker/spellcheck_host_metrics.h
@@ -56,6 +56,7 @@
   void RecordSuggestionStats(int delta);
 
  private:
+  friend class SpellcheckHostMetricsTest;
   void OnHistogramTimerExpired();
 
   // Records various counters without changing their values.
@@ -63,12 +64,23 @@
 
   // Number of corrected words of checked words.
   int misspelled_word_count_;
+  int last_misspelled_word_count_;
+
   // Number of checked words.
   int spellchecked_word_count_;
+  int last_spellchecked_word_count_;
+
   // Number of suggestion list showings.
   int suggestion_show_count_;
+  int last_suggestion_show_count_;
+
   // Number of misspelled words replaced by a user.
   int replaced_word_count_;
+  int last_replaced_word_count_;
+
+  // Last recorded number of unique words.
+  int last_unique_word_count_;
+
   // Time when first spellcheck happened.
   base::TimeTicks start_time_;
   // Set of checked words in the hashed form.
diff --git a/chrome/browser/spellchecker/spellcheck_host_metrics_unittest.cc b/chrome/browser/spellchecker/spellcheck_host_metrics_unittest.cc
new file mode 100644
index 0000000..a2b9bb3
--- /dev/null
+++ b/chrome/browser/spellchecker/spellcheck_host_metrics_unittest.cc
@@ -0,0 +1,126 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/spellchecker/spellcheck_host_metrics.h"
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_samples.h"
+#include "base/metrics/statistics_recorder.h"
+#include "base/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::Histogram;
+using base::HistogramSamples;
+using base::StatisticsRecorder;
+
+class SpellcheckHostMetricsTest : public testing::Test {
+ public:
+  SpellcheckHostMetricsTest() : loop_(MessageLoop::TYPE_DEFAULT) {
+  }
+
+  virtual void SetUp() OVERRIDE {
+    base::StatisticsRecorder::Initialize();
+    metrics_.reset(new SpellCheckHostMetrics);
+  }
+
+  SpellCheckHostMetrics* metrics() { return metrics_.get(); }
+  void RecordWordCountsForTesting() { metrics_->RecordWordCounts(); }
+
+ private:
+   MessageLoop loop_;
+   scoped_ptr<SpellCheckHostMetrics> metrics_;
+};
+
+TEST_F(SpellcheckHostMetricsTest, RecordEnabledStats) {
+  scoped_ptr<HistogramSamples> baseline;
+  Histogram* histogram =
+      StatisticsRecorder::FindHistogram("SpellCheck.Enabled");
+  if (histogram)
+    baseline = histogram->SnapshotSamples();
+
+  metrics()->RecordEnabledStats(false);
+
+  histogram =
+      StatisticsRecorder::FindHistogram("SpellCheck.Enabled");
+  ASSERT_TRUE(histogram != NULL);
+  scoped_ptr<HistogramSamples> samples(histogram->SnapshotSamples());
+  if (baseline.get())
+    samples->Subtract(*baseline);
+  EXPECT_EQ(1, samples->GetCount(0));
+  EXPECT_EQ(0, samples->GetCount(1));
+
+  baseline.reset(samples.release());
+
+  metrics()->RecordEnabledStats(true);
+
+  histogram =
+      StatisticsRecorder::FindHistogram("SpellCheck.Enabled");
+  ASSERT_TRUE(histogram != NULL);
+  samples = histogram->SnapshotSamples();
+  samples->Subtract(*baseline);
+  EXPECT_EQ(0, samples->GetCount(0));
+  EXPECT_EQ(1, samples->GetCount(1));
+}
+
+TEST_F(SpellcheckHostMetricsTest, CustomWordStats) {
+  metrics()->RecordCustomWordCountStats(123);
+
+  Histogram* histogram =
+      StatisticsRecorder::FindHistogram("SpellCheck.CustomWords");
+  ASSERT_TRUE(histogram != NULL);
+  scoped_ptr<HistogramSamples> baseline = histogram->SnapshotSamples();
+
+  metrics()->RecordCustomWordCountStats(23);
+  histogram =
+      StatisticsRecorder::FindHistogram("SpellCheck.CustomWords");
+  ASSERT_TRUE(histogram != NULL);
+  scoped_ptr<HistogramSamples> samples = histogram->SnapshotSamples();
+
+  samples->Subtract(*baseline);
+  EXPECT_EQ(23,samples->sum());
+}
+
+TEST_F(SpellcheckHostMetricsTest, RecordWordCountsDiscardsDuplicates) {
+  // This test ensures that RecordWordCounts only records metrics if they
+  // have changed from the last invocation.
+  const char* histogramName[] = {
+    "SpellCheck.CheckedWords",
+    "SpellCheck.MisspelledWords",
+    "SpellCheck.ReplacedWords",
+    "SpellCheck.UniqueWords",
+    "SpellCheck.ShownSuggestions"
+  };
+
+  // Ensure all histograms exist.
+  metrics()->RecordCheckedWordStats(string16(ASCIIToUTF16("test")), false);
+  RecordWordCountsForTesting();
+
+  // Get baselines for all affected histograms.
+  scoped_ptr<HistogramSamples> baselines[arraysize(histogramName)];
+  for (size_t i = 0; i < arraysize(histogramName); ++i) {
+    Histogram* histogram =
+        StatisticsRecorder::FindHistogram(histogramName[i]);
+    if (histogram)
+      baselines[i] = histogram->SnapshotSamples();
+  }
+
+  // Nothing changed, so this invocation should not affect any histograms.
+  RecordWordCountsForTesting();
+
+  // Get samples for all affected histograms.
+  scoped_ptr<HistogramSamples> samples[arraysize(histogramName)];
+  for (size_t i = 0; i < arraysize(histogramName); ++i) {
+    Histogram* histogram =
+        StatisticsRecorder::FindHistogram(histogramName[i]);
+    ASSERT_TRUE(histogram != NULL);
+    samples[i] = histogram->SnapshotSamples();
+    if (baselines[i].get())
+      samples[i]->Subtract(*baselines[i]);
+
+    EXPECT_EQ(0, samples[i]->TotalCount());
+  }
+}