-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Fix the xrpl.net unit test #6241
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Conversation
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #6241 +/- ##
=========================================
+ Coverage 79.3% 79.4% +0.1%
=========================================
Files 839 839
Lines 71607 71634 +27
Branches 8307 8240 -67
=========================================
+ Hits 56768 56856 +88
+ Misses 14839 14778 -61 🚀 New features to boost your workflow:
|
src/tests/libxrpl/net/HTTPClient.cpp
Outdated
| { | ||
| // Create a null journal for testing | ||
| beast::Journal j{beast::Journal::getNullSink()}; | ||
| beast::Journal j{DebugSink::instance()}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of creating a whole new sink, can you use any of the other existing sinks for this? e.g. StreamSink in src/test/unit_test/SuiteJournal.h, TestSink in src/test/server/Server_test.cpp, ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can't because those things are in xrpld and this test is a standalone executable. We'll be able to get rid of them completely in the future after we fully switch to gtest.
Co-authored-by: Bart <bthomee@users.noreply.github.com>
Co-authored-by: Bart <bthomee@users.noreply.github.com>
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
src/tests/libxrpl/net/HTTPClient.cpp
Outdated
| if (boost::iequals(name, "Content-Length")) | ||
| { | ||
| res->erase(boost::beast::http::field::content_length); | ||
| res->set(name, value); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| if (boost::iequals(name, "Content-Length")) | |
| { | |
| res->erase(boost::beast::http::field::content_length); | |
| res->set(name, value); | |
| } | |
| res->set(name, value); |
Now that the headers are no longer set before res->body(), shouldn't we set them all here? (I'm not familiar with when Boost sets these fields, so perhaps the erase is still needed if it always adds a Content-Length header by itself.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that I'm a bit surprised the tests succeeded without any of the headers being set (unless Boost sets Content-Length automatically).
Then again, this might be because some tests have already been ported to gtest and others haven't, so here you're just lucky it works?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I think we only have one custom header here and boost always automatically add the content-length header.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't we then keep the erase, as otherwise we will get two headers with the "same" name, e.g. Content-Type and content-type?
Something like this might be better:
if (boost::iequals(name, "Content-Length"))
{
res->erase(boost::beast::http::field::content_length);
}
res->set(name, value);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I think it still works without explicitly erasing and re-setting. The logs show that the client is still able to receive different content-length headers.
4: Hello World!"
4: DBG: invokeComplete: Deadline popping: 1
4: TRC: Fetch: 127.0.0.1
4: TRC: Resolving: 127.0.0.1
4: TRC: Resolve complete.
4: TRC: Connected.
4: TRC: Session started.
4: TRC: Wrote.
4: TRC: Header: "HTTP/1.0 200 OK
4: Server: TestServer
4: content-length: 12
4:
4: Hello World!"
4: DBG: invokeComplete: Deadline popping: 1
4: TRC: Fetch: 127.0.0.1
4: TRC: Resolving: 127.0.0.1
4: TRC: Resolve complete.
4: TRC: Connected.
4: TRC: Session started.
4: TRC: Wrote.
4: TRC: Header: "HTTP/1.0 200 OK
4: Server: TestServer
4: CONTENT-LENGTH: 12
4:
4: Hello World!"
4: DBG: invokeComplete: Deadline popping: 1
4: TRC: Fetch: 127.0.0.1
4: TRC: Resolving: 127.0.0.1
4: TRC: Resolve complete.
4: TRC: Connected.
4: TRC: Session started.
4: TRC: Wrote.
4: TRC: Header: "HTTP/1.0 200 OK
4: Server: TestServer
4: Content-length: 12
4:
4: Hello World!"
4: DBG: invokeComplete: Deadline popping: 1
4: TRC: Fetch: 127.0.0.1
4: TRC: Resolving: 127.0.0.1
4: TRC: Resolve complete.
4: TRC: Connected.
4: TRC: Session started.
4: TRC: Wrote.
4: TRC: Header: "HTTP/1.0 200 OK
4: Server: TestServer
4: content-Length: 12
4:
4: Hello World!"
4: DBG: invokeComplete: Deadline popping: 1
4: [ OK ] HTTPClient.case_insensitive_content_length (10 ms)
4: [ RUN ] HTTPClient.basic_http_request
4: TRC: Fetch: 127.0.0.1
4: TRC: Resolving: 127.0.0.1
4: TRC: Resolve complete.
4: TRC: Connected.
4: TRC: Session started.
4: TRC: Wrote.
4: TRC: Header: "HTTP/1.0 200 OK
4: Server: TestServer
4: Content-Length: 18
4: Content-Type: text/plain
4:
4: Test response body"
4: DBG: invokeComplete: Deadline popping: 1
4: [ OK ] HTTPClient.basic_http_request (0 ms)
4: [ RUN ] HTTPClient.empty_response
4: TRC: Fetch: 127.0.0.1
4: TRC: Resolving: 127.0.0.1
4: TRC: Resolve complete.
4: TRC: Connected.
4: TRC: Session started.
4: TRC: Wrote.
4: TRC: Header: "HTTP/1.0 200 OK
4: Server: TestServer
4: Content-Length: 0
4:
4: "
4: DBG: invokeComplete: Deadline popping: 1
4: [ OK ] HTTPClient.empty_response (0 ms)
4: [ RUN ] HTTPClient.different_status_codes
4: TRC: Fetch: 127.0.0.1
4: TRC: Resolving: 127.0.0.1
4: TRC: Resolve complete.
4: TRC: Connected.
4: TRC: Session started.
4: TRC: Wrote.
4: TRC: Header: "HTTP/1.0 200 OK
4: Server: TestServer
4: Content-Length: 10
4:
4: Status 200"
4: DBG: invokeComplete: Deadline popping: 1
4: TRC: Fetch: 127.0.0.1
4: TRC: Resolving: 127.0.0.1
4: TRC: Resolve complete.
4: TRC: Connected.
4: TRC: Session started.
4: TRC: Wrote.
4: TRC: Header: "HTTP/1.0 404 Not Found
4: Server: TestServer
4: Content-Length: 10
4:
4: Status 404"
4: DBG: invokeComplete: Deadline popping: 1
4: TRC: Fetch: 127.0.0.1
4: TRC: Resolving: 127.0.0.1
4: TRC: Resolve complete.
4: TRC: Connected.
4: TRC: Session started.
4: TRC: Wrote.
4: TRC: Header: "HTTP/1.0 500 Internal Server Error
4: Server: TestServer
4: Content-Length: 10
4:
4: Status 500"
4: DBG: invokeComplete: Deadline popping: 1
4: [ OK ] HTTPClient.different_status_codes (1 ms)
4: [----------] 4 tests from HTTPClient (14 ms total)
4:
4: [----------] Global test environment tear-down
4: [==========] 4 tests from 1 test suite ran. (14 ms total)
4: [ PASSED ] 4 tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
template<class Allocator>
void
basic_fields<Allocator>::
set_element(element& e)
{
auto it = set_.lower_bound(
e.name_string(), key_compare{});
if(it == set_.end() || ! beast::iequals(
e.name_string(), it->name_string()))
{
set_.insert_before(it, e);
list_.push_back(e);
return;
}
for(;;)
{
auto next = it;
++next;
set_.erase(it);
list_.erase(list_.iterator_to(*it));
delete_element(*it);
it = next;
if(it == set_.end() ||
! beast::iequals(e.name_string(), it->name_string()))
break;
}
set_.insert_before(it, e);
list_.push_back(e);
}
The code in boost also erases the existing element
Co-authored-by: Bart <bthomee@users.noreply.github.com>
Co-authored-by: Bart <bthomee@users.noreply.github.com>
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
| static TestSink& | ||
| instance() | ||
| { | ||
| static TestSink _; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_ is an unused variable placeholder in C++26. Maybe I'm thinking too much upfront but probably it would be better to avoid using it for anything important.
| *sock, | ||
| *buffer, | ||
| *req, | ||
| [this, sock, buffer, req]( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move constructor of the class should be explicitly deleted since we are capturing this here to avoid potential problems.
| boost::beast::http::request<boost::beast::http::string_body>>(); | ||
|
|
||
| // Read the HTTP request asynchronously | ||
| boost::beast::http::async_read( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe consider refactoring this to coroutines to avoid a lot of nested lambdas
| stop() | ||
| { | ||
| running_ = false; | ||
| acceptor_.close(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should add some synchronisation here to make sure all the async operations are finished before the class is deleted. E.g. binary_semaphore.acquire() here and binary_semaphore.release() at the end of async operations.
High Level Overview of Change
This PR makes the
readfunction call inhandleConnectionasync, adds a new classTestSinkto help debugging, and adds a new targetxrpl.tests.helpersto put the helper class in.Context of Change
Our Trigger job fails almost everyday because of two unit tests. The first issue is that it takes forever for the unit tests to finish, and the second issue is we get a time out error in the
xrpl.nettest case.I investigated the second issue and found out that the synchronous function
readblocks the connection callback on the client side. As thereadfunction expect some data that is supposed to be sent in the connection callback, we're getting a deadlock here. By making the read function asynchronous, the connection callback will have a chance to be called so that thereadfunction can consume the data eventually.Type of Change
.gitignore, formatting, dropping support for older tooling)