@@ -20,6 +20,7 @@ TEST_CASE("InterfaceLog dump: full (req+rsp)", "[rpc][common][interface_log]") {
20
20
.enabled = true ,
21
21
.ifc_name = " eth_rpc" ,
22
22
.container_folder = tmp_dir,
23
+ .auto_flush = false ,
23
24
.dump_response = true ,
24
25
};
25
26
auto ifc_log{std::make_unique<InterfaceLog>(settings)};
@@ -62,6 +63,7 @@ TEST_CASE("InterfaceLog dump: default (only req)", "[rpc][common][interface_log]
62
63
.enabled = true ,
63
64
.ifc_name = " eth_rpc" ,
64
65
.container_folder = tmp_dir,
66
+ .auto_flush = false ,
65
67
};
66
68
auto ifc_log{std::make_unique<InterfaceLog>(settings)};
67
69
REQUIRE (!ifc_log->path ().empty ());
@@ -94,4 +96,191 @@ TEST_CASE("InterfaceLog dump: default (only req)", "[rpc][common][interface_log]
94
96
CHECK (log_ifstream.eof ());
95
97
}
96
98
99
+ TEST_CASE (" InterfaceLog dump: two instances w/o auto-flush" , " [rpc][common][interface_log]" ) {
100
+ const auto tmp_dir{TemporaryDirectory::get_unique_temporary_path ()};
101
+ InterfaceLogSettings settings{
102
+ .enabled = true ,
103
+ .ifc_name = " eth_rpc" ,
104
+ .container_folder = tmp_dir,
105
+ .auto_flush = false ,
106
+ };
107
+ auto ifc_log1 = std::make_unique<InterfaceLog>(settings);
108
+ auto ifc_log2 = std::make_unique<InterfaceLog>(settings);
109
+ REQUIRE (ifc_log1->path () == ifc_log2->path ());
110
+
111
+ std::ifstream log_ifstream{ifc_log1->path ().string ()};
112
+
113
+ static constexpr size_t kLogBufferSize {1024 * 4 };
114
+ static const size_t kLogLineHeaderSize {InterfaceLog::kLogLineHeaderSize };
115
+
116
+ std::string request1;
117
+ std::string request2 (100 , ' B' ); // always less than page size
118
+
119
+ SECTION (" less than page size" ) {
120
+ request1.assign (kLogBufferSize - kLogLineHeaderSize - 1 /* \n*/ - 1 , ' A' );
121
+
122
+ SECTION (" same instance" ) {
123
+ // Logging request1 is NOT sufficient to trigger page write w/o flush
124
+ ifc_log1->log_req (request1);
125
+ CHECK (std::filesystem::file_size (ifc_log1->path ()) == 0 );
126
+
127
+ // Logging request2 crosses the page size, so one page is written w/o flush
128
+ ifc_log1->log_req (request2);
129
+ CHECK (std::filesystem::file_size (ifc_log1->path ()) == kLogBufferSize );
130
+
131
+ // Flushing writes the whole write buffer content
132
+ ifc_log1->flush ();
133
+
134
+ // Log file content is exactly log_line_header + request1 + log_line_header + request2
135
+ std::string content;
136
+ std::getline (log_ifstream, content);
137
+ CHECK (content.substr (kLogLineHeaderSize ) == request1);
138
+ std::getline (log_ifstream, content);
139
+ CHECK (content.substr (kLogLineHeaderSize ) == request2);
140
+ }
141
+ SECTION (" different instances" ) {
142
+ // Logging request1 through ifc_log1 is NOT sufficient to trigger page write w/o flush
143
+ ifc_log1->log_req (request1);
144
+ CHECK (std::filesystem::file_size (ifc_log1->path ()) == 0 );
145
+
146
+ // Logging request2 through ifc_log2 DOES NOT trigger page write: write buffers are separate
147
+ ifc_log2->log_req (request2);
148
+ CHECK (std::filesystem::file_size (ifc_log2->path ()) == 0 );
149
+
150
+ // Flushing both instances dumps the separate write buffers in order
151
+ ifc_log2->flush ();
152
+ ifc_log1->flush ();
153
+
154
+ // Log file content is exactly log_line_header + request2 + log_line_header + request1
155
+ std::string content;
156
+ std::getline (log_ifstream, content);
157
+ CHECK (content.substr (kLogLineHeaderSize ) == request2);
158
+ std::getline (log_ifstream, content);
159
+ CHECK (content.substr (kLogLineHeaderSize ) == request1);
160
+ }
161
+ // No other content is present
162
+ CHECK (log_ifstream.get () == -1 );
163
+ CHECK (log_ifstream.eof ());
164
+
165
+ CHECK (std::filesystem::file_size (ifc_log1->path ()) ==
166
+ (request1.size () + kLogLineHeaderSize + 1 ) +
167
+ (request2.size () + kLogLineHeaderSize + 1 ));
168
+ }
169
+
170
+ SECTION (" greater than or equal to page size" ) {
171
+ request1.assign (4096 - kLogLineHeaderSize + 1 , ' A' );
172
+
173
+ SECTION (" same instance" ) {
174
+ // Logging request1 is sufficient to trigger page write w/o flush
175
+ ifc_log1->log_req (request1);
176
+ CHECK (std::filesystem::file_size (ifc_log1->path ()) == kLogBufferSize );
177
+
178
+ // Logging request2 DOES NOT trigger another page write
179
+ ifc_log1->log_req (request2);
180
+ CHECK (std::filesystem::file_size (ifc_log1->path ()) == kLogBufferSize );
181
+
182
+ // Flushing writes the whole write buffer content
183
+ ifc_log1->flush ();
184
+
185
+ // Log file content is exactly log_line_header + request1 + log_line_header + request2
186
+ std::string content;
187
+ std::getline (log_ifstream, content);
188
+ CHECK (content.substr (kLogLineHeaderSize ) == request1);
189
+ std::getline (log_ifstream, content);
190
+ CHECK (content.substr (kLogLineHeaderSize ) == request2);
191
+ }
192
+ SECTION (" different instances w/o flush: possible truncation" ) {
193
+ // Logging request1 through ifc_log1 is sufficient to trigger page write w/o flush
194
+ ifc_log1->log_req (request1);
195
+ CHECK (std::filesystem::file_size (ifc_log1->path ()) == kLogBufferSize );
196
+
197
+ // Logging request2 through ifc_log2 DOES NOT trigger another page write
198
+ ifc_log2->log_req (request2);
199
+ CHECK (std::filesystem::file_size (ifc_log2->path ()) == kLogBufferSize );
200
+
201
+ // Flushing ifc_log2 BEFORE ifc_log1 generates a mixed content: truncated request1 + request2
202
+ ifc_log2->flush ();
203
+
204
+ // Log file content is exactly log_line_header + request1 TRUNCATED AT 4k + log_line_header + request2
205
+ std::string content;
206
+ std::getline (log_ifstream, content);
207
+ CHECK (content.substr (0 , kLogLineHeaderSize ).ends_with (" REQ -> " ));
208
+ CHECK (content.substr (kLogLineHeaderSize , kLogBufferSize - kLogLineHeaderSize ) == std::string (kLogBufferSize - kLogLineHeaderSize , ' A' ));
209
+ CHECK (content.substr (kLogBufferSize , kLogLineHeaderSize ).ends_with (" REQ -> " ));
210
+ CHECK (content.substr (kLogBufferSize + kLogLineHeaderSize ) == request2);
211
+ }
212
+ SECTION (" different instances w/ flush: no truncation" ) {
213
+ // Logging request1 through ifc_log1 and flushing
214
+ ifc_log1->log_req (request1);
215
+ ifc_log1->flush ();
216
+ CHECK (std::filesystem::file_size (ifc_log1->path ()) == request1.size () + kLogLineHeaderSize + 1 );
217
+
218
+ // Logging request2 through ifc_log2 and flushing
219
+ ifc_log2->log_req (request2);
220
+ ifc_log2->flush ();
221
+ CHECK (std::filesystem::file_size (ifc_log2->path ()) ==
222
+ (request1.size () + kLogLineHeaderSize + 1 ) +
223
+ (request2.size () + kLogLineHeaderSize + 1 ));
224
+
225
+ // Log file content is exactly log_line_header + request2 + log_line_header + request1
226
+ std::string content;
227
+ std::getline (log_ifstream, content);
228
+ CHECK (content.substr (kLogLineHeaderSize ) == request1);
229
+ std::getline (log_ifstream, content);
230
+ CHECK (content.substr (kLogLineHeaderSize ) == request2);
231
+ }
232
+ // No other content is present
233
+ CHECK (log_ifstream.get () == -1 );
234
+ CHECK (log_ifstream.eof ());
235
+ }
236
+ }
237
+
238
+ TEST_CASE (" InterfaceLog dump: two instances w/ auto-flush" , " [rpc][common][interface_log]" ) {
239
+ const auto tmp_dir{TemporaryDirectory::get_unique_temporary_path ()};
240
+ InterfaceLogSettings settings{
241
+ .enabled = true ,
242
+ .ifc_name = " eth_rpc" ,
243
+ .container_folder = tmp_dir,
244
+ };
245
+ auto ifc_log1 = std::make_unique<InterfaceLog>(settings);
246
+ auto ifc_log2 = std::make_unique<InterfaceLog>(settings);
247
+ REQUIRE (ifc_log1->path () == ifc_log2->path ());
248
+
249
+ static constexpr size_t kLogBufferSize {1024 * 4 };
250
+ static const size_t kLogLineHeaderSize {InterfaceLog::kLogLineHeaderSize };
251
+
252
+ std::string request1;
253
+ std::string request2 (100 , ' B' ); // always less than page size
254
+
255
+ SECTION (" less than page size" ) {
256
+ request1.assign (kLogBufferSize - kLogLineHeaderSize - 1 /* \n*/ - 1 , ' A' );
257
+ }
258
+
259
+ SECTION (" greater than or equal to page size" ) {
260
+ request1.assign (4096 - kLogLineHeaderSize + 1 , ' A' );
261
+ }
262
+
263
+ // Logging request1 through ifc_log1 implicitly flushes
264
+ ifc_log1->log_req (request1);
265
+ CHECK (std::filesystem::file_size (ifc_log1->path ()) == request1.size () + kLogLineHeaderSize + 1 );
266
+
267
+ // Logging request2 through ifc_log2 implicitly flushes
268
+ ifc_log2->log_req (request2);
269
+ CHECK (std::filesystem::file_size (ifc_log2->path ()) ==
270
+ (request1.size () + kLogLineHeaderSize + 1 ) +
271
+ (request2.size () + kLogLineHeaderSize + 1 ));
272
+
273
+ // Log file content is exactly log_line_header + request1 + log_line_header + request2
274
+ std::ifstream log_ifstream{ifc_log1->path ().string ()};
275
+ std::string content;
276
+ std::getline (log_ifstream, content);
277
+ CHECK (content.substr (kLogLineHeaderSize ) == request1);
278
+ std::getline (log_ifstream, content);
279
+ CHECK (content.substr (kLogLineHeaderSize ) == request2);
280
+
281
+ // No other content is present
282
+ CHECK (log_ifstream.get () == -1 );
283
+ CHECK (log_ifstream.eof ());
284
+ }
285
+
97
286
} // namespace silkworm::rpc
0 commit comments