File: limiter_test.cpp

package info (click to toggle)
dd-opentracing-cpp 1.3.7-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 3,336 kB
  • sloc: cpp: 44,895; sh: 697; ansic: 27; makefile: 20
file content (132 lines) | stat: -rw-r--r-- 3,986 bytes parent folder | download | duplicates (2)
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
#include "../src/limiter.h"

#include <catch2/catch.hpp>

#include "mocks.h"
using namespace datadog::opentracing;

TEST_CASE("limiter") {
  // Starting calendar time 2007-03-12 00:00:00
  std::tm start{};
  start.tm_mday = 12;
  start.tm_mon = 2;
  start.tm_year = 107;
  TimePoint time{std::chrono::system_clock::from_time_t(timegm(&start)),
                 std::chrono::steady_clock::time_point{}};
  TimeProvider get_time = [&time]() { return time; };  // Mock clock.

  SECTION("limits requests") {
    Limiter lim(get_time, 1, 1.0, 1);
    auto first = lim.allow();
    auto second = lim.allow();
    REQUIRE(first.allowed);
    REQUIRE(!second.allowed);
  }

  SECTION("refreshes over time") {
    Limiter lim(get_time, 1, 1.0, 1);
    auto first = lim.allow();
    auto second = lim.allow();
    advanceTime(time, std::chrono::seconds(1));
    auto third = lim.allow();
    REQUIRE(first.allowed);
    REQUIRE(!second.allowed);
    REQUIRE(third.allowed);
  }

  SECTION("handles long intervals correctly") {
    Limiter lim(get_time, 1, 1.0, 1);
    auto first = lim.allow();
    advanceTime(time, std::chrono::seconds(2));
    auto second = lim.allow();
    auto third = lim.allow();
    REQUIRE(first.allowed);
    REQUIRE(second.allowed);
    REQUIRE(!third.allowed);
  }

  SECTION("calculates effective rate") {
    // starts off at 1.0, and decreases if nothing happens
    Limiter lim(get_time, 1, 1.0, 1);
    auto first = lim.allow();
    REQUIRE(first.allowed);
    REQUIRE(first.effective_rate == 1.0);
    auto second = lim.allow();
    REQUIRE(!second.allowed);
    REQUIRE(second.effective_rate == 0.95);
    // if 10 seconds pass, then the effective rate gets reset, so it should be
    // 9 seconds of 1.0 and one second of 1.0
    advanceTime(time, std::chrono::seconds(10));
    auto third = lim.allow();
    REQUIRE(third.allowed);
    REQUIRE(third.effective_rate == 1.0);
  }

  SECTION("updates tokens at sub-second intervals") {
    Limiter lim(get_time, 5, 5.0, 1);  // replace tokens @ 5.0 per second (i.e. every 0.2 seconds)
    // consume all the tokens first
    for (auto i = 0; i < 5; i++) {
      auto result = lim.allow();
      REQUIRE(result.allowed);
    }
    auto all_consumed = lim.allow();
    REQUIRE(!all_consumed.allowed);

    advanceTime(time, std::chrono::milliseconds(200));
    auto first = lim.allow();
    auto second = lim.allow();
    REQUIRE(first.allowed);
    REQUIRE(!second.allowed);  // only one token after 0.2s

    // refills to maximum, and can consume 5 tokens
    advanceTime(time, std::chrono::seconds(1));
    for (auto i = 0; i < 5; i++) {
      auto result = lim.allow();
      REQUIRE(result.allowed);
    }
    all_consumed = lim.allow();
    REQUIRE(!all_consumed.allowed);
  }

  SECTION("updates tokens at multi-second intervals") {
    Limiter lim(get_time, 1, 0.25, 1);  // replace tokens @ 0.25 per second (i.e. every 4 seconds)

    // 0 seconds (0s)
    auto result = lim.allow();
    REQUIRE(result.allowed);

    for (int i = 0; i < 3; ++i) {
      // 1s, 2s, 3s... still haven't released a token
      advanceTime(time, std::chrono::seconds(1));
      result = lim.allow();
      REQUIRE(!result.allowed);
    }

    // 4s... one token was just released
    advanceTime(time, std::chrono::seconds(1));
    result = lim.allow();
    REQUIRE(result.allowed);

    // still 4s... and we used that token already
    result = lim.allow();
    REQUIRE(!result.allowed);
  }

  SECTION("dedicated constructor configures based on desired allowed-per-second") {
    const double per_second = 23.97;
    Limiter lim(get_time, per_second);
    for (int i = 0; i < 24; ++i) {
      auto result = lim.allow();
      REQUIRE(result.allowed);
    }

    auto result = lim.allow();
    REQUIRE(!result.allowed);

    advanceTime(time, std::chrono::milliseconds(int(1 / per_second * 1000) + 1));
    result = lim.allow();
    REQUIRE(result.allowed);
    result = lim.allow();
    REQUIRE(!result.allowed);
  }
}