| 19 | #include "fl/stl/mutex.h" |
| 20 | |
| 21 | FL_TEST_FILE(FL_FILEPATH) { |
| 22 | |
| 23 | using namespace fl; |
| 24 | using namespace fl::net::http; |
| 25 | |
| 26 | FL_TEST_CASE("Loopback: connect and sync RPC round-trip") { |
| 27 | // Use port 0 to let the OS assign an available ephemeral port. |
| 28 | // This eliminates port collisions entirely. |
| 29 | auto server_transport = fl::make_shared<HttpStreamServer>(0); |
| 30 | FL_REQUIRE(server_transport->connect()); |
| 31 | uint16_t PORT = server_transport->port(); |
| 32 | FL_REQUIRE(PORT != 0); |
| 33 | |
| 34 | fl::Remote server_remote( |
| 35 | [&server_transport]() { return server_transport->readRequest(); }, |
| 36 | [&server_transport](const fl::json& r) { server_transport->writeResponse(r); } |
| 37 | ); |
| 38 | server_remote.bind("add", [](int a, int b) -> int { return a + b; }); |
| 39 | |
| 40 | // Background thread: handles acceptClients + server transport update + RPC. |
| 41 | // This is the standard net-thread pattern — the server side runs entirely |
| 42 | // in a dedicated thread, and results (the RPC response) flow back through |
| 43 | // the TCP connection to the client on the main thread. |
| 44 | fl::atomic<bool> server_running{true}; |
| 45 | fl::thread server_thread([&]() { |
| 46 | while (server_running.load()) { |
| 47 | uint32_t now = fl::millis(); |
| 48 | server_transport->acceptClients(); |
| 49 | server_transport->update(now); |
| 50 | server_remote.update(now); |
| 51 | fl::this_thread::sleep_for(fl::chrono::milliseconds(1)); // ok sleep for - inside thread |
| 52 | } |
| 53 | }); |
| 54 | |
| 55 | // Client — connect() blocks until the server accepts, but the server |
| 56 | // thread is already running acceptClients() so this won't deadlock. |
| 57 | // Use 127.0.0.1 directly instead of "localhost" to avoid DNS resolution |
| 58 | // overhead and IPv6 ambiguity on Windows (server binds to INADDR_LOOPBACK). |
| 59 | auto client_transport = fl::make_shared<HttpStreamClient>("127.0.0.1", PORT); |
| 60 | |
| 61 | { |
| 62 | bool connected = false; |
| 63 | uint32_t connect_start = fl::millis(); |
| 64 | while (fl::millis() - connect_start < 10000) { |
| 65 | if (client_transport->connect()) { |
| 66 | connected = true; |
| 67 | break; |
| 68 | } |
| 69 | fl::delay(10); |
| 70 | } |
| 71 | FL_REQUIRE(connected); |
| 72 | } |
| 73 | |
| 74 | FL_REQUIRE(client_transport->isConnected()); |
| 75 | |
| 76 | // Send request |
| 77 | json params = json::array(); |
| 78 | params.push_back(json(5)); |
nothing calls this directly
no test coverage detected