Merge pull request #12891 from ctiller/verify

Multithread & shard stats test, make it exhaustive
diff --git a/build.yaml b/build.yaml
index ac3aa15..557025d 100644
--- a/build.yaml
+++ b/build.yaml
@@ -4734,6 +4734,9 @@
   - grpc
   - gpr_test_util
   - gpr
+  exclude_configs:
+  - tsan
+  timeout_seconds: 1200
   uses_polling: false
 - name: status_test
   build: test
diff --git a/test/core/debug/stats_test.cc b/test/core/debug/stats_test.cc
index c85ab35..5015819 100644
--- a/test/core/debug/stats_test.cc
+++ b/test/core/debug/stats_test.cc
@@ -20,7 +20,11 @@
 #include "src/core/lib/debug/stats.h"
 }
 
+#include <mutex>
+#include <thread>
+
 #include <grpc/grpc.h>
+#include <grpc/support/cpu.h>
 #include <grpc/support/log.h>
 #include <gtest/gtest.h>
 
@@ -79,38 +83,59 @@
          grpc_stats_histo_bucket_boundaries[i] - 1;
 }
 
-TEST(StatsTest, IncHistogram) {
-  for (int i = 0; i < GRPC_STATS_HISTOGRAM_COUNT; i++) {
-    std::vector<int> test_values;
-    for (int j = -1000;
-         j <
-         grpc_stats_histo_bucket_boundaries[i]
-                                           [grpc_stats_histo_buckets[i] - 1] +
-             1000;
-         j++) {
-      test_values.push_back(j);
-    }
-    std::random_shuffle(test_values.begin(), test_values.end());
-    if (test_values.size() > 10000) {
-      test_values.resize(10000);
-    }
+class HistogramTest : public ::testing::TestWithParam<int> {};
+
+TEST_P(HistogramTest, IncHistogram) {
+  const int kHistogram = GetParam();
+  std::vector<std::thread> threads;
+  int cur_bucket = 0;
+  auto run = [kHistogram](const std::vector<int>& test_values,
+                          int expected_bucket) {
+    gpr_log(GPR_DEBUG, "expected_bucket:%d nvalues=%" PRIdPTR, expected_bucket,
+            test_values.size());
     for (auto j : test_values) {
       Snapshot snapshot;
 
-      int expected_bucket = FindExpectedBucket(i, j);
-
       grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-      grpc_stats_inc_histogram[i](&exec_ctx, j);
+      grpc_stats_inc_histogram[kHistogram](&exec_ctx, j);
       grpc_exec_ctx_finish(&exec_ctx);
 
       auto delta = snapshot.delta();
 
-      EXPECT_EQ(delta.histograms[grpc_stats_histo_start[i] + expected_bucket],
-                1);
+      EXPECT_EQ(
+          delta
+              .histograms[grpc_stats_histo_start[kHistogram] + expected_bucket],
+          1)
+          << "\nhistogram:" << kHistogram
+          << "\nexpected_bucket:" << expected_bucket << "\nj:" << j;
     }
+  };
+  std::vector<int> test_values;
+  for (int j = -1000;
+       j <
+       grpc_stats_histo_bucket_boundaries[kHistogram]
+                                         [grpc_stats_histo_buckets[kHistogram] -
+                                          1] +
+           1000;
+       j++) {
+    int expected_bucket = FindExpectedBucket(kHistogram, j);
+    if (cur_bucket != expected_bucket) {
+      threads.emplace_back(
+          [test_values, run, cur_bucket]() { run(test_values, cur_bucket); });
+      cur_bucket = expected_bucket;
+      test_values.clear();
+    }
+    test_values.push_back(j);
+  }
+  run(test_values, cur_bucket);
+  for (auto& t : threads) {
+    t.join();
   }
 }
 
+INSTANTIATE_TEST_CASE_P(HistogramTestCases, HistogramTest,
+                        ::testing::Range<int>(0, GRPC_STATS_HISTOGRAM_COUNT));
+
 }  // namespace testing
 }  // namespace grpc
 
diff --git a/tools/run_tests/generated/tests.json b/tools/run_tests/generated/tests.json
index 3441252..d819170 100644
--- a/tools/run_tests/generated/tests.json
+++ b/tools/run_tests/generated/tests.json
@@ -4389,7 +4389,9 @@
       "windows"
     ], 
     "cpu_cost": 1.0, 
-    "exclude_configs": [], 
+    "exclude_configs": [
+      "tsan"
+    ], 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "gtest": true, 
@@ -4401,6 +4403,7 @@
       "posix", 
       "windows"
     ], 
+    "timeout_seconds": 1200, 
     "uses_polling": false
   }, 
   {
diff --git a/tools/run_tests/python_utils/filter_pull_request_tests.py b/tools/run_tests/python_utils/filter_pull_request_tests.py
index 393aef8..56d6e4e 100644
--- a/tools/run_tests/python_utils/filter_pull_request_tests.py
+++ b/tools/run_tests/python_utils/filter_pull_request_tests.py
@@ -78,7 +78,7 @@
   '^src/python/': [_PYTHON_TEST_SUITE],
   '^src/ruby/': [_RUBY_TEST_SUITE],
   '^templates/': [],
-  '^test/core/': [_CORE_TEST_SUITE],
+  '^test/core/': [_CORE_TEST_SUITE, _CPP_TEST_SUITE],
   '^test/cpp/': [_CPP_TEST_SUITE],
   '^test/distrib/cpp/': [_CPP_TEST_SUITE],
   '^test/distrib/csharp/': [_CSHARP_TEST_SUITE],
diff --git a/tools/run_tests/run_tests.py b/tools/run_tests/run_tests.py
index 4c3db14..9999878 100755
--- a/tools/run_tests/run_tests.py
+++ b/tools/run_tests/run_tests.py
@@ -371,7 +371,7 @@
                 out.append(self.config.job_spec(cmdline,
                                                 shortname='%s %s' % (' '.join(cmdline), shortname_ext),
                                                 cpu_cost=cpu_cost,
-                                                timeout_seconds=_DEFAULT_TIMEOUT_SECONDS * timeout_scaling,
+                                                timeout_seconds=target.get('timeout_seconds', _DEFAULT_TIMEOUT_SECONDS) * timeout_scaling,
                                                 environ=env))
           else:
             cmdline = [binary] + target['args']
diff --git a/tools/run_tests/sanity/check_test_filtering.py b/tools/run_tests/sanity/check_test_filtering.py
index 3ebb938..a523f08 100755
--- a/tools/run_tests/sanity/check_test_filtering.py
+++ b/tools/run_tests/sanity/check_test_filtering.py
@@ -81,7 +81,8 @@
     self.test_filtering(['src/core/foo.bar'], [_LIST_OF_LANGUAGE_LABELS])
     # Testing individual languages
     self.test_filtering(['test/core/foo.bar'], [label for label in _LIST_OF_LANGUAGE_LABELS if label not in
-                                                filter_pull_request_tests._CORE_TEST_SUITE.labels])
+                                                filter_pull_request_tests._CORE_TEST_SUITE.labels +
+                                                filter_pull_request_tests._CPP_TEST_SUITE.labels])
     self.test_filtering(['src/cpp/foo.bar'], [label for label in _LIST_OF_LANGUAGE_LABELS if label not in
                                               filter_pull_request_tests._CPP_TEST_SUITE.labels])
     self.test_filtering(['src/csharp/foo.bar'], [label for label in _LIST_OF_LANGUAGE_LABELS if label not in