Skip to content

Commit 1b91eb9

Browse files
author
Andrei Popescu
authored
Adapt PendingUrlRequests tests to avoid timeout. (#1235)
If the PendingUrlRequests do not get proper completion they will hang on destructor, resp. CancelAllAndWait(), until the timeout of 60s expires. This happened on the unit tests which lead to massive wait on CI. This is now fixed as all pending requests get proper completion from an async thread. Resolves: OLPEDGE-2608 Signed-off-by: Andrei Popescu <[email protected]>
1 parent 32066a1 commit 1b91eb9

File tree

2 files changed

+157
-41
lines changed

2 files changed

+157
-41
lines changed

olp-cpp-sdk-core/src/client/PendingUrlRequests.cpp

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ olp::client::HttpResponse GetCancelledResponse() {
2828
return {olp::client::PendingUrlRequest::kCancelledStatus,
2929
"Operation cancelled"};
3030
}
31+
32+
std::string ToString(bool value) { return value ? "true" : "false"; }
3133
} // namespace
3234

3335
namespace olp {
@@ -38,10 +40,8 @@ size_t PendingUrlRequest::Append(NetworkAsyncCallback callback) {
3840
std::lock_guard<std::mutex> lock(mutex_);
3941
const auto callback_id = callbacks_.size();
4042

41-
OLP_SDK_LOG_DEBUG_F(
42-
kLogTag,
43-
"PendingUrlRequest::Append, callback_id=%zu, request_id=%" PRIu64,
44-
callback_id, http_request_id_);
43+
OLP_SDK_LOG_DEBUG_F(kLogTag, "Append, callback_id=%zu, request_id=%" PRIu64,
44+
callback_id, http_request_id_);
4545

4646
callbacks_.emplace(callback_id, std::move(callback));
4747
return callback_id;
@@ -58,10 +58,8 @@ bool PendingUrlRequest::ExecuteOrCancelled(const ExecuteFuncType& func,
5858
bool PendingUrlRequest::Cancel(size_t callback_id) {
5959
std::lock_guard<std::mutex> lock(mutex_);
6060

61-
OLP_SDK_LOG_DEBUG_F(
62-
kLogTag,
63-
"PendingUrlRequest::Cancel, callback_id=%zu, request_id=%" PRIu64,
64-
callback_id, http_request_id_);
61+
OLP_SDK_LOG_DEBUG_F(kLogTag, "Cancel, callback_id=%zu, request_id=%" PRIu64,
62+
callback_id, http_request_id_);
6563

6664
auto it = callbacks_.find(callback_id);
6765
if (it == callbacks_.end()) {
@@ -107,10 +105,12 @@ void PendingUrlRequest::OnRequestCompleted(HttpResponse response) {
107105
http_request_id_ = kInvalidRequestId;
108106
}
109107

110-
OLP_SDK_LOG_DEBUG_F(kLogTag,
111-
"OnRequestCompleted, request_id=%" PRIu64
112-
", callbacks=%zu, cancelled_callbacks=%zu",
113-
request_id, callbacks.size(), cancelled_callbacks.size());
108+
OLP_SDK_LOG_DEBUG_F(
109+
kLogTag,
110+
"OnRequestCompleted, request_id=%" PRIu64
111+
", cancelled='%s', callbacks=%zu, cancelled_callbacks=%zu",
112+
request_id, ToString(context_.IsCancelled()).c_str(), callbacks.size(),
113+
cancelled_callbacks.size());
114114

115115
client::HttpResponse response_out;
116116
if (!context_.IsCancelled()) {
@@ -185,6 +185,10 @@ bool PendingUrlRequests::CancelAll() {
185185
// This only cancells the ongoing Network request the callback trigger
186186
// is taking care of the Network callback.
187187
std::lock_guard<std::mutex> lock(mutex_);
188+
189+
OLP_SDK_LOG_DEBUG_F(kLogTag, "CancelAll, pending_requests=%zu",
190+
pending_requests_.size());
191+
188192
for (auto& request : pending_requests_) {
189193
request.second->CancelOperation();
190194
}
@@ -200,6 +204,12 @@ bool PendingUrlRequests::CancelAllAndWait() const {
200204
// Copy, do not move else the OnRequestCompleted callback
201205
// will not work
202206
std::lock_guard<std::mutex> lock(mutex_);
207+
208+
OLP_SDK_LOG_DEBUG_F(
209+
kLogTag,
210+
"CancelAllAndWait, pending_requests=%zu, cancelled_requests=%zu",
211+
pending_requests_.size(), cancelled_requests_.size());
212+
203213
if (pending_requests_.empty() && cancelled_requests_.empty()) {
204214
return true;
205215
}

olp-cpp-sdk-core/tests/client/PendingUrlRequestsTest.cpp

Lines changed: 135 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,11 @@ namespace http = olp::http;
4343

4444
static const std::string kGoodResponse = "Response1234";
4545
static const std::string kBadResponse = "Cancelled";
46-
static constexpr int kCancelledStatus =
46+
constexpr int kCancelledStatus =
4747
static_cast<int>(http::ErrorCode::CANCELLED_ERROR);
48+
constexpr http::RequestId kRequestId = 1234u;
49+
constexpr auto kSleepFor = std::chrono::seconds(1);
50+
constexpr auto kWaitFor = std::chrono::seconds(5);
4851

4952
client::HttpResponse GetHttpResponse(ErrorCode error, std::string status) {
5053
return client::HttpResponse(static_cast<int>(error), status);
@@ -167,22 +170,62 @@ TEST(PendingUrlRequestsTest, IsCancelled) {
167170
const std::string url1 = "url1";
168171
const std::string url2 = "url2";
169172

173+
auto check_cancelled = [&](client::HttpResponse response) {
174+
ASSERT_EQ(response.GetStatus(), kCancelledStatus);
175+
};
176+
177+
auto check_not_cancelled = [&](client::HttpResponse response) {
178+
ASSERT_NE(response.GetStatus(), kCancelledStatus);
179+
};
180+
181+
auto complete_call = [&](http::RequestId request_id, const std::string& url) {
182+
std::this_thread::sleep_for(kSleepFor);
183+
pending_requests.OnRequestCompleted(
184+
request_id, url,
185+
GetHttpResponse(http::HttpStatusCode::OK, kGoodResponse,
186+
{{"header1", "value1"}, {"header2", "value2"}}));
187+
};
188+
170189
{
171190
SCOPED_TRACE("Cancel one request");
172191

173192
auto request_valid = pending_requests[url1];
174193
auto request_cancelled = pending_requests[url2];
175194
ASSERT_TRUE(request_valid && request_cancelled);
176195

177-
request_valid->Append([&](client::HttpResponse) {});
178-
auto request_id = request_cancelled->Append([&](client::HttpResponse) {});
196+
request_valid->Append(check_not_cancelled);
197+
auto request_id = request_cancelled->Append(check_cancelled);
179198

180199
ASSERT_FALSE(request_valid->IsCancelled());
181200
ASSERT_FALSE(request_cancelled->IsCancelled());
182201

202+
std::future<void> future1, future2;
203+
204+
request_valid->ExecuteOrCancelled([&](http::RequestId& id) {
205+
id = kRequestId;
206+
future1 = std::async(std::launch::async, complete_call, id, url1);
207+
return client::CancellationToken([] {
208+
// Should not be called
209+
EXPECT_TRUE(false) << "Cancellation called on not cancelled request";
210+
});
211+
});
212+
213+
request_cancelled->ExecuteOrCancelled([&](http::RequestId& id) {
214+
id = kRequestId + 1;
215+
return client::CancellationToken([&] {
216+
future2 = std::async(std::launch::async, complete_call, id, url2);
217+
});
218+
});
219+
183220
EXPECT_TRUE(pending_requests.Cancel(url2, request_id));
184221
EXPECT_FALSE(request_valid->IsCancelled());
185222
EXPECT_TRUE(request_cancelled->IsCancelled());
223+
224+
ASSERT_EQ(future1.wait_for(kWaitFor), std::future_status::ready);
225+
ASSERT_EQ(future2.wait_for(kWaitFor), std::future_status::ready);
226+
227+
// Cancell all and wait to leave a clean state for the next scope
228+
ASSERT_TRUE(pending_requests.CancelAllAndWait());
186229
}
187230

188231
{
@@ -192,15 +235,34 @@ TEST(PendingUrlRequestsTest, IsCancelled) {
192235
auto request2 = pending_requests[url2];
193236
ASSERT_TRUE(request1 && request2);
194237

195-
request1->Append([&](client::HttpResponse) {});
196-
request2->Append([&](client::HttpResponse) {});
238+
request1->Append(check_cancelled);
239+
request2->Append(check_cancelled);
197240

198241
ASSERT_FALSE(request1->IsCancelled());
199242
ASSERT_FALSE(request2->IsCancelled());
200243

244+
std::future<void> future1, future2;
245+
246+
request1->ExecuteOrCancelled([&](http::RequestId& id) {
247+
id = kRequestId;
248+
return client::CancellationToken([&] {
249+
future1 = std::async(std::launch::async, complete_call, id, url1);
250+
});
251+
});
252+
253+
request2->ExecuteOrCancelled([&](http::RequestId& id) {
254+
id = kRequestId + 1;
255+
return client::CancellationToken([&] {
256+
future2 = std::async(std::launch::async, complete_call, id, url2);
257+
});
258+
});
259+
201260
EXPECT_TRUE(pending_requests.CancelAll());
202261
EXPECT_TRUE(request1->IsCancelled());
203262
EXPECT_TRUE(request2->IsCancelled());
263+
264+
ASSERT_EQ(future1.wait_for(kWaitFor), std::future_status::ready);
265+
ASSERT_EQ(future2.wait_for(kWaitFor), std::future_status::ready);
204266
}
205267
}
206268

@@ -256,19 +318,40 @@ TEST(PendingUrlRequestsTest, ExecuteOrCancelled) {
256318
client::PendingUrlRequests pending_requests;
257319
const std::string url = "url";
258320

321+
auto check_cancelled = [&](client::HttpResponse response) {
322+
ASSERT_EQ(response.GetStatus(), kCancelledStatus);
323+
};
324+
259325
// Check that ExecuteOrCancelled is behaving correctly
260326
auto request_ptr = pending_requests[url];
261327
ASSERT_TRUE(request_ptr);
262328
ASSERT_FALSE(request_ptr->IsCancelled());
263329
ASSERT_EQ(request_ptr.use_count(), 2);
264330

331+
request_ptr->Append(check_cancelled);
332+
265333
bool is_cancelled = false;
266334
bool cancel_func_called = false;
335+
std::future<void> future;
267336

268337
// Set request Id
269338
request_ptr->ExecuteOrCancelled(
270-
[&](http::RequestId&) {
271-
return client::CancellationToken([&] { is_cancelled = true; });
339+
[&](http::RequestId& id) {
340+
return client::CancellationToken([&] {
341+
is_cancelled = true;
342+
id = kRequestId;
343+
future = std::async(
344+
std::launch::async,
345+
[&](http::RequestId request_id, const std::string& url) {
346+
std::this_thread::sleep_for(kSleepFor);
347+
pending_requests.OnRequestCompleted(
348+
request_id, url,
349+
GetHttpResponse(
350+
http::HttpStatusCode::OK, kGoodResponse,
351+
{{"header1", "value1"}, {"header2", "value2"}}));
352+
},
353+
id, url);
354+
});
272355
},
273356
[] { EXPECT_TRUE(false) << "Cancel function should not be called!"; });
274357

@@ -284,31 +367,50 @@ TEST(PendingUrlRequestsTest, ExecuteOrCancelled) {
284367

285368
EXPECT_TRUE(is_cancelled);
286369
EXPECT_TRUE(cancel_func_called);
370+
371+
ASSERT_EQ(future.wait_for(kWaitFor), std::future_status::ready);
287372
}
288373

289374
TEST(PendingUrlRequestsTest, SameUrlAfterCancel) {
290-
// This test covers the user case were you have a cancelled request and
291-
// afterwards a new request with the same URL. In this case both should
292-
// exist at the same time, one in the pending request list the other in the
293-
// cancelled list.
375+
// This test covers the use-case were you have a cancelled request which is in
376+
// the process of waiting for the network cancel answer and afterwards a new
377+
// request with the same URL. In this case both should exist at the same time,
378+
// one in the pending request list the other in the cancelled list.
294379
client::PendingUrlRequests pending_requests;
295380
const std::string url = "url1";
381+
std::future<void> future_cancelled, future_valid;
382+
383+
auto check_cancelled = [&](client::HttpResponse response) {
384+
ASSERT_EQ(response.GetStatus(), kCancelledStatus);
385+
};
386+
387+
auto check_not_cancelled = [&](client::HttpResponse response) {
388+
ASSERT_NE(response.GetStatus(), kCancelledStatus);
389+
};
296390

297391
// Add request to be cancelled
298392
auto request_ptr = pending_requests[url];
299393
ASSERT_TRUE(request_ptr);
300394
ASSERT_FALSE(request_ptr->IsCancelled());
301395
ASSERT_EQ(request_ptr.use_count(), 2);
302396

303-
client::HttpResponse response_cancelled;
304-
auto cancel_id = request_ptr->Append([&](client::HttpResponse response) {
305-
response_cancelled = std::move(response);
306-
});
397+
auto cancel_id = request_ptr->Append(check_cancelled);
307398

308-
const http::RequestId request_id = 1234;
309399
request_ptr->ExecuteOrCancelled([&](http::RequestId& id) {
310-
id = request_id;
311-
return client::CancellationToken();
400+
id = kRequestId;
401+
return client::CancellationToken([&] {
402+
future_cancelled =
403+
std::async(std::launch::async,
404+
[&](http::RequestId request_id, const std::string& url) {
405+
std::this_thread::sleep_for(kSleepFor);
406+
pending_requests.OnRequestCompleted(
407+
request_id, url,
408+
GetHttpResponse(
409+
http::HttpStatusCode::OK, kGoodResponse,
410+
{{"header1", "value1"}, {"header2", "value2"}}));
411+
},
412+
id, url);
413+
});
312414
});
313415

314416
// Now cancel the request
@@ -322,22 +424,26 @@ TEST(PendingUrlRequestsTest, SameUrlAfterCancel) {
322424
ASSERT_EQ(new_request_ptr.use_count(), 2);
323425
ASSERT_FALSE(new_request_ptr == request_ptr);
324426

325-
client::HttpResponse response_valid;
326-
new_request_ptr->Append([&](client::HttpResponse response) {
327-
response_valid = std::move(response);
328-
});
427+
new_request_ptr->Append(check_not_cancelled);
329428

330-
const http::RequestId new_request_id = 1 + request_id;
331429
new_request_ptr->ExecuteOrCancelled([&](http::RequestId& id) {
332-
id = new_request_id;
430+
id = kRequestId + 1;
431+
future_valid = std::async(
432+
std::launch::async,
433+
[&](http::RequestId request_id, const std::string& url) {
434+
std::this_thread::sleep_for(kSleepFor);
435+
pending_requests.OnRequestCompleted(
436+
request_id, url,
437+
GetHttpResponse(http::HttpStatusCode::OK, kGoodResponse,
438+
{{"header1", "value1"}, {"header2", "value2"}}));
439+
},
440+
id, url);
441+
333442
return client::CancellationToken();
334443
});
335444

336-
// First trigger the valid response, then the cancelled
337-
http::Headers headers = {{"header1", "value1"}, {"header2", "value2"}};
338-
const auto response_in =
339-
GetHttpResponse(http::HttpStatusCode::OK, kGoodResponse, headers);
340-
pending_requests.OnRequestCompleted(request_id, url, response_in);
445+
ASSERT_EQ(future_cancelled.wait_for(kWaitFor), std::future_status::ready);
446+
ASSERT_EQ(future_valid.wait_for(kWaitFor), std::future_status::ready);
341447
}
342448

343449
TEST(PendingUrlRequestsTest, CallbackCalled) {

0 commit comments

Comments
 (0)