-
Notifications
You must be signed in to change notification settings - Fork 342
/
Copy pathtest_ageing.cpp
205 lines (164 loc) · 6.18 KB
/
test_ageing.cpp
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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
/* Copyright 2013-present Barefoot Networks, Inc.
*
* 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
*
* http://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.
*/
/*
* Antonin Bas ([email protected])
*
*/
#include <gtest/gtest.h>
#include <bm/bm_sim/ageing.h>
#include <bm/bm_sim/match_tables.h>
#include <memory>
#include <string>
#include <mutex>
#include <thread>
#include <condition_variable>
#include <chrono>
#include <vector>
#include <cassert>
#include "utils.h"
using namespace bm;
using std::chrono::milliseconds;
using std::chrono::duration_cast;
using std::this_thread::sleep_for;
// Google Test fixture for learning tests
class AgeingTest : public ::testing::Test {
protected:
using clock = std::chrono::high_resolution_clock;
protected:
PHVFactory phv_factory;
const device_id_t device_id = 0;
const cxt_id_t cxt_id = 0;
MatchKeyBuilder key_builder;
std::unique_ptr<MatchTable> table;
HeaderType testHeaderType;
header_id_t testHeader1{0}, testHeader2{1};
ActionFn action_fn;
std::shared_ptr<MemoryAccessor> ageing_writer;
char buffer[4096];
std::unique_ptr<AgeingMonitorIface> ageing_monitor;
clock::time_point start;
std::unique_ptr<PHVSourceIface> phv_source{nullptr};
AgeingTest()
: testHeaderType("test_t", 0),
action_fn("actionA", 0, 0),
ageing_writer(new MemoryAccessor(4096)),
phv_source(PHVSourceIface::make_phv_source()) {
testHeaderType.push_back_field("f16", 16);
testHeaderType.push_back_field("f48", 48);
phv_factory.push_back_header("test1", testHeader1, testHeaderType);
phv_factory.push_back_header("test2", testHeader2, testHeaderType);
key_builder.push_back_field(testHeader1, 0, 16, MatchKeyParam::Type::EXACT);
using MUExact = MatchUnitExact<ActionEntry>;
LookupStructureFactory factory;
std::unique_ptr<MUExact> match_unit(new MUExact(1, key_builder, &factory));
// counters disabled, ageing enabled
table = std::unique_ptr<MatchTable>(
new MatchTable("test_table", 0, std::move(match_unit), false, true));
table->set_next_node(0, nullptr);
}
virtual void SetUp() {
phv_source->set_phv_factory(0, &phv_factory);
start = clock::now();
}
// virtual void TearDown() { }
Packet get_pkt() {
// dummy packet, won't be parsed
Packet packet = Packet::make_new(128, PacketBuffer(256), phv_source.get());
packet.get_phv()->get_header(testHeader1).mark_valid();
packet.get_phv()->get_header(testHeader2).mark_valid();
// return std::move(packet);
// enable NVRO
return packet;
}
MatchErrorCode add_entry(const std::string &key, entry_handle_t *handle,
unsigned int ttl_ms) {
ActionData action_data;
std::vector<MatchKeyParam> match_key;
match_key.emplace_back(MatchKeyParam::Type::EXACT, key);
MatchErrorCode rc;
rc = table->add_entry(match_key, &action_fn, action_data, handle);
if (rc != MatchErrorCode::SUCCESS) return rc;
return table->set_entry_ttl(*handle, ttl_ms);
}
MatchErrorCode delete_entry(entry_handle_t handle) {
return table->delete_entry(handle);
}
bool send_pkt(const std::string &key, entry_handle_t *handle) {
Packet pkt = get_pkt();
Field &f = pkt.get_phv()->get_field(this->testHeader1, 0);
f.set(key);
bool hit;
const ControlFlowNode *next_node;
(void) next_node;
// lookup is enough to update timestamp (done in match unit),
// no need for apply_action
table->lookup(pkt, &hit, handle, &next_node);
return hit;
}
void init_monitor(unsigned int sweep_int) {
ageing_monitor = AgeingMonitorIface::make(
device_id, cxt_id, ageing_writer, sweep_int);
ageing_monitor->add_table(table.get());
}
};
TEST_F(AgeingTest, OneNotification) {
std::string key_("\x0a\xba");
std::string key("0x0aba");
entry_handle_t handle_1;
entry_handle_t lookup_handle;
unsigned int sweep_int = 200u; // use it as timeout too
init_monitor(sweep_int);
ASSERT_EQ(MatchErrorCode::SUCCESS, add_entry(key_, &handle_1, sweep_int));
ASSERT_NE(MemoryAccessor::Status::CAN_READ, ageing_writer->check_status());
sleep_for(milliseconds(150));
ASSERT_NE(MemoryAccessor::Status::CAN_READ, ageing_writer->check_status());
auto tp1 = clock::now();
ASSERT_TRUE(send_pkt(key, &lookup_handle));
ageing_writer->read(buffer, sizeof(buffer));
auto tp2 = clock::now();
unsigned int elapsed = duration_cast<milliseconds>(tp2 - tp1).count();
ASSERT_GT(elapsed, sweep_int - 20u);
ASSERT_LT(elapsed, 2 * sweep_int + 20u);
// delete and make sure we do not get a new notification
ASSERT_EQ(MatchErrorCode::SUCCESS, delete_entry(handle_1));
sleep_for(milliseconds(2 * sweep_int + 100u));
ASSERT_NE(MemoryAccessor::Status::CAN_READ, ageing_writer->check_status());
}
TEST_F(AgeingTest, NoDuplicate) {
std::string key_("\x0a\xba");
std::string key("0x0aba");
entry_handle_t handle_1;
unsigned int sweep_int = 200u;
init_monitor(sweep_int);
auto tp1 = clock::now();
ASSERT_EQ(MatchErrorCode::SUCCESS, add_entry(key_, &handle_1, sweep_int));
ageing_writer->read(buffer, sizeof(buffer));
auto tp2 = clock::now();
unsigned int elapsed = duration_cast<milliseconds>(tp2 - tp1).count();
ASSERT_GT(elapsed, sweep_int - 20u);
ASSERT_LT(elapsed, 2 * sweep_int + 20u);
auto tp3 = clock::now();
ageing_writer->read(buffer, sizeof(buffer));
auto tp4 = clock::now();
// we make sure that the next sweep does not generate a message
elapsed = duration_cast<milliseconds>(tp4 - tp3).count();
ASSERT_GT(elapsed, (unsigned int) (sweep_int * 1.5));
}
TEST_F(AgeingTest, GetTableNameFromId) {
unsigned int sweep_int = 200u;
init_monitor(sweep_int);
EXPECT_EQ(ageing_monitor->get_table_name_from_id(0), "test_table");
EXPECT_EQ(ageing_monitor->get_table_name_from_id(1), "");
}