Coverage Report

Created: 2024-09-19 09:45

/proc/self/cwd/test/test_common/utility.cc
Line
Count
Source (jump to first uncovered line)
1
#include "utility.h"
2
3
#include <cstdint>
4
#include <fstream>
5
#include <iomanip>
6
#include <iostream>
7
#include <list>
8
#include <regex>
9
#include <stdexcept>
10
#include <string>
11
#include <vector>
12
13
#include "envoy/buffer/buffer.h"
14
#include "envoy/common/platform.h"
15
#include "envoy/config/bootstrap/v3/bootstrap.pb.h"
16
#include "envoy/config/cluster/v3/cluster.pb.h"
17
#include "envoy/config/endpoint/v3/endpoint.pb.h"
18
#include "envoy/config/listener/v3/listener.pb.h"
19
#include "envoy/config/route/v3/route.pb.h"
20
#include "envoy/config/route/v3/route_components.pb.h"
21
#include "envoy/http/codec.h"
22
#include "envoy/server/overload/thread_local_overload_state.h"
23
#include "envoy/service/runtime/v3/rtds.pb.h"
24
25
#include "source/common/api/api_impl.h"
26
#include "source/common/common/fmt.h"
27
#include "source/common/common/lock_guard.h"
28
#include "source/common/common/thread_impl.h"
29
#include "source/common/common/utility.h"
30
#include "source/common/filesystem/directory.h"
31
#include "source/common/filesystem/filesystem_impl.h"
32
#include "source/common/http/header_utility.h"
33
#include "source/common/json/json_loader.h"
34
#include "source/common/network/address_impl.h"
35
#include "source/common/network/utility.h"
36
37
#include "test/mocks/common.h"
38
#include "test/mocks/stats/mocks.h"
39
#include "test/test_common/printers.h"
40
#include "test/test_common/resources.h"
41
#include "test/test_common/test_time.h"
42
43
#include "absl/container/fixed_array.h"
44
#include "absl/strings/str_cat.h"
45
#include "absl/strings/string_view.h"
46
#include "absl/synchronization/notification.h"
47
#include "gtest/gtest.h"
48
49
namespace Envoy {
50
51
bool TestUtility::headerMapEqualIgnoreOrder(const Http::HeaderMap& lhs,
52
0
                                            const Http::HeaderMap& rhs) {
53
0
  absl::flat_hash_set<std::string> lhs_keys;
54
0
  absl::flat_hash_set<std::string> rhs_keys;
55
0
  lhs.iterate([&lhs_keys](const Http::HeaderEntry& header) -> Http::HeaderMap::Iterate {
56
0
    const std::string key{header.key().getStringView()};
57
0
    lhs_keys.insert(key);
58
0
    return Http::HeaderMap::Iterate::Continue;
59
0
  });
60
0
  bool values_match = true;
61
0
  rhs.iterate([&values_match, &lhs, &rhs,
62
0
               &rhs_keys](const Http::HeaderEntry& header) -> Http::HeaderMap::Iterate {
63
0
    const std::string key{header.key().getStringView()};
64
    // Compare with canonicalized multi-value headers. This ensures we respect order within
65
    // a header.
66
0
    const auto lhs_entry =
67
0
        Http::HeaderUtility::getAllOfHeaderAsString(lhs, Http::LowerCaseString(key));
68
0
    const auto rhs_entry =
69
0
        Http::HeaderUtility::getAllOfHeaderAsString(rhs, Http::LowerCaseString(key));
70
0
    ASSERT(rhs_entry.result());
71
0
    if (lhs_entry.result() != rhs_entry.result()) {
72
0
      values_match = false;
73
0
      return Http::HeaderMap::Iterate::Break;
74
0
    }
75
0
    rhs_keys.insert(key);
76
0
    return Http::HeaderMap::Iterate::Continue;
77
0
  });
78
0
  return values_match && lhs_keys.size() == rhs_keys.size();
79
0
}
80
81
0
bool TestUtility::buffersEqual(const Buffer::Instance& lhs, const Buffer::Instance& rhs) {
82
0
  if (lhs.length() != rhs.length()) {
83
0
    return false;
84
0
  }
85
86
  // Check whether the two buffers contain the same content. It is valid for the content
87
  // to be arranged differently in the buffers. For example, lhs could have one slice
88
  // containing 10 bytes while rhs has ten slices containing one byte each.
89
0
  Buffer::RawSliceVector lhs_slices = lhs.getRawSlices();
90
0
  Buffer::RawSliceVector rhs_slices = rhs.getRawSlices();
91
92
0
  size_t rhs_slice = 0;
93
0
  size_t rhs_offset = 0;
94
0
  for (auto& lhs_slice : lhs_slices) {
95
0
    for (size_t lhs_offset = 0; lhs_offset < lhs_slice.len_; lhs_offset++) {
96
0
      while (rhs_offset >= rhs_slices[rhs_slice].len_) {
97
0
        rhs_slice++;
98
0
        ASSERT(rhs_slice < rhs_slices.size());
99
0
        rhs_offset = 0;
100
0
      }
101
0
      auto lhs_str = static_cast<const uint8_t*>(lhs_slice.mem_);
102
0
      auto rhs_str = static_cast<const uint8_t*>(rhs_slices[rhs_slice].mem_);
103
0
      if (lhs_str[lhs_offset] != rhs_str[rhs_offset]) {
104
0
        return false;
105
0
      }
106
0
      rhs_offset++;
107
0
    }
108
0
  }
109
110
0
  return true;
111
0
}
112
113
bool TestUtility::rawSlicesEqual(const Buffer::RawSlice* lhs, const Buffer::RawSlice* rhs,
114
0
                                 size_t num_slices) {
115
0
  for (size_t slice = 0; slice < num_slices; slice++) {
116
0
    auto rhs_slice = rhs[slice];
117
0
    auto lhs_slice = lhs[slice];
118
0
    if (rhs_slice.len_ != lhs_slice.len_) {
119
0
      return false;
120
0
    }
121
0
    auto rhs_slice_data = static_cast<const uint8_t*>(rhs_slice.mem_);
122
0
    auto lhs_slice_data = static_cast<const uint8_t*>(lhs_slice.mem_);
123
0
    for (size_t offset = 0; offset < rhs_slice.len_; offset++) {
124
0
      if (rhs_slice_data[offset] != lhs_slice_data[offset]) {
125
0
        return false;
126
0
      }
127
0
    }
128
0
  }
129
0
  return true;
130
0
}
131
132
void TestUtility::feedBufferWithRandomCharacters(Buffer::Instance& buffer, uint64_t n_char,
133
0
                                                 uint64_t seed, uint64_t n_slice) {
134
0
  const std::string sample = "Neque porro quisquam est qui dolorem ipsum..";
135
0
  std::mt19937 generate(seed);
136
0
  std::uniform_int_distribution<> distribute(1, sample.length() - 1);
137
0
  std::string str{};
138
0
  for (uint64_t n = 0; n < n_char; ++n) {
139
0
    str += sample.at(distribute(generate));
140
0
  }
141
0
  for (uint64_t n = 0; n < n_slice; ++n) {
142
0
    buffer.add(str);
143
0
  }
144
0
}
145
146
8.98k
Stats::CounterSharedPtr TestUtility::findCounter(Stats::Store& store, const std::string& name) {
147
8.98k
  return findByName(store.counters(), name);
148
8.98k
}
149
150
874
Stats::GaugeSharedPtr TestUtility::findGauge(Stats::Store& store, const std::string& name) {
151
874
  return findByName(store.gauges(), name);
152
874
}
153
154
Stats::TextReadoutSharedPtr TestUtility::findTextReadout(Stats::Store& store,
155
0
                                                         const std::string& name) {
156
0
  return findByName(store.textReadouts(), name);
157
0
}
158
159
Stats::ParentHistogramSharedPtr TestUtility::findHistogram(Stats::Store& store,
160
0
                                                           const std::string& name) {
161
0
  return findByName(store.histograms(), name);
162
0
}
163
164
AssertionResult TestUtility::waitForCounterEq(Stats::Store& store, const std::string& name,
165
                                              uint64_t value, Event::TestTimeSystem& time_system,
166
                                              std::chrono::milliseconds timeout,
167
550
                                              Event::Dispatcher* dispatcher) {
168
550
  Event::TestTimeSystem::RealTimeBound bound(timeout);
169
562
  while (findCounter(store, name) == nullptr || findCounter(store, name)->value() != value) {
170
12
    time_system.advanceTimeWait(std::chrono::milliseconds(10));
171
12
    if (timeout != std::chrono::milliseconds::zero() && !bound.withinBound()) {
172
0
      std::string current_value;
173
0
      if (findCounter(store, name)) {
174
0
        current_value = absl::StrCat(findCounter(store, name)->value());
175
0
      } else {
176
0
        current_value = "nil";
177
0
      }
178
0
      return AssertionFailure() << fmt::format(
179
0
                 "timed out waiting for {} to be {}, current value {}", name, value, current_value);
180
0
    }
181
12
    if (dispatcher != nullptr) {
182
0
      dispatcher->run(Event::Dispatcher::RunType::NonBlock);
183
0
    }
184
12
  }
185
550
  return AssertionSuccess();
186
550
}
187
188
AssertionResult TestUtility::waitForCounterGe(Stats::Store& store, const std::string& name,
189
                                              uint64_t value, Event::TestTimeSystem& time_system,
190
0
                                              std::chrono::milliseconds timeout) {
191
0
  Event::TestTimeSystem::RealTimeBound bound(timeout);
192
0
  while (findCounter(store, name) == nullptr || findCounter(store, name)->value() < value) {
193
0
    time_system.advanceTimeWait(std::chrono::milliseconds(10));
194
0
    if (timeout != std::chrono::milliseconds::zero() && !bound.withinBound()) {
195
0
      return AssertionFailure() << fmt::format("timed out waiting for {} to be >= {}", name, value);
196
0
    }
197
0
  }
198
0
  return AssertionSuccess();
199
0
}
200
201
AssertionResult TestUtility::waitForGaugeGe(Stats::Store& store, const std::string& name,
202
                                            uint64_t value, Event::TestTimeSystem& time_system,
203
0
                                            std::chrono::milliseconds timeout) {
204
0
  Event::TestTimeSystem::RealTimeBound bound(timeout);
205
0
  while (findGauge(store, name) == nullptr || findGauge(store, name)->value() < value) {
206
0
    time_system.advanceTimeWait(std::chrono::milliseconds(10));
207
0
    if (timeout != std::chrono::milliseconds::zero() && !bound.withinBound()) {
208
0
      return AssertionFailure() << fmt::format("timed out waiting for {} to be {}", name, value);
209
0
    }
210
0
  }
211
0
  return AssertionSuccess();
212
0
}
213
214
AssertionResult TestUtility::waitForGaugeEq(Stats::Store& store, const std::string& name,
215
                                            uint64_t value, Event::TestTimeSystem& time_system,
216
402
                                            std::chrono::milliseconds timeout) {
217
402
  Event::TestTimeSystem::RealTimeBound bound(timeout);
218
416
  while (findGauge(store, name) == nullptr || findGauge(store, name)->value() != value) {
219
14
    time_system.advanceTimeWait(std::chrono::milliseconds(10));
220
14
    if (timeout != std::chrono::milliseconds::zero() && !bound.withinBound()) {
221
0
      std::string current_value;
222
0
      if (findGauge(store, name)) {
223
0
        current_value = absl::StrCat(findGauge(store, name)->value());
224
0
      } else {
225
0
        current_value = "nil";
226
0
      }
227
0
      return AssertionFailure() << fmt::format(
228
0
                 "timed out waiting for {} to be {}, current value {}", name, value, current_value);
229
0
    }
230
14
  }
231
402
  return AssertionSuccess();
232
402
}
233
234
AssertionResult TestUtility::waitForProactiveOverloadResourceUsageEq(
235
    Server::ThreadLocalOverloadState& overload_state,
236
    const Server::OverloadProactiveResourceName resource_name, int64_t expected_value,
237
    Event::TestTimeSystem& time_system, Event::Dispatcher& dispatcher,
238
0
    std::chrono::milliseconds timeout) {
239
0
  Event::TestTimeSystem::RealTimeBound bound(timeout);
240
0
  const auto& monitor = overload_state.getProactiveResourceMonitorForTest(resource_name);
241
0
  while (monitor->currentResourceUsage() != expected_value) {
242
0
    time_system.advanceTimeWait(std::chrono::milliseconds(10));
243
0
    if (timeout != std::chrono::milliseconds::zero() && !bound.withinBound()) {
244
0
      uint64_t current_value;
245
0
      current_value = monitor->currentResourceUsage();
246
0
      return AssertionFailure() << fmt::format(
247
0
                 "timed out waiting for proactive resource to be {}, current value {}",
248
0
                 expected_value, current_value);
249
0
    }
250
0
    dispatcher.run(Event::Dispatcher::RunType::NonBlock);
251
0
  }
252
0
  return AssertionSuccess();
253
0
}
254
255
AssertionResult TestUtility::waitForGaugeDestroyed(Stats::Store& store, const std::string& name,
256
0
                                                   Event::TestTimeSystem& time_system) {
257
0
  while (findGauge(store, name) != nullptr) {
258
0
    time_system.advanceTimeWait(std::chrono::milliseconds(10));
259
0
  }
260
0
  return AssertionSuccess();
261
0
}
262
263
AssertionResult TestUtility::waitForNumHistogramSamplesGe(Stats::Store& store,
264
                                                          const std::string& name,
265
                                                          uint64_t min_sample_count_required,
266
                                                          Event::TestTimeSystem& time_system,
267
                                                          Event::Dispatcher& main_dispatcher,
268
0
                                                          std::chrono::milliseconds timeout) {
269
0
  Event::TestTimeSystem::RealTimeBound bound(timeout);
270
0
  while (true) {
271
0
    auto histo = findByName<Stats::ParentHistogramSharedPtr>(store.histograms(), name);
272
0
    if (histo) {
273
0
      uint64_t sample_count = readSampleCount(main_dispatcher, *histo);
274
0
      if (sample_count >= min_sample_count_required) {
275
0
        break;
276
0
      }
277
0
    }
278
279
0
    time_system.advanceTimeWait(std::chrono::milliseconds(10));
280
281
0
    if (timeout != std::chrono::milliseconds::zero() && !bound.withinBound()) {
282
0
      return AssertionFailure() << fmt::format("timed out waiting for {} to have {} samples", name,
283
0
                                               min_sample_count_required);
284
0
    }
285
0
  }
286
0
  return AssertionSuccess();
287
0
}
288
289
AssertionResult TestUtility::waitUntilHistogramHasSamples(Stats::Store& store,
290
                                                          const std::string& name,
291
                                                          Event::TestTimeSystem& time_system,
292
                                                          Event::Dispatcher& main_dispatcher,
293
0
                                                          std::chrono::milliseconds timeout) {
294
0
  return waitForNumHistogramSamplesGe(store, name, 1, time_system, main_dispatcher, timeout);
295
0
}
296
297
uint64_t TestUtility::readSampleCount(Event::Dispatcher& main_dispatcher,
298
0
                                      const Stats::ParentHistogram& histogram) {
299
  // Note: we need to read the sample count from the main thread, to avoid data races.
300
0
  uint64_t sample_count = 0;
301
0
  absl::Notification notification;
302
303
0
  main_dispatcher.post([&] {
304
0
    sample_count = histogram.cumulativeStatistics().sampleCount();
305
0
    notification.Notify();
306
0
  });
307
0
  notification.WaitForNotification();
308
309
0
  return sample_count;
310
0
}
311
312
double TestUtility::readSampleSum(Event::Dispatcher& main_dispatcher,
313
0
                                  const Stats::ParentHistogram& histogram) {
314
  // Note: we need to read the sample count from the main thread, to avoid data races.
315
0
  double sample_sum = 0;
316
0
  absl::Notification notification;
317
318
0
  main_dispatcher.post([&] {
319
0
    sample_sum = histogram.cumulativeStatistics().sampleSum();
320
0
    notification.Notify();
321
0
  });
322
0
  notification.WaitForNotification();
323
324
0
  return sample_sum;
325
0
}
326
327
std::list<Network::DnsResponse>
328
0
TestUtility::makeDnsResponse(const std::list<std::string>& addresses, std::chrono::seconds ttl) {
329
0
  std::list<Network::DnsResponse> ret;
330
0
  for (const auto& address : addresses) {
331
0
    ret.emplace_back(
332
0
        Network::DnsResponse(Network::Utility::parseInternetAddressNoThrow(address), ttl));
333
0
  }
334
0
  return ret;
335
0
}
336
337
0
std::vector<std::string> TestUtility::listFiles(const std::string& path, bool recursive) {
338
0
  std::vector<std::string> file_names;
339
0
  Filesystem::Directory directory(path);
340
0
  for (const Filesystem::DirectoryEntry& entry : directory) {
341
0
    std::string file_name = fmt::format("{}/{}", path, entry.name_);
342
0
    if (entry.type_ == Filesystem::FileType::Directory) {
343
0
      if (recursive && entry.name_ != "." && entry.name_ != "..") {
344
0
        std::vector<std::string> more_file_names = listFiles(file_name, recursive);
345
0
        file_names.insert(file_names.end(), more_file_names.begin(), more_file_names.end());
346
0
      }
347
0
    } else { // regular file
348
0
      file_names.push_back(file_name);
349
0
    }
350
0
  }
351
0
  return file_names;
352
0
}
353
354
1.96k
std::string TestUtility::uniqueFilename(absl::string_view prefix) {
355
1.96k
  return absl::StrCat(prefix, "_", getpid(), "_",
356
1.96k
                      std::chrono::system_clock::now().time_since_epoch().count());
357
1.96k
}
358
359
0
std::string TestUtility::addLeftAndRightPadding(absl::string_view to_pad, int desired_length) {
360
0
  int line_fill_len = desired_length - to_pad.length();
361
0
  int first_half_len = line_fill_len / 2;
362
0
  int second_half_len = line_fill_len - first_half_len;
363
0
  return absl::StrCat(std::string(first_half_len, '='), to_pad, std::string(second_half_len, '='));
364
0
}
365
366
0
std::vector<std::string> TestUtility::split(const std::string& source, char split) {
367
0
  return TestUtility::split(source, std::string{split});
368
0
}
369
370
std::vector<std::string> TestUtility::split(const std::string& source, const std::string& split,
371
0
                                            bool keep_empty_string) {
372
0
  std::vector<std::string> ret;
373
0
  const auto tokens_sv = StringUtil::splitToken(source, split, keep_empty_string);
374
0
  std::transform(tokens_sv.begin(), tokens_sv.end(), std::back_inserter(ret),
375
0
                 [](absl::string_view sv) { return std::string(sv); });
376
0
  return ret;
377
0
}
378
379
// static
380
0
absl::Time TestUtility::parseTime(const std::string& input, const std::string& input_format) {
381
0
  absl::Time time;
382
0
  std::string parse_error;
383
0
  EXPECT_TRUE(absl::ParseTime(input_format, input, &time, &parse_error))
384
0
      << " error \"" << parse_error << "\" from failing to parse timestamp \"" << input
385
0
      << "\" with format string \"" << input_format << "\"";
386
0
  return time;
387
0
}
388
389
// static
390
0
std::string TestUtility::formatTime(const absl::Time input, const std::string& output_format) {
391
0
  static const absl::TimeZone utc = absl::UTCTimeZone();
392
0
  return absl::FormatTime(output_format, input, utc);
393
0
}
394
395
// static
396
0
std::string TestUtility::formatTime(const SystemTime input, const std::string& output_format) {
397
0
  return TestUtility::formatTime(absl::FromChrono(input), output_format);
398
0
}
399
400
// static
401
std::string TestUtility::convertTime(const std::string& input, const std::string& input_format,
402
0
                                     const std::string& output_format) {
403
0
  return TestUtility::formatTime(TestUtility::parseTime(input, input_format), output_format);
404
0
}
405
406
// static
407
0
std::string TestUtility::nonZeroedGauges(const std::vector<Stats::GaugeSharedPtr>& gauges) {
408
  // Returns all gauges that are 0 except the circuit_breaker remaining resource
409
  // gauges which default to the resource max.
410
0
  std::regex omitted(".*circuit_breakers\\..*\\.remaining.*");
411
0
  std::string non_zero;
412
0
  for (const Stats::GaugeSharedPtr& gauge : gauges) {
413
0
    if (!std::regex_match(gauge->name(), omitted) && gauge->value() != 0) {
414
0
      non_zero.append(fmt::format("{}: {}; ", gauge->name(), gauge->value()));
415
0
    }
416
0
  }
417
0
  return non_zero;
418
0
}
419
420
// static
421
0
bool TestUtility::gaugesZeroed(const std::vector<Stats::GaugeSharedPtr>& gauges) {
422
0
  return nonZeroedGauges(gauges).empty();
423
0
}
424
425
// static
426
bool TestUtility::gaugesZeroed(
427
0
    const std::vector<std::pair<absl::string_view, Stats::PrimitiveGaugeReference>>& gauges) {
428
  // Returns true if all gauges are 0 except the circuit_breaker remaining resource
429
  // gauges which default to the resource max.
430
0
  std::regex omitted(".*circuit_breakers\\..*\\.remaining.*");
431
0
  for (const auto& gauge : gauges) {
432
0
    if (!std::regex_match(std::string(gauge.first), omitted) && gauge.second.get().value() != 0) {
433
0
      return false;
434
0
    }
435
0
  }
436
0
  return true;
437
0
}
438
439
3.96k
void ConditionalInitializer::setReady() {
440
3.96k
  absl::MutexLock lock(&mutex_);
441
3.96k
  EXPECT_FALSE(ready_);
442
3.96k
  ready_ = true;
443
3.96k
}
444
445
3.96k
void ConditionalInitializer::waitReady() {
446
3.96k
  absl::MutexLock lock(&mutex_);
447
3.96k
  if (ready_) {
448
0
    ready_ = false;
449
0
    return;
450
0
  }
451
452
3.96k
  mutex_.Await(absl::Condition(&ready_));
453
3.96k
  EXPECT_TRUE(ready_);
454
3.96k
  ready_ = false;
455
3.96k
}
456
457
0
void ConditionalInitializer::wait() {
458
0
  absl::MutexLock lock(&mutex_);
459
0
  mutex_.Await(absl::Condition(&ready_));
460
0
  EXPECT_TRUE(ready_);
461
0
}
462
463
namespace Api {
464
465
class TestImplProvider {
466
protected:
467
  Event::GlobalTimeSystem global_time_system_;
468
  testing::NiceMock<Stats::MockIsolatedStatsStore> default_stats_store_;
469
  testing::NiceMock<Random::MockRandomGenerator> mock_random_generator_;
470
  envoy::config::bootstrap::v3::Bootstrap empty_bootstrap_;
471
};
472
473
class TestImpl : public TestImplProvider, public Impl {
474
public:
475
  TestImpl(Thread::ThreadFactory& thread_factory, Filesystem::Instance& file_system,
476
           Stats::Store* stats_store = nullptr, Event::TimeSystem* time_system = nullptr,
477
           Random::RandomGenerator* random = nullptr)
478
      : Impl(thread_factory, stats_store ? *stats_store : default_stats_store_,
479
             time_system ? *time_system : global_time_system_, file_system,
480
54.8k
             random ? *random : mock_random_generator_, empty_bootstrap_) {}
481
};
482
483
50.8k
ApiPtr createApiForTest() {
484
50.8k
  return std::make_unique<TestImpl>(Thread::threadFactoryForTest(),
485
50.8k
                                    Filesystem::fileSystemForTest());
486
50.8k
}
487
488
0
ApiPtr createApiForTest(Filesystem::Instance& filesystem) {
489
0
  return std::make_unique<TestImpl>(Thread::threadFactoryForTest(), filesystem);
490
0
}
491
492
0
ApiPtr createApiForTest(Random::RandomGenerator& random) {
493
0
  return std::make_unique<TestImpl>(Thread::threadFactoryForTest(), Filesystem::fileSystemForTest(),
494
0
                                    nullptr, nullptr, &random);
495
0
}
496
497
1.99k
ApiPtr createApiForTest(Stats::Store& stat_store) {
498
1.99k
  return std::make_unique<TestImpl>(Thread::threadFactoryForTest(), Filesystem::fileSystemForTest(),
499
1.99k
                                    &stat_store);
500
1.99k
}
501
502
0
ApiPtr createApiForTest(Stats::Store& stat_store, Random::RandomGenerator& random) {
503
0
  return std::make_unique<TestImpl>(Thread::threadFactoryForTest(), Filesystem::fileSystemForTest(),
504
0
                                    &stat_store, nullptr, &random);
505
0
}
506
507
2
ApiPtr createApiForTest(Event::TimeSystem& time_system) {
508
2
  return std::make_unique<TestImpl>(Thread::threadFactoryForTest(), Filesystem::fileSystemForTest(),
509
2
                                    nullptr, &time_system);
510
2
}
511
512
1.97k
ApiPtr createApiForTest(Stats::Store& stat_store, Event::TimeSystem& time_system) {
513
1.97k
  return std::make_unique<TestImpl>(Thread::threadFactoryForTest(), Filesystem::fileSystemForTest(),
514
1.97k
                                    &stat_store, &time_system);
515
1.97k
}
516
517
} // namespace Api
518
} // namespace Envoy