Change tab count metrics to be usable for both live tabs and all tabs.

Previously this functionality was only usable for live tabs, this change makes it possible to use these functions for tab count filtered histogram metrics based on the total tab count. This will be used for a future tab hover cards metric.

Bug: 910739
Change-Id: I66462d68ac95a81008eeaab90587c11378afad98
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1639084
Reviewed-by: Cait Phillips <[email protected]>
Reviewed-by: Steven Holte <[email protected]>
Reviewed-by: Ryan Sturm <[email protected]>
Commit-Queue: Caroline Rising <[email protected]>
Cr-Commit-Position: refs/heads/master@{#670706}
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 6c6e4ec..187e155c 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -772,8 +772,6 @@
     "metrics/incognito_observer.h",
     "metrics/jumplist_metrics_win.cc",
     "metrics/jumplist_metrics_win.h",
-    "metrics/live_tab_count_metrics.cc",
-    "metrics/live_tab_count_metrics.h",
     "metrics/metrics_memory_details.cc",
     "metrics/metrics_memory_details.h",
     "metrics/metrics_reporting_state.cc",
@@ -794,6 +792,8 @@
     "metrics/sampling_metrics_provider.h",
     "metrics/subprocess_metrics_provider.cc",
     "metrics/subprocess_metrics_provider.h",
+    "metrics/tab_count_metrics.cc",
+    "metrics/tab_count_metrics.h",
     "metrics/tab_footprint_aggregator.cc",
     "metrics/tab_footprint_aggregator.h",
     "metrics/testing/metrics_reporting_pref_helper.cc",
@@ -1927,7 +1927,6 @@
     "//components/language/core/common",
     "//components/leveldb_proto",
     "//components/leveldb_proto/content:factory",
-    "//components/live_tab_count_metrics",
     "//components/metrics:call_stack_profile_collector",
     "//components/metrics:component_metrics",
     "//components/metrics:gpu",
@@ -2003,6 +2002,7 @@
     "//components/sync_bookmarks",
     "//components/sync_preferences",
     "//components/sync_sessions",
+    "//components/tab_count_metrics",
     "//components/tracing:startup_tracing",
     "//components/translate/content/browser",
     "//components/translate/core/browser",
diff --git a/chrome/browser/metrics/live_tab_count_metrics.h b/chrome/browser/metrics/live_tab_count_metrics.h
deleted file mode 100644
index fc004b1..0000000
--- a/chrome/browser/metrics/live_tab_count_metrics.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2018 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.
-
-#ifndef CHROME_BROWSER_METRICS_LIVE_TAB_COUNT_METRICS_H_
-#define CHROME_BROWSER_METRICS_LIVE_TAB_COUNT_METRICS_H_
-
-#include <stddef.h>
-
-// This contains functions for creating live tab count metrics that are specific
-// to //chrome/browser. All bucket-related and process-independent code should
-// live in //components/live_tab_count_metrics.
-namespace live_tab_count_metrics {
-
-// Returns the current number of live tabs in the browser. A tab is considered
-// to be alive if it is associated with the tab UI (i.e. tabstrip), and it is
-// either loading or loaded. This excludes crashed or discarded tabs.
-//
-// Must be called on the UI thread. This function is implemented using
-// TabLoadTracker, and so it is subject to TabLoadTracker's threading rules.
-// Accessing TabLoadTracker must be done from the sequence to which it is bound,
-// which is meant to be the UI thread.
-size_t LiveTabCount();
-
-}  // namespace live_tab_count_metrics
-
-#endif  // CHROME_BROWSER_METRICS_LIVE_TAB_COUNT_METRICS_H_
diff --git a/chrome/browser/metrics/live_tab_count_metrics.cc b/chrome/browser/metrics/tab_count_metrics.cc
similarity index 63%
rename from chrome/browser/metrics/live_tab_count_metrics.cc
rename to chrome/browser/metrics/tab_count_metrics.cc
index 95f3cb9..b2e6434 100644
--- a/chrome/browser/metrics/live_tab_count_metrics.cc
+++ b/chrome/browser/metrics/tab_count_metrics.cc
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/metrics/live_tab_count_metrics.h"
+#include "chrome/browser/metrics/tab_count_metrics.h"
 
 #include "chrome/browser/resource_coordinator/tab_load_tracker.h"
 #include "content/public/browser/browser_thread.h"
 
-namespace live_tab_count_metrics {
+namespace tab_count_metrics {
 
 size_t LiveTabCount() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
@@ -17,4 +17,11 @@
          tab_load_tracker->GetLoadedUiTabCount();
 }
 
-}  // namespace live_tab_count_metrics
+size_t TabCount() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  auto* tab_load_tracker = resource_coordinator::TabLoadTracker::Get();
+  return tab_load_tracker->GetUiTabCount();
+}
+
+}  // namespace tab_count_metrics
diff --git a/chrome/browser/metrics/tab_count_metrics.h b/chrome/browser/metrics/tab_count_metrics.h
new file mode 100644
index 0000000..b80162bb
--- /dev/null
+++ b/chrome/browser/metrics/tab_count_metrics.h
@@ -0,0 +1,36 @@
+// Copyright 2018 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.
+
+#ifndef CHROME_BROWSER_METRICS_TAB_COUNT_METRICS_H_
+#define CHROME_BROWSER_METRICS_TAB_COUNT_METRICS_H_
+
+#include <stddef.h>
+
+// This contains functions for creating tab count metrics that are specific
+// to //chrome/browser. All bucket-related and process-independent code should
+// live in //components/tab_count_metrics.
+namespace tab_count_metrics {
+
+// Returns the current number of live tabs in the browser. A tab is considered
+// to be alive if it is associated with the tab UI (i.e. tabstrip), and it is
+// either loading or loaded. This excludes crashed or discarded tabs.
+//
+// Must be called on the UI thread. This function is implemented using
+// TabLoadTracker, and so it is subject to TabLoadTracker's threading rules.
+// Accessing TabLoadTracker must be done from the sequence to which it is bound,
+// which is meant to be the UI thread.
+size_t LiveTabCount();
+
+// Returns the current number of tabs in the browser. This includes unloaded,
+// loading, and loaded tabs.
+//
+// Must be called on the UI thread. This function is implemented using
+// TabLoadTracker, and so it is subject to TabLoadTracker's threading rules.
+// Accessing TabLoadTracker must be done from the sequence to which it is bound,
+// which is meant to be the UI thread.
+size_t TabCount();
+
+}  // namespace tab_count_metrics
+
+#endif  // CHROME_BROWSER_METRICS_TAB_COUNT_METRICS_H_
diff --git a/chrome/browser/page_load_metrics/observers/live_tab_count_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/live_tab_count_page_load_metrics_observer.cc
index c10f85e9..2a517514 100644
--- a/chrome/browser/page_load_metrics/observers/live_tab_count_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/live_tab_count_page_load_metrics_observer.cc
@@ -8,22 +8,23 @@
 
 #include "base/metrics/histogram.h"
 #include "base/metrics/histogram_macros.h"
-#include "chrome/browser/metrics/live_tab_count_metrics.h"
+#include "chrome/browser/metrics/tab_count_metrics.h"
 #include "chrome/browser/page_load_metrics/observers/histogram_suffixes.h"
 #include "chrome/browser/page_load_metrics/page_load_metrics_util.h"
-#include "components/live_tab_count_metrics/live_tab_count_metrics.h"
+#include "components/tab_count_metrics/tab_count_metrics.h"
 
 #define LIVE_TAB_COUNT_PAGE_LOAD_HISTOGRAM(prefix, tab_count_bucket, sample, \
                                            histogram_min, histogram_max,     \
                                            histogram_buckets)                \
   STATIC_HISTOGRAM_POINTER_GROUP(                                            \
-      live_tab_count_metrics::HistogramName(histogram_prefix,                \
-                                            tab_count_bucket),               \
+      tab_count_metrics::HistogramName(                                      \
+          histogram_prefix, /* live_tabs_only = */ true, tab_count_bucket),  \
       static_cast<int>(bucket),                                              \
-      static_cast<int>(live_tab_count_metrics::kNumLiveTabCountBuckets),     \
+      static_cast<int>(tab_count_metrics::kNumTabCountBuckets),              \
       AddTimeMillisecondsGranularity(sample),                                \
       base::Histogram::FactoryTimeGet(                                       \
-          live_tab_count_metrics::HistogramName(prefix, tab_count_bucket),   \
+          tab_count_metrics::HistogramName(                                  \
+              prefix, /* live_tabs_only = */ true, tab_count_bucket),        \
           histogram_min, histogram_max, histogram_buckets,                   \
           base::HistogramBase::kUmaTargetedHistogramFlag))
 
@@ -63,7 +64,7 @@
         std::string(internal::kHistogramPrefixLiveTabCount)
             .append(internal::kHistogramFirstContentfulPaintSuffix));
     const size_t bucket =
-        live_tab_count_metrics::BucketForLiveTabCount(GetLiveTabCount());
+        tab_count_metrics::BucketForTabCount(GetLiveTabCount());
     LIVE_TAB_COUNT_PAINT_PAGE_LOAD_HISTOGRAM(
         histogram_prefix, bucket,
         timing.paint_timing->first_contentful_paint.value());
@@ -80,7 +81,7 @@
         std::string(internal::kHistogramPrefixLiveTabCount)
             .append(internal::kHistogramFirstMeaningfulPaintSuffix));
     const size_t bucket =
-        live_tab_count_metrics::BucketForLiveTabCount(GetLiveTabCount());
+        tab_count_metrics::BucketForTabCount(GetLiveTabCount());
     LIVE_TAB_COUNT_PAINT_PAGE_LOAD_HISTOGRAM(
         histogram_prefix, bucket,
         timing.paint_timing->first_meaningful_paint.value());
@@ -96,7 +97,7 @@
         std::string(internal::kHistogramPrefixLiveTabCount)
             .append(internal::kHistogramFirstInputDelaySuffix));
     const size_t bucket =
-        live_tab_count_metrics::BucketForLiveTabCount(GetLiveTabCount());
+        tab_count_metrics::BucketForTabCount(GetLiveTabCount());
     LIVE_TAB_COUNT_INPUT_PAGE_LOAD_HISTOGRAM(
         histogram_prefix, bucket,
         timing.interactive_timing->first_input_delay.value());
@@ -104,5 +105,5 @@
 }
 
 size_t LiveTabCountPageLoadMetricsObserver::GetLiveTabCount() const {
-  return live_tab_count_metrics::LiveTabCount();
+  return tab_count_metrics::LiveTabCount();
 }
diff --git a/chrome/browser/page_load_metrics/observers/live_tab_count_page_load_metrics_observer_browsertest.cc b/chrome/browser/page_load_metrics/observers/live_tab_count_page_load_metrics_observer_browsertest.cc
index 55f9958..22e34a4 100644
--- a/chrome/browser/page_load_metrics/observers/live_tab_count_page_load_metrics_observer_browsertest.cc
+++ b/chrome/browser/page_load_metrics/observers/live_tab_count_page_load_metrics_observer_browsertest.cc
@@ -9,18 +9,18 @@
 #include <string>
 
 #include "base/test/metrics/histogram_tester.h"
-#include "chrome/browser/metrics/live_tab_count_metrics.h"
+#include "chrome/browser/metrics/tab_count_metrics.h"
 #include "chrome/browser/page_load_metrics/observers/histogram_suffixes.h"
 #include "chrome/browser/page_load_metrics/page_load_metrics_test_waiter.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
-#include "components/live_tab_count_metrics/live_tab_count_metrics.h"
+#include "components/tab_count_metrics/tab_count_metrics.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using BucketCountArray =
-    std::array<size_t, live_tab_count_metrics::kNumLiveTabCountBuckets>;
+    std::array<size_t, tab_count_metrics::kNumTabCountBuckets>;
 using page_load_metrics::PageLoadMetricsTestWaiter;
 using TimingField = page_load_metrics::PageLoadMetricsTestWaiter::TimingField;
 
@@ -49,7 +49,8 @@
         std::string(suffix);
     for (size_t bucket = 0; bucket < expected_counts.size(); bucket++) {
       histogram_tester_.ExpectTotalCount(
-          live_tab_count_metrics::HistogramName(histogram_prefix, bucket),
+          tab_count_metrics::HistogramName(histogram_prefix,
+                                           /* live_tabs_only = */ true, bucket),
           expected_counts[bucket]);
     }
   }
@@ -70,9 +71,9 @@
   ui_test_utils::NavigateToURL(browser(), GetTestURL());
   waiter->Wait();
 
-  size_t live_tab_count = live_tab_count_metrics::LiveTabCount();
+  size_t live_tab_count = tab_count_metrics::LiveTabCount();
   EXPECT_EQ(live_tab_count, 1u);
-  ++counts[live_tab_count_metrics::BucketForLiveTabCount(live_tab_count)];
+  ++counts[tab_count_metrics::BucketForTabCount(live_tab_count)];
   ValidateHistograms(internal::kHistogramFirstContentfulPaintSuffix, counts);
   ValidateHistograms(internal::kHistogramFirstMeaningfulPaintSuffix, counts);
 }
@@ -117,9 +118,9 @@
   ui_test_utils::NavigateToURL(browser(), GetTestURL());
   waiter->Wait();
 
-  size_t live_tab_count = live_tab_count_metrics::LiveTabCount();
+  size_t live_tab_count = tab_count_metrics::LiveTabCount();
   EXPECT_EQ(live_tab_count, 1u);
-  ++counts[live_tab_count_metrics::BucketForLiveTabCount(live_tab_count)];
+  ++counts[tab_count_metrics::BucketForTabCount(live_tab_count)];
   ValidateHistograms(internal::kHistogramFirstContentfulPaintSuffix, counts);
   ValidateHistograms(internal::kHistogramFirstMeaningfulPaintSuffix, counts);
 
@@ -139,9 +140,9 @@
 
     waiter->Wait();
 
-    live_tab_count = live_tab_count_metrics::LiveTabCount();
+    live_tab_count = tab_count_metrics::LiveTabCount();
     EXPECT_EQ(live_tab_count, tab + 1);
-    ++counts[live_tab_count_metrics::BucketForLiveTabCount(live_tab_count)];
+    ++counts[tab_count_metrics::BucketForTabCount(live_tab_count)];
 
     ValidateHistograms(internal::kHistogramFirstContentfulPaintSuffix, counts);
     ValidateHistograms(internal::kHistogramFirstMeaningfulPaintSuffix, counts);
diff --git a/chrome/browser/page_load_metrics/observers/live_tab_count_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/live_tab_count_page_load_metrics_observer_unittest.cc
index 5b9cb97..dd55e68 100644
--- a/chrome/browser/page_load_metrics/observers/live_tab_count_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/live_tab_count_page_load_metrics_observer_unittest.cc
@@ -12,7 +12,7 @@
 #include "chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.h"
 #include "chrome/browser/page_load_metrics/page_load_tracker.h"
 #include "chrome/common/page_load_metrics/test/page_load_metrics_test_util.h"
-#include "components/live_tab_count_metrics/live_tab_count_metrics.h"
+#include "components/tab_count_metrics/tab_count_metrics.h"
 #include "content/public/browser/web_contents.h"
 #include "url/gurl.h"
 
@@ -21,7 +21,7 @@
 enum TabState { kForeground, kBackground };
 
 using BucketCountArray =
-    std::array<size_t, live_tab_count_metrics::kNumLiveTabCountBuckets>;
+    std::array<size_t, tab_count_metrics::kNumTabCountBuckets>;
 
 class TestLiveTabCountPageLoadMetricsObserver
     : public LiveTabCountPageLoadMetricsObserver {
@@ -87,7 +87,8 @@
         std::string(page_load_histogram_suffix);
     for (size_t bucket = 0; bucket < expected_counts.size(); bucket++) {
       histogram_tester().ExpectTotalCount(
-          live_tab_count_metrics::HistogramName(histogram_prefix, bucket),
+          tab_count_metrics::HistogramName(histogram_prefix,
+                                           /* live_tabs_only = */ true, bucket),
           expected_counts[bucket]);
     }
   }
@@ -105,7 +106,7 @@
   // Simulate loading pages with the number of live tabs ranging from 0 to
   // kMaxLiveTabCount.
   for (size_t num_tabs = 0; num_tabs <= kMaxLiveTabCount; num_tabs++) {
-    bucket = live_tab_count_metrics::BucketForLiveTabCount(num_tabs);
+    bucket = tab_count_metrics::BucketForTabCount(num_tabs);
     SimulatePageLoad(num_tabs, tab_state);
     // We only record metrics if the tab was foregrounded the entire time
     // preceding the event that caused the metrics to be recorded.
@@ -116,7 +117,7 @@
     ValidateHistograms(internal::kHistogramFirstInputDelaySuffix, counts);
   }
   // Make sure we are testing each bucket.
-  EXPECT_EQ(bucket, live_tab_count_metrics::kNumLiveTabCountBuckets - 1);
+  EXPECT_EQ(bucket, tab_count_metrics::kNumTabCountBuckets - 1);
 }
 
 INSTANTIATE_TEST_SUITE_P(Foreground,
diff --git a/components/BUILD.gn b/components/BUILD.gn
index db0f8f1..1e3f6d6 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -108,7 +108,6 @@
     "//components/language/core/common:unit_tests",
     "//components/language_usage_metrics:unit_tests",
     "//components/leveldb_proto:unit_tests",
-    "//components/live_tab_count_metrics:unit_tests",
     "//components/metrics:unit_tests",
     "//components/navigation_metrics:unit_tests",
     "//components/net_log:unit_tests",
@@ -152,6 +151,7 @@
     "//components/sync_preferences:unit_tests",
     "//components/sync_sessions:unit_tests",
     "//components/sync_user_events:unit_tests",
+    "//components/tab_count_metrics:unit_tests",
     "//components/test:run_all_unittests",
     "//components/translate/core/browser:unit_tests",
     "//components/translate/core/common:unit_tests",
diff --git a/components/live_tab_count_metrics/README b/components/live_tab_count_metrics/README
deleted file mode 100644
index aa1dc62..0000000
--- a/components/live_tab_count_metrics/README
+++ /dev/null
@@ -1,21 +0,0 @@
-This directory contains process-independent code for recording metrics bucketed
-by the number of live tabs.
-
-We consider a tab to be alive (i.e. a live tab) if it is a UI tab (e.g. in a
-tabstrip, as opposed to a prerenderer), and it has not been discarded and has
-not crashed. Tabs can be discarded on desktop by TabManager to conserve
-resources, and tabs crash when the corresponding renderer process is killed,
-e.g. due to limited resources (OOM).
-
-Clients of this component must be able to count live tabs. The interface this
-component exposes provides a way to help create metrics bucketed by live tab
-count in a consistent manner, but this is dependent on the client knowing the
-count. The code in this directory is meant to be shared between processes, and
-so we do not count live tabs here. The live tab count is a browser concept, and
-it can be computed there. To record metrics bucketed by live tab counts in
-processes other than the browser, the live tab count would need to be plumbed
-out of the browser. In some cases this may be more efficient than plumbing
-metrics data out of the process to the browser, which is an alternative.
-
-This component should not have any dependencies other than //base as it
-should be able to be used from any other place.
diff --git a/components/live_tab_count_metrics/live_tab_count_metrics.cc b/components/live_tab_count_metrics/live_tab_count_metrics.cc
deleted file mode 100644
index f37a7c2..0000000
--- a/components/live_tab_count_metrics/live_tab_count_metrics.cc
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright 2018 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 "components/live_tab_count_metrics/live_tab_count_metrics.h"
-
-#include <limits>
-
-#include "base/logging.h"
-#include "base/stl_util.h"
-
-namespace live_tab_count_metrics {
-
-// These values represent the lower bound for each bucket, and define the live
-// tab count buckets. The final bucket has no upper bound, and each other
-// bucket, i, is bounded above by the lower bound of bucket i + 1.
-//
-// The buckets were determined from the Tabs.MaxTabsInADay histogram,
-// approximating the 25th, 50th, 75th, 95th, and 99th percentiles, but with the
-// single and zero tab cases separated.
-//
-// If adding or removing a bucket, update |kNumLiveTabCountBuckets|,
-// |kLiveTabCountBucketMins|, and |kLiveTabCountBucketNames|. If adding,
-// removing, or changing bucket ranges, the existing metrics that use these
-// functions for emitting histograms should be marked as obsolete, and new
-// metrics should be created. This can be accomplished by versioning
-// |kLiveTabCountBucketNames|, e.g.  ".ByLiveTabCount2.0Tabs", etc., and
-// updating the histogram suffixes section of histograms.xml, creating a new
-// entry for the new suffixes and marking the old suffixes obsolete.
-constexpr size_t kLiveTabCountBucketMins[] = {0, 1, 2, 3, 5, 8, 20, 40};
-
-// Text for the live tab count portion of metric names. These need to be kept
-// in sync with |kLiveTabCountBucketMins|.
-constexpr const char* kLiveTabCountBucketNames[]{
-    ".ByLiveTabCount.0Tabs",      ".ByLiveTabCount.1Tab",
-    ".ByLiveTabCount.2Tabs",      ".ByLiveTabCount.3To4Tabs",
-    ".ByLiveTabCount.5To7Tabs",   ".ByLiveTabCount.8To19Tabs",
-    ".ByLiveTabCount.20To39Tabs", ".ByLiveTabCount.40OrMoreTabs"};
-
-std::string HistogramName(const std::string prefix, size_t bucket) {
-  static_assert(
-      base::size(kLiveTabCountBucketMins) == kNumLiveTabCountBuckets,
-      "kLiveTabCountBucketMins must have kNumLiveTabCountBuckets elements.");
-  static_assert(
-      base::size(kLiveTabCountBucketNames) == kNumLiveTabCountBuckets,
-      "kLiveTabCountBucketNames must have kNumLiveTabCountBuckets elements.");
-  DCHECK_LT(bucket, kNumLiveTabCountBuckets);
-  DCHECK(prefix.length());
-  return prefix + kLiveTabCountBucketNames[bucket];
-}
-
-size_t BucketForLiveTabCount(size_t num_live_tabs) {
-  for (size_t bucket = 0; bucket < kNumLiveTabCountBuckets; bucket++) {
-    if (internal::IsInBucket(num_live_tabs, bucket))
-      return bucket;
-  }
-  // There should be a bucket for any number of tabs >= 0.
-  NOTREACHED();
-  return kNumLiveTabCountBuckets;
-}
-
-namespace internal {
-
-size_t BucketMin(size_t bucket) {
-  DCHECK_LT(bucket, kNumLiveTabCountBuckets);
-  return kLiveTabCountBucketMins[bucket];
-}
-
-size_t BucketMax(size_t bucket) {
-  DCHECK_LT(bucket, kNumLiveTabCountBuckets);
-  // The last bucket includes everything after the min bucket value.
-  if (bucket == kNumLiveTabCountBuckets - 1)
-    return std::numeric_limits<size_t>::max();
-  return kLiveTabCountBucketMins[bucket + 1] - 1;
-}
-
-bool IsInBucket(size_t num_live_tabs, size_t bucket) {
-  DCHECK_LT(bucket, kNumLiveTabCountBuckets);
-  return num_live_tabs >= BucketMin(bucket) &&
-         num_live_tabs <= BucketMax(bucket);
-}
-
-}  // namespace internal
-
-}  // namespace live_tab_count_metrics
diff --git a/components/live_tab_count_metrics/live_tab_count_metrics.h b/components/live_tab_count_metrics/live_tab_count_metrics.h
deleted file mode 100644
index a990d0c..0000000
--- a/components/live_tab_count_metrics/live_tab_count_metrics.h
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright 2018 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.
-
-#ifndef COMPONENTS_LIVE_TAB_COUNT_METRICS_LIVE_TAB_COUNT_METRICS_H_
-#define COMPONENTS_LIVE_TAB_COUNT_METRICS_LIVE_TAB_COUNT_METRICS_H_
-
-#include <string>
-
-#include "base/component_export.h"
-
-// This namespace contains functions for creating histograms bucketed by number
-// of live tabs.
-//
-// All bucket parameters --- number of buckets, bucket sizes, bucket names ---
-// are determined at compile time, and these methods are safe to call from any
-// thread.
-//
-// A typical example of creating a histogram bucketed by live tab count using
-// STATIC_HISTOGRAM_POINTER_GROUP looks something like this:
-//  const size_t live_tab_count = GetLiveTabCount();
-//  const size_t bucket =
-//      live_tab_count_metrics::BucketForLiveTabCount(live_tab_count);
-//  STATIC_HISTOGRAM_POINTER_GROUP(
-//      live_tab_count_metrics::HistogramName(constant_histogram_prefix,
-//                                            bucket),
-//      static_cast<int>(bucket),
-//      static_cast<int>(live_tab_count_metrics::kNumLiveTabCountBuckets),
-//      Add(sample),
-//      base::Histogram::FactoryGet(
-//          live_tab_count_metrics::HistogramName(constant_histogram_prefix,
-//                                                bucket),
-//          MINIMUM_SAMPLE, MAXIMUM_SAMPLE, BUCKET_COUNT,
-//          base::HistogramBase::kUmaTargetedHistogramFlag));
-//  }
-namespace live_tab_count_metrics {
-
-// |kNumLiveTabCountBuckets| is used in various constexpr arrays and as a bound
-// on the histogram array when using STATIC_HISTOGRAM_POINTER_GROUP. This value
-// must be equal to the length of the array of live tab count bucket min values
-// (|kLiveTabCountBucketMins|) and the array of bucket names
-// (|kLiveTabCountBucketNames|) found in the corresponding .cc file.
-constexpr size_t kNumLiveTabCountBuckets = 8;
-
-// Returns the histogram name for |bucket|. The histogram name is the
-// concatenation of |prefix| and the name corresponding to |bucket|, which is of
-// the form |prefix| + ".ByLiveTabCount." + <BucketRangeText>, where
-// <BucketRangeText> is a string describing the bucket range, e.g. "1Tab",
-// "3To4Tabs", etc. See |kLiveTabCountBucketNames| for all of the bucket names.
-// |bucket| must be in the interval [0, |kNumLiveTabCountBuckets|).
-COMPONENT_EXPORT(LIVE_TAB_COUNT_METRICS)
-std::string HistogramName(const std::string prefix, size_t bucket);
-
-// Return the bucket index for the |num_live_tabs|.
-COMPONENT_EXPORT(LIVE_TAB_COUNT_METRICS)
-size_t BucketForLiveTabCount(size_t num_live_tabs);
-
-// These are exposed for unit tests.
-namespace internal {
-
-// Returns the number of tabs corresponding to the minimum value of |bucket|.
-// |bucket| must be in the interval [0, |kNumLiveTabCountBuckets|).
-COMPONENT_EXPORT(LIVE_TAB_COUNT_METRICS)
-size_t BucketMin(size_t bucket);
-
-// Returns the number of tabs corresponding to the maximum value of |bucket|.
-// |bucket| must be in the interval [0, |kNumLiveTabCountBuckets|).
-COMPONENT_EXPORT(LIVE_TAB_COUNT_METRICS)
-size_t BucketMax(size_t bucket);
-
-// Returns true if |num_live_tabs| falls within |bucket|.
-// |bucket| must be in the interval [0, |kNumLiveTabCountBuckets|).
-COMPONENT_EXPORT(LIVE_TAB_COUNT_METRICS)
-bool IsInBucket(size_t num_live_tabs, size_t bucket);
-
-}  // namespace internal
-
-}  // namespace live_tab_count_metrics
-
-#endif  // COMPONENTS_LIVE_TAB_COUNT_METRICS_LIVE_TAB_COUNT_METRICS_H_
diff --git a/components/live_tab_count_metrics/live_tab_count_metrics_unittest.cc b/components/live_tab_count_metrics/live_tab_count_metrics_unittest.cc
deleted file mode 100644
index 56b1337..0000000
--- a/components/live_tab_count_metrics/live_tab_count_metrics_unittest.cc
+++ /dev/null
@@ -1,168 +0,0 @@
-// Copyright 2018 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 "components/live_tab_count_metrics/live_tab_count_metrics.h"
-
-#include <algorithm>
-#include <array>
-#include <string>
-
-#include "base/containers/flat_map.h"
-#include "base/metrics/histogram.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/test/metrics/histogram_tester.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace live_tab_count_metrics {
-
-namespace {
-
-// For unit tests, we create bucketed test enumeration histograms with a prefix
-// of |kTestMetricPrefix| and a range of [0, |kMaxLiveTabCount|]. We want good
-// test coverage in all buckets, but the overflow live tab count bucket is
-// unbounded, so we cap the max number of live tabs at a reasonable value.
-constexpr char kTestMetricPrefix[] = "TestMetric";
-constexpr size_t kMaxLiveTabCount = 400;
-
-// LiveTabCounts is a map of a live tab count to expected histogram counts,
-// which is used to validate the test histogram.
-//
-// When we record a sample for test metrics, the number of live tabs is used as
-// the bucket in the enumeration histogram. For example, if we record a sample
-// with the number of live tabs equal to 10, we use 10 as the sample in the
-// enumeration histogram. Suppose we have LiveTabCounts map, |expected_counts|.
-// When recording the metric, we would increment the value of
-// expected_counts[10]. |expected_counts| is then used to validate the
-// histogram.
-using LiveTabCounts = base::flat_map<size_t, size_t>;
-
-// Array of LiveTabCounts, indexed by live tab count bucket.
-//
-// Each value in this array corresponds to the expected counts for a different
-// histogram, i.e. the histogram corresponding to the live tab count bucket
-// equal to the array index. For example, given a
-// HistogramCountsByLiveTabCountBucket array, |expected_counts_by_bucket|,
-// expected_counts_by_bucket[1] are the expected counts for the histogram
-// corresponding to live tab count bucket 1.
-using HistogramCountsByLiveTabCountBucket =
-    std::array<LiveTabCounts, kNumLiveTabCountBuckets>;
-
-}  // namespace
-
-class LiveTabCountMetricsTest : public testing::Test {
- public:
-  LiveTabCountMetricsTest() = default;
-  ~LiveTabCountMetricsTest() override = default;
-
-  void RecordLiveTabCountMetric(size_t live_tab_count) {
-    EXPECT_LE(live_tab_count, kMaxLiveTabCount);
-    const size_t bucket = BucketForLiveTabCount(live_tab_count);
-    STATIC_HISTOGRAM_POINTER_GROUP(
-        HistogramName(kTestMetricPrefix, bucket), static_cast<int>(bucket),
-        static_cast<int>(kNumLiveTabCountBuckets), Add(live_tab_count),
-        base::Histogram::FactoryGet(
-            HistogramName(kTestMetricPrefix, bucket), 1, kMaxLiveTabCount,
-            kMaxLiveTabCount + 1,
-            base::HistogramBase::kUmaTargetedHistogramFlag));
-  }
-
- protected:
-  void ValidateHistograms(
-      const HistogramCountsByLiveTabCountBucket& expected_counts) {
-    for (size_t bucket = 0; bucket < expected_counts.size(); bucket++) {
-      size_t expected_total_count = 0;
-      std::string histogram_name = HistogramName(kTestMetricPrefix, bucket);
-      for (const auto& live_tab_counts : expected_counts[bucket]) {
-        histogram_tester_.ExpectBucketCount(
-            histogram_name, live_tab_counts.first, live_tab_counts.second);
-        expected_total_count += live_tab_counts.second;
-      }
-      histogram_tester_.ExpectTotalCount(histogram_name, expected_total_count);
-    }
-  }
-
- private:
-  base::HistogramTester histogram_tester_;
-};
-
-TEST_F(LiveTabCountMetricsTest, HistogramNames) {
-  // Testing with hard-coded strings to check that the concatenated names
-  // produced by HistogramName() are indeed what we expect. If the bucket ranges
-  // change, these strings will need to as well.
-  const std::string kTestMetricNameBucket1("TestMetric.ByLiveTabCount.1Tab");
-  const std::string kTestMetricNameBucket3(
-      "TestMetric.ByLiveTabCount.3To4Tabs");
-  EXPECT_EQ(kTestMetricNameBucket1, HistogramName(kTestMetricPrefix, 1));
-  EXPECT_EQ(kTestMetricNameBucket3, HistogramName(kTestMetricPrefix, 3));
-}
-
-TEST_F(LiveTabCountMetricsTest, RecordMetricsForBucketLimits) {
-  // For each bucket, simulate recording metrics with the live tab count equal
-  // to the lower and upper bounds of each bucket. For the last bucket, we use
-  // an artificial upper bound since that bucket has no practical bound (max
-  // size_t value) and do not want to create a histogram with that many buckets.
-  HistogramCountsByLiveTabCountBucket expected_counts;
-  for (size_t bucket = 0; bucket < expected_counts.size(); bucket++) {
-    size_t num_live_tabs = internal::BucketMin(bucket);
-    RecordLiveTabCountMetric(num_live_tabs);
-    // Expect a count of 1 for the |num_live_tabs| case in the histogram
-    // corresponding to |bucket|.
-    expected_counts[bucket].emplace(num_live_tabs, 1);
-
-    num_live_tabs = std::min(internal::BucketMax(bucket), kMaxLiveTabCount);
-    RecordLiveTabCountMetric(num_live_tabs);
-    // Ensure that we aren't setting the artificial maximum for the last bucket
-    // too low.
-    EXPECT_LE(internal::BucketMin(bucket), num_live_tabs);
-
-    auto iter = expected_counts[bucket].find(num_live_tabs);
-    if (iter != expected_counts[bucket].end()) {
-      // If the lower bound for |bucket| is the same as the upper bound, we've
-      // already recorded a value for |num_live_tabs| in |bucket|.
-      EXPECT_EQ(iter->second, 1u);
-      ++iter->second;
-    } else {
-      expected_counts[bucket].emplace(num_live_tabs, 1);
-    }
-
-    ValidateHistograms(expected_counts);
-  }
-}
-
-TEST_F(LiveTabCountMetricsTest, RecordMetricsForAllBucketValues) {
-  // For each bucket, simulate recording metrics with the live tab count equal
-  // to each value in the bucket range, i.e. from BucketMin() to BucketMax().
-  // For the last bucket, we use an artificial upper bound since that bucket has
-  // no practical bound (max size_t value) and do not want to create a histogram
-  // with that many buckets.
-  HistogramCountsByLiveTabCountBucket expected_counts;
-  for (size_t bucket = 0; bucket < expected_counts.size(); bucket++) {
-    size_t num_live_tabs_lower_bound = internal::BucketMin(bucket);
-    size_t num_live_tabs_upper_bound =
-        std::min(internal::BucketMax(bucket), kMaxLiveTabCount);
-    EXPECT_LE(num_live_tabs_lower_bound, num_live_tabs_upper_bound);
-    for (size_t live_tab_count = num_live_tabs_lower_bound;
-         live_tab_count <= num_live_tabs_upper_bound; live_tab_count++) {
-      RecordLiveTabCountMetric(live_tab_count);
-      expected_counts[bucket].emplace(live_tab_count, 1);
-    }
-    ValidateHistograms(expected_counts);
-  }
-}
-
-TEST_F(LiveTabCountMetricsTest, BucketsDoNotOverlap) {
-  // For any number of tabs >= 0, the tab should belong to exactly one bucket.
-  for (size_t tab_count = 0; tab_count <= kMaxLiveTabCount; tab_count++) {
-    bool has_bucket_for_tab_count = false;
-    for (size_t bucket = 0; bucket < kNumLiveTabCountBuckets; bucket++) {
-      if (internal::IsInBucket(tab_count, bucket)) {
-        EXPECT_FALSE(has_bucket_for_tab_count);
-        has_bucket_for_tab_count = true;
-      }
-    }
-    EXPECT_TRUE(has_bucket_for_tab_count);
-  }
-}
-
-}  // namespace live_tab_count_metrics
diff --git a/components/live_tab_count_metrics/BUILD.gn b/components/tab_count_metrics/BUILD.gn
similarity index 61%
rename from components/live_tab_count_metrics/BUILD.gn
rename to components/tab_count_metrics/BUILD.gn
index 5868db2..84b24ff 100644
--- a/components/live_tab_count_metrics/BUILD.gn
+++ b/components/tab_count_metrics/BUILD.gn
@@ -2,13 +2,13 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-component("live_tab_count_metrics") {
+component("tab_count_metrics") {
   sources = [
-    "live_tab_count_metrics.cc",
-    "live_tab_count_metrics.h",
+    "tab_count_metrics.cc",
+    "tab_count_metrics.h",
   ]
 
-  defines = [ "IS_LIVE_TAB_COUNT_METRICS_IMPL" ]
+  defines = [ "IS_TAB_COUNT_METRICS_IMPL" ]
 
   public_deps = [
     "//base",
@@ -18,11 +18,11 @@
 source_set("unit_tests") {
   testonly = true
   sources = [
-    "live_tab_count_metrics_unittest.cc",
+    "tab_count_metrics_unittest.cc",
   ]
 
   deps = [
-    ":live_tab_count_metrics",
+    ":tab_count_metrics",
     "//base/test:test_support",
     "//testing/gtest",
   ]
diff --git a/components/live_tab_count_metrics/OWNERS b/components/tab_count_metrics/OWNERS
similarity index 100%
rename from components/live_tab_count_metrics/OWNERS
rename to components/tab_count_metrics/OWNERS
diff --git a/components/tab_count_metrics/README b/components/tab_count_metrics/README
new file mode 100644
index 0000000..f9a70a0
--- /dev/null
+++ b/components/tab_count_metrics/README
@@ -0,0 +1,21 @@
+This directory contains process-independent code for recording metrics bucketed
+by the number of tabs or the number of live tabs.
+
+We consider a tab to be alive (i.e. a live tab) if it is a UI tab (e.g. in a
+tabstrip, as opposed to a prerenderer), and it has not been discarded and has
+not crashed. Tabs can be discarded on desktop by TabManager to conserve
+resources, and tabs crash when the corresponding renderer process is killed,
+e.g. due to limited resources (OOM).
+
+Clients of this component must be able to count live tabs. The interface this
+component exposes provides a way to help create metrics bucketed by live/ or all
+tab count in a consistent manner, but this is dependent on the client knowing
+the count. The code in this directory is meant to be shared between processes,
+and so we do not count tabs here. The tab count is a browser concept, and it can
+be computed there. To record metrics bucketed by tab counts in processes other
+than the browser, the tab count would need to be plumbed out of the browser. In
+some cases this may be more efficient than plumbing metrics data out of the
+process to the browser, which is an alternative.
+
+This component should not have any dependencies other than //base as it
+should be able to be used from any other place.
diff --git a/components/tab_count_metrics/tab_count_metrics.cc b/components/tab_count_metrics/tab_count_metrics.cc
new file mode 100644
index 0000000..068a0b3
--- /dev/null
+++ b/components/tab_count_metrics/tab_count_metrics.cc
@@ -0,0 +1,86 @@
+// Copyright 2018 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 "components/tab_count_metrics/tab_count_metrics.h"
+
+#include <limits>
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+
+namespace tab_count_metrics {
+
+// These values represent the lower bound for each bucket, and define the
+// tab count buckets. The final bucket has no upper bound, and each other
+// bucket, i, is bounded above by the lower bound of bucket i + 1.
+//
+// The buckets were determined from the Tabs.MaxTabsInADay histogram,
+// approximating the 25th, 50th, 75th, 95th, and 99th percentiles, but with the
+// single and zero tab cases separated.
+//
+// If adding or removing a bucket, update |kNumTabCountBuckets|,
+// |kTabCountBucketMins|, and |kTabCountBucketNames|. If adding,
+// removing, or changing bucket ranges, the existing metrics that use these
+// functions for emitting histograms should be marked as obsolete, and new
+// metrics should be created. This can be accomplished by versioning
+// |kTabCountBucketNames|, e.g.  ".ByTabCount2.0Tabs", etc., and
+// updating the histogram suffixes section of histograms.xml, creating a new
+// entry for the new suffixes and marking the old suffixes obsolete.
+constexpr size_t kTabCountBucketMins[] = {0, 1, 2, 3, 5, 8, 20, 40};
+
+constexpr const char* kTabBucketNamePrefix[]{".ByTabCount", ".ByLiveTabCount"};
+
+// Text for the tab count portion of metric names. These need to be kept
+// in sync with |kTabCountBucketMins|.
+constexpr const char* kTabCountBucketNames[]{
+    ".0Tabs",    ".1Tab",      ".2Tabs",      ".3To4Tabs",
+    ".5To7Tabs", ".8To19Tabs", ".20To39Tabs", ".40OrMoreTabs"};
+
+std::string HistogramName(const std::string prefix,
+                          bool live_tabs_only,
+                          size_t bucket) {
+  DCHECK(live_tabs_only);
+  static_assert(base::size(kTabCountBucketMins) == kNumTabCountBuckets,
+                "kTabCountBucketMins must have kNumTabCountBuckets elements.");
+  static_assert(base::size(kTabCountBucketNames) == kNumTabCountBuckets,
+                "kTabCountBucketNames must have kNumTabCountBuckets elements.");
+  DCHECK_LT(bucket, kNumTabCountBuckets);
+  DCHECK(prefix.length());
+  return prefix + kTabBucketNamePrefix[live_tabs_only ? 1u : 0u] +
+         kTabCountBucketNames[bucket];
+}
+
+size_t BucketForTabCount(size_t num_tabs) {
+  for (size_t bucket = 0; bucket < kNumTabCountBuckets; bucket++) {
+    if (internal::IsInBucket(num_tabs, bucket))
+      return bucket;
+  }
+  // There should be a bucket for any number of tabs >= 0.
+  NOTREACHED();
+  return kNumTabCountBuckets;
+}
+
+namespace internal {
+
+size_t BucketMin(size_t bucket) {
+  DCHECK_LT(bucket, kNumTabCountBuckets);
+  return kTabCountBucketMins[bucket];
+}
+
+size_t BucketMax(size_t bucket) {
+  DCHECK_LT(bucket, kNumTabCountBuckets);
+  // The last bucket includes everything after the min bucket value.
+  if (bucket == kNumTabCountBuckets - 1)
+    return std::numeric_limits<size_t>::max();
+  return kTabCountBucketMins[bucket + 1] - 1;
+}
+
+bool IsInBucket(size_t num_tabs, size_t bucket) {
+  DCHECK_LT(bucket, kNumTabCountBuckets);
+  return num_tabs >= BucketMin(bucket) && num_tabs <= BucketMax(bucket);
+}
+
+}  // namespace internal
+
+}  // namespace tab_count_metrics
diff --git a/components/tab_count_metrics/tab_count_metrics.h b/components/tab_count_metrics/tab_count_metrics.h
new file mode 100644
index 0000000..5a573d8
--- /dev/null
+++ b/components/tab_count_metrics/tab_count_metrics.h
@@ -0,0 +1,82 @@
+// Copyright 2018 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.
+
+#ifndef COMPONENTS_TAB_COUNT_METRICS_TAB_COUNT_METRICS_H_
+#define COMPONENTS_TAB_COUNT_METRICS_TAB_COUNT_METRICS_H_
+
+#include <string>
+
+#include "base/component_export.h"
+
+// This namespace contains functions for creating histograms bucketed by number
+// of tabs.
+//
+// All bucket parameters --- number of buckets, bucket sizes, bucket names ---
+// are determined at compile time, and these methods are safe to call from any
+// thread.
+//
+// A typical example of creating a histogram bucketed by tab count using
+// STATIC_HISTOGRAM_POINTER_GROUP looks something like this:
+//  const size_t live_tab_count = GetLiveTabCount();
+//  const size_t bucket =
+//      tab_count_metrics::BucketForTabCount(live_tab_count);
+//  STATIC_HISTOGRAM_POINTER_GROUP(
+//      tab_count_metrics::HistogramName(constant_histogram_prefix,
+//                                       live_tabs_only, bucket),
+//      static_cast<int>(bucket),
+//      static_cast<int>(tab_count_metrics::kNumTabCountBuckets),
+//      Add(sample),
+//      base::Histogram::FactoryGet(
+//          tab_count_metrics::HistogramName(constant_histogram_prefix,
+//                                           live_tabs_only, bucket),
+//          MINIMUM_SAMPLE, MAXIMUM_SAMPLE, BUCKET_COUNT,
+//          base::HistogramBase::kUmaTargetedHistogramFlag));
+//  }
+namespace tab_count_metrics {
+
+// |kNumTabCountBuckets| is used in various constexpr arrays and as a bound
+// on the histogram array when using STATIC_HISTOGRAM_POINTER_GROUP. This value
+// must be equal to the length of the array of tab count bucket min values
+// (|kTabCountBucketMins|) and the array of bucket names
+// (|kTabCountBucketNames|) found in the corresponding .cc file.
+constexpr size_t kNumTabCountBuckets = 8;
+
+// Returns the histogram name for |bucket|. The histogram name is the
+// concatenation of |prefix| and the name corresponding to |bucket|, which is of
+// the form |prefix| + ".ByTabCount."/".ByLiveTabCount." + <BucketRangeText>,
+// where <BucketRangeText> is a string describing the bucket range, e.g. "1Tab",
+// "3To4Tabs", etc. See |kTabCountBucketNames| for all of the bucket names.
+// |bucket| must be in the interval [0, |kNumTabCountBuckets|).
+COMPONENT_EXPORT(TAB_COUNT_METRICS)
+std::string HistogramName(const std::string prefix,
+                          bool live_tabs_only,
+                          size_t bucket);
+
+// Return the bucket index for the |num_tabs|.
+COMPONENT_EXPORT(TAB_COUNT_METRICS)
+size_t BucketForTabCount(size_t num_tabs);
+
+// These are exposed for unit tests.
+namespace internal {
+
+// Returns the number of tabs corresponding to the minimum value of |bucket|.
+// |bucket| must be in the interval [0, |kNumTabCountBuckets|).
+COMPONENT_EXPORT(TAB_COUNT_METRICS)
+size_t BucketMin(size_t bucket);
+
+// Returns the number of tabs corresponding to the maximum value of |bucket|.
+// |bucket| must be in the interval [0, |kNumTabCountBuckets|).
+COMPONENT_EXPORT(TAB_COUNT_METRICS)
+size_t BucketMax(size_t bucket);
+
+// Returns true if |num_tabs| falls within |bucket|.
+// |bucket| must be in the interval [0, |kNumTabCountBuckets|).
+COMPONENT_EXPORT(TAB_COUNT_METRICS)
+bool IsInBucket(size_t num_tabs, size_t bucket);
+
+}  // namespace internal
+
+}  // namespace tab_count_metrics
+
+#endif  // COMPONENTS_TAB_COUNT_METRICS_TAB_COUNT_METRICS_H_
diff --git a/components/tab_count_metrics/tab_count_metrics_unittest.cc b/components/tab_count_metrics/tab_count_metrics_unittest.cc
new file mode 100644
index 0000000..987452dc
--- /dev/null
+++ b/components/tab_count_metrics/tab_count_metrics_unittest.cc
@@ -0,0 +1,173 @@
+// Copyright 2018 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 "components/tab_count_metrics/tab_count_metrics.h"
+
+#include <algorithm>
+#include <array>
+#include <string>
+
+#include "base/containers/flat_map.h"
+#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace tab_count_metrics {
+
+namespace {
+
+// For unit tests, we create bucketed test enumeration histograms with a prefix
+// of |kTestMetricPrefix| and a range of [0, |kMaxTabCount|]. We want good
+// test coverage in all buckets, but the overflow  tab count bucket is
+// unbounded, so we cap the max number of  tabs at a reasonable value.
+constexpr char kTestMetricPrefix[] = "TestMetric";
+constexpr size_t kMaxTabCount = 400;
+
+// TabCounts is a map of a tab count to expected histogram counts,
+// which is used to validate the test histogram.
+//
+// When we record a sample for test metrics, the number of  tabs is used as
+// the bucket in the enumeration histogram. For example, if we record a sample
+// with the number of  tabs equal to 10, we use 10 as the sample in the
+// enumeration histogram. Suppose we have TabCounts map, |expected_counts|.
+// When recording the metric, we would increment the value of
+// expected_counts[10]. |expected_counts| is then used to validate the
+// histogram.
+using TabCounts = base::flat_map<size_t, size_t>;
+
+// Array of TabCounts, indexed by tab count bucket.
+//
+// Each value in this array corresponds to the expected counts for a different
+// histogram, i.e. the histogram corresponding to the live tab count bucket
+// equal to the array index. For example, given a
+// HistogramCountsByTabCountBucket array, |expected_counts_by_bucket|,
+// expected_counts_by_bucket[1] are the expected counts for the histogram
+// corresponding to live tab count bucket 1.
+using HistogramCountsByTabCountBucket =
+    std::array<TabCounts, kNumTabCountBuckets>;
+
+}  // namespace
+
+class TabCountMetricsTest : public testing::Test {
+ public:
+  TabCountMetricsTest() = default;
+  ~TabCountMetricsTest() override = default;
+
+  void RecordTabCountMetric(size_t tab_count) {
+    EXPECT_LE(tab_count, kMaxTabCount);
+    const size_t bucket = BucketForTabCount(tab_count);
+    STATIC_HISTOGRAM_POINTER_GROUP(
+        HistogramName(kTestMetricPrefix, /* live_tabs_only = */ true, bucket),
+        static_cast<int>(bucket), static_cast<int>(kNumTabCountBuckets),
+        Add(tab_count),
+        base::Histogram::FactoryGet(
+            HistogramName(kTestMetricPrefix, /* live_tabs_only = */ true,
+                          bucket),
+            1, kMaxTabCount, kMaxTabCount + 1,
+            base::HistogramBase::kUmaTargetedHistogramFlag));
+  }
+
+ protected:
+  void ValidateHistograms(
+      const HistogramCountsByTabCountBucket& expected_counts) {
+    for (size_t bucket = 0; bucket < expected_counts.size(); bucket++) {
+      size_t expected_total_count = 0;
+      std::string histogram_name =
+          HistogramName(kTestMetricPrefix, /* live_tabs_only = */ true, bucket);
+      for (const auto& tab_counts : expected_counts[bucket]) {
+        histogram_tester_.ExpectBucketCount(histogram_name, tab_counts.first,
+                                            tab_counts.second);
+        expected_total_count += tab_counts.second;
+      }
+      histogram_tester_.ExpectTotalCount(histogram_name, expected_total_count);
+    }
+  }
+
+ private:
+  base::HistogramTester histogram_tester_;
+};
+
+TEST_F(TabCountMetricsTest, HistogramNames) {
+  // Testing with hard-coded strings to check that the concatenated names
+  // produced by HistogramName() are indeed what we expect. If the bucket ranges
+  // change, these strings will need to as well.
+  const std::string kTestMetricNameBucket1("TestMetric.ByLiveTabCount.1Tab");
+  const std::string kTestMetricNameBucket3(
+      "TestMetric.ByLiveTabCount.3To4Tabs");
+  EXPECT_EQ(kTestMetricNameBucket1,
+            HistogramName(kTestMetricPrefix, /* live_tabs_only = */ true, 1));
+  EXPECT_EQ(kTestMetricNameBucket3,
+            HistogramName(kTestMetricPrefix, /* live_tabs_only = */ true, 3));
+}
+
+TEST_F(TabCountMetricsTest, RecordMetricsForBucketLimits) {
+  // For each bucket, simulate recording metrics with the tab count equal
+  // to the lower and upper bounds of each bucket. For the last bucket, we use
+  // an artificial upper bound since that bucket has no practical bound (max
+  // size_t value) and do not want to create a histogram with that many buckets.
+  HistogramCountsByTabCountBucket expected_counts;
+  for (size_t bucket = 0; bucket < expected_counts.size(); bucket++) {
+    size_t num_tabs = internal::BucketMin(bucket);
+    RecordTabCountMetric(num_tabs);
+    // Expect a count of 1 for the |num_tabs| case in the histogram
+    // corresponding to |bucket|.
+    expected_counts[bucket].emplace(num_tabs, 1);
+
+    num_tabs = std::min(internal::BucketMax(bucket), kMaxTabCount);
+    RecordTabCountMetric(num_tabs);
+    // Ensure that we aren't setting the artificial maximum for the last bucket
+    // too low.
+    EXPECT_LE(internal::BucketMin(bucket), num_tabs);
+
+    auto iter = expected_counts[bucket].find(num_tabs);
+    if (iter != expected_counts[bucket].end()) {
+      // If the lower bound for |bucket| is the same as the upper bound, we've
+      // already recorded a value for |num_tabs| in |bucket|.
+      EXPECT_EQ(iter->second, 1u);
+      ++iter->second;
+    } else {
+      expected_counts[bucket].emplace(num_tabs, 1);
+    }
+
+    ValidateHistograms(expected_counts);
+  }
+}
+
+TEST_F(TabCountMetricsTest, RecordMetricsForAllBucketValues) {
+  // For each bucket, simulate recording metrics with the tab count equal
+  // to each value in the bucket range, i.e. from BucketMin() to BucketMax().
+  // For the last bucket, we use an artificial upper bound since that bucket has
+  // no practical bound (max size_t value) and do not want to create a histogram
+  // with that many buckets.
+  HistogramCountsByTabCountBucket expected_counts;
+  for (size_t bucket = 0; bucket < expected_counts.size(); bucket++) {
+    size_t num_tabs_lower_bound = internal::BucketMin(bucket);
+    size_t num_tabs_upper_bound =
+        std::min(internal::BucketMax(bucket), kMaxTabCount);
+    EXPECT_LE(num_tabs_lower_bound, num_tabs_upper_bound);
+    for (size_t tab_count = num_tabs_lower_bound;
+         tab_count <= num_tabs_upper_bound; tab_count++) {
+      RecordTabCountMetric(tab_count);
+      expected_counts[bucket].emplace(tab_count, 1);
+    }
+    ValidateHistograms(expected_counts);
+  }
+}
+
+TEST_F(TabCountMetricsTest, BucketsDoNotOverlap) {
+  // For any number of tabs >= 0, the tab should belong to exactly one bucket.
+  for (size_t tab_count = 0; tab_count <= kMaxTabCount; tab_count++) {
+    bool has_bucket_for_tab_count = false;
+    for (size_t bucket = 0; bucket < kNumTabCountBuckets; bucket++) {
+      if (internal::IsInBucket(tab_count, bucket)) {
+        EXPECT_FALSE(has_bucket_for_tab_count);
+        has_bucket_for_tab_count = true;
+      }
+    }
+    EXPECT_TRUE(has_bucket_for_tab_count);
+  }
+}
+
+}  // namespace tab_count_metrics