aboutsummaryrefslogtreecommitdiff
path: root/third_party/abseil-cpp/absl/strings/cordz_test_helpers.h
blob: e410eecf7fda43d07828a97d0da7eaaf6cc49a8d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
// Copyright 2021 The Abseil Authors
//
// 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
//
//     https://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.

#ifndef ABSL_STRINGS_CORDZ_TEST_HELPERS_H_
#define ABSL_STRINGS_CORDZ_TEST_HELPERS_H_

#include <utility>

#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/base/config.h"
#include "absl/base/macros.h"
#include "absl/strings/cord.h"
#include "absl/strings/internal/cord_internal.h"
#include "absl/strings/internal/cordz_info.h"
#include "absl/strings/internal/cordz_sample_token.h"
#include "absl/strings/internal/cordz_statistics.h"
#include "absl/strings/internal/cordz_update_tracker.h"
#include "absl/strings/str_cat.h"

namespace absl {
ABSL_NAMESPACE_BEGIN

// Returns the CordzInfo for the cord, or nullptr if the cord is not sampled.
inline const cord_internal::CordzInfo* GetCordzInfoForTesting(
    const Cord& cord) {
  if (!cord.contents_.is_tree()) return nullptr;
  return cord.contents_.cordz_info();
}

// Returns true if the provided cordz_info is in the list of sampled cords.
inline bool CordzInfoIsListed(const cord_internal::CordzInfo* cordz_info,
                              cord_internal::CordzSampleToken token = {}) {
  for (const cord_internal::CordzInfo& info : token) {
    if (cordz_info == &info) return true;
  }
  return false;
}

// Matcher on Cord that verifies all of:
// - the cord is sampled
// - the CordzInfo of the cord is listed / discoverable.
// - the reported CordzStatistics match the cord's actual properties
// - the cord has an (initial) UpdateTracker count of 1 for `method`
MATCHER_P(HasValidCordzInfoOf, method, "CordzInfo matches cord") {
  const cord_internal::CordzInfo* cord_info = GetCordzInfoForTesting(arg);
  if (cord_info == nullptr) {
    *result_listener << "cord is not sampled";
    return false;
  }
  if (!CordzInfoIsListed(cord_info)) {
    *result_listener << "cord is sampled, but not listed";
    return false;
  }
  cord_internal::CordzStatistics stat = cord_info->GetCordzStatistics();
  if (stat.size != arg.size()) {
    *result_listener << "cordz size " << stat.size
                     << " does not match cord size " << arg.size();
    return false;
  }
  if (stat.update_tracker.Value(method) != 1) {
    *result_listener << "Expected method count 1 for " << method << ", found "
                     << stat.update_tracker.Value(method);
    return false;
  }
  return true;
}

// Matcher on Cord that verifies that the cord is sampled and that the CordzInfo
// update tracker has 'method' with a call count of 'n'
MATCHER_P2(CordzMethodCountEq, method, n,
           absl::StrCat("CordzInfo method count equals ", n)) {
  const cord_internal::CordzInfo* cord_info = GetCordzInfoForTesting(arg);
  if (cord_info == nullptr) {
    *result_listener << "cord is not sampled";
    return false;
  }
  cord_internal::CordzStatistics stat = cord_info->GetCordzStatistics();
  if (stat.update_tracker.Value(method) != n) {
    *result_listener << "Expected method count " << n << " for " << method
                     << ", found " << stat.update_tracker.Value(method);
    return false;
  }
  return true;
}

// Cordz will only update with a new rate once the previously scheduled event
// has fired. When we disable Cordz, a long delay takes place where we won't
// consider profiling new Cords. CordzSampleIntervalHelper will burn through
// that interval and allow for testing that assumes that the average sampling
// interval is a particular value.
class CordzSamplingIntervalHelper {
 public:
  explicit CordzSamplingIntervalHelper(int32_t interval)
      : orig_mean_interval_(absl::cord_internal::get_cordz_mean_interval()) {
    absl::cord_internal::set_cordz_mean_interval(interval);
    absl::cord_internal::cordz_set_next_sample_for_testing(interval);
  }

  ~CordzSamplingIntervalHelper() {
    absl::cord_internal::set_cordz_mean_interval(orig_mean_interval_);
    absl::cord_internal::cordz_set_next_sample_for_testing(orig_mean_interval_);
  }

 private:
  int32_t orig_mean_interval_;
};

// Wrapper struct managing a small CordRep `rep`
struct TestCordRep {
  cord_internal::CordRepFlat* rep;

  TestCordRep() {
    rep = cord_internal::CordRepFlat::New(100);
    rep->length = 100;
    memset(rep->Data(), 1, 100);
  }
  ~TestCordRep() { cord_internal::CordRep::Unref(rep); }
};

// Wrapper struct managing a small CordRep `rep`, and
// an InlineData `data` initialized with that CordRep.
struct TestCordData {
  TestCordRep rep;
  cord_internal::InlineData data{rep.rep};
};

// Creates a Cord that is not sampled
template <typename... Args>
Cord UnsampledCord(Args... args) {
  CordzSamplingIntervalHelper never(9999);
  Cord cord(std::forward<Args>(args)...);
  ABSL_ASSERT(GetCordzInfoForTesting(cord) == nullptr);
  return cord;
}

ABSL_NAMESPACE_END
}  // namespace absl

#endif  // ABSL_STRINGS_CORDZ_TEST_HELPERS_H_