From ed5c5d325b14c0bfc7fae578a73b9deb5c0f0f3b Mon Sep 17 00:00:00 2001 From: yhirose Date: Thu, 12 Feb 2026 20:55:30 -1000 Subject: [PATCH] Parallel test on CI (#2364) * Parallel test on CI * Fix problem with Windows * Use cache for vcpkg * Parallel 'No Exception' test * Use one job to run all shards --- .github/workflows/test.yaml | 44 +++++++- .github/workflows/test_no_exceptions.yaml | 2 +- .github/workflows/test_offline.yaml | 4 +- .gitignore | 1 + justfile | 9 ++ test/Makefile | 38 ++++++- test/test.cc | 119 ++++++++++++++-------- 7 files changed, 166 insertions(+), 51 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 31459e8..027eda5 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -94,12 +94,12 @@ jobs: run: sudo apt-get install -y libmbedtls-dev - name: build and run tests (OpenSSL) if: matrix.tls_backend == 'openssl' - run: cd test && make + run: cd test && make test_split && make test_openssl_parallel env: LSAN_OPTIONS: suppressions=lsan_suppressions.txt - name: build and run tests (Mbed TLS) if: matrix.tls_backend == 'mbedtls' - run: cd test && make test_split_mbedtls && make test_mbedtls && ./test_mbedtls + run: cd test && make test_split_mbedtls && make test_mbedtls_parallel - name: run fuzz test target if: matrix.tls_backend == 'openssl' run: cd test && make fuzz_test @@ -123,12 +123,12 @@ jobs: run: brew install mbedtls@3 - name: build and run tests (OpenSSL) if: matrix.tls_backend == 'openssl' - run: cd test && make + run: cd test && make test_split && make test_openssl_parallel env: LSAN_OPTIONS: suppressions=lsan_suppressions.txt - name: build and run tests (Mbed TLS) if: matrix.tls_backend == 'mbedtls' - run: cd test && make test_split_mbedtls && make test_mbedtls && ./test_mbedtls + run: cd test && make test_split_mbedtls && make test_mbedtls_parallel - name: run fuzz test target if: matrix.tls_backend == 'openssl' run: cd test && make fuzz_test @@ -171,7 +171,14 @@ jobs: core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); - name: Setup msbuild on windows uses: microsoft/setup-msbuild@v2 + - name: Cache vcpkg packages + id: vcpkg-cache + uses: actions/cache@v4 + with: + path: C:/vcpkg/installed + key: vcpkg-installed-windows-gtest-curl-zlib-brotli-zstd - name: Install vcpkg dependencies + if: steps.vcpkg-cache.outputs.cache-hit != 'true' run: vcpkg install gtest curl zlib brotli zstd - name: Install OpenSSL if: ${{ matrix.config.with_ssl }} @@ -192,7 +199,34 @@ jobs: run: cmake --build build --config Release -- /v:m /clp:ShowCommandLine - name: Run tests ${{ matrix.config.name }} if: ${{ matrix.config.run_tests }} - run: ctest --output-on-failure --test-dir build -C Release -E "BenchmarkTest" + shell: pwsh + working-directory: build/test + run: | + $shards = 4 + $procs = @() + for ($i = 0; $i -lt $shards; $i++) { + $log = "shard_${i}.log" + $procs += Start-Process -FilePath ./Release/httplib-test.exe ` + -ArgumentList "--gtest_color=yes","--gtest_filter=${{ github.event.inputs.gtest_filter || '*' }}-*BenchmarkTest*" ` + -NoNewWindow -PassThru -RedirectStandardOutput $log -RedirectStandardError "${log}.err" ` + -Environment @{ GTEST_TOTAL_SHARDS="$shards"; GTEST_SHARD_INDEX="$i" } + } + $procs | Wait-Process + $failed = $false + for ($i = 0; $i -lt $shards; $i++) { + $log = "shard_${i}.log" + if (Select-String -Path $log -Pattern "\[ PASSED \]" -Quiet) { + $passed = (Select-String -Path $log -Pattern "\[ PASSED \]").Line + Write-Host "Shard ${i}: $passed" + } else { + Write-Host "=== Shard $i FAILED ===" + Get-Content $log + if (Test-Path "${log}.err") { Get-Content "${log}.err" } + $failed = $true + } + } + if ($failed) { exit 1 } + Write-Host "All shards passed." - name: Run benchmark tests with retry ${{ matrix.config.name }} if: ${{ matrix.config.run_tests }} run: ctest --output-on-failure --test-dir build -C Release -R "BenchmarkTest" --repeat until-pass:5 diff --git a/.github/workflows/test_no_exceptions.yaml b/.github/workflows/test_no_exceptions.yaml index 7e71635..f525297 100644 --- a/.github/workflows/test_no_exceptions.yaml +++ b/.github/workflows/test_no_exceptions.yaml @@ -17,4 +17,4 @@ jobs: - name: Run tests with CPPHTTPLIB_NO_EXCEPTIONS run: | - cd test && make EXTRA_CXXFLAGS="-fno-exceptions -DCPPHTTPLIB_NO_EXCEPTIONS" + cd test && make test_split EXTRA_CXXFLAGS="-fno-exceptions -DCPPHTTPLIB_NO_EXCEPTIONS" && make test_openssl_parallel EXTRA_CXXFLAGS="-fno-exceptions -DCPPHTTPLIB_NO_EXCEPTIONS" diff --git a/.github/workflows/test_offline.yaml b/.github/workflows/test_offline.yaml index 8814877..42c4495 100644 --- a/.github/workflows/test_offline.yaml +++ b/.github/workflows/test_offline.yaml @@ -49,12 +49,12 @@ jobs: sudo ip6tables -A OUTPUT -j REJECT - name: build and run tests (OpenSSL) if: matrix.tls_backend == 'openssl' - run: cd test && make + run: cd test && make test_split && make test_openssl_parallel env: LSAN_OPTIONS: suppressions=lsan_suppressions.txt - name: build and run tests (No TLS) if: matrix.tls_backend == 'no-tls' - run: cd test && make test_no_tls && ./test_no_tls + run: cd test && make test_no_tls_parallel - name: restore network if: always() run: | diff --git a/.gitignore b/.gitignore index 80278dc..c2590a0 100644 --- a/.gitignore +++ b/.gitignore @@ -46,6 +46,7 @@ test/test.xcodeproj/*/xcuser* test/*.o test/*.pem test/*.srl +test/*.log test/_build_* work/ benchmark/server* diff --git a/justfile b/justfile index 0745a78..6b4c5f0 100644 --- a/justfile +++ b/justfile @@ -9,13 +9,22 @@ openssl: @(cd test && make test && LSAN_OPTIONS=suppressions=lsan_suppressions.txt ./test) @(cd test && make proxy) +openssl_parallel: + @(cd test && make test_openssl_parallel) + mbedtls: @(cd test && make test_mbedtls && LSAN_OPTIONS=suppressions=lsan_suppressions.txt ./test_mbedtls) @(cd test && make proxy_mbedtls) +mbedtls_parallel: + @(cd test && make test_mbedtls_parallel) + no_tls: @(cd test && make test_no_tls && ./test_no_tls) +no_tls_parallel: + @(cd test && make test_no_tls_parallel) + fuzz: @(cd test && make fuzz_test) diff --git a/test/Makefile b/test/Makefile index 9b2d1e7..b8f5bab 100644 --- a/test/Makefile +++ b/test/Makefile @@ -56,6 +56,42 @@ STYLE_CHECK_FILES = $(filter-out httplib.h httplib.cc, \ all : test test_split LSAN_OPTIONS=suppressions=lsan_suppressions.txt ./test +SHARDS ?= 4 + +define run_parallel + @echo "Running $(1) with $(SHARDS) shards in parallel..." + @fail=0; \ + for i in $$(seq 0 $$(($(SHARDS) - 1))); do \ + GTEST_TOTAL_SHARDS=$(SHARDS) GTEST_SHARD_INDEX=$$i \ + LSAN_OPTIONS=suppressions=lsan_suppressions.txt \ + ./$(1) --gtest_color=yes > $(1)_shard_$$i.log 2>&1 & \ + done; \ + wait; \ + for i in $$(seq 0 $$(($(SHARDS) - 1))); do \ + if ! grep -q "\[ PASSED \]" $(1)_shard_$$i.log; then \ + echo "=== Shard $$i FAILED ==="; \ + cat $(1)_shard_$$i.log; \ + fail=1; \ + else \ + passed=$$(grep "\[ PASSED \]" $(1)_shard_$$i.log); \ + echo "Shard $$i: $$passed"; \ + fi; \ + done; \ + if [ $$fail -ne 0 ]; then exit 1; fi; \ + echo "All shards passed." +endef + +.PHONY: test_openssl_parallel test_mbedtls_parallel test_no_tls_parallel + +test_openssl_parallel : test + $(call run_parallel,test) + +test_mbedtls_parallel : test_mbedtls + $(call run_parallel,test_mbedtls) + +test_no_tls_parallel : test_no_tls + $(call run_parallel,test_no_tls) + proxy : test_proxy @echo "Starting proxy server..." cd proxy && \ @@ -172,5 +208,5 @@ cert.pem: ./gen-certs.sh clean: - rm -rf test test_split test_mbedtls test_split_mbedtls test_no_tls, test_split_no_tls test_proxy test_proxy_mbedtls server_fuzzer *.pem *.0 *.o *.1 *.srl httplib.h httplib.cc _build* *.dSYM + rm -rf test test_split test_mbedtls test_split_mbedtls test_no_tls, test_split_no_tls test_proxy test_proxy_mbedtls server_fuzzer *.pem *.0 *.o *.1 *.srl httplib.h httplib.cc _build* *.dSYM *_shard_*.log diff --git a/test/test.cc b/test/test.cc index 010b9de..de29274 100644 --- a/test/test.cc +++ b/test/test.cc @@ -55,7 +55,13 @@ using namespace std; using namespace httplib; const char *HOST = "localhost"; -const int PORT = 1234; + +static int get_base_port() { + const char *shard = getenv("GTEST_SHARD_INDEX"); + return shard ? 11234 + std::atoi(shard) * 100 : 1234; +} + +const int PORT = get_base_port(); const string LONG_QUERY_VALUE = string(25000, '@'); const string LONG_QUERY_URL = "/long-query-value?key=" + LONG_QUERY_VALUE; @@ -178,7 +184,12 @@ protected: EXPECT_EQ(resp.body, content_); } - const std::string pathname_{"./httplib-server.sock"}; + static std::string make_sock_path() { + const char *shard = getenv("GTEST_SHARD_INDEX"); + return shard ? std::string("./httplib-server-") + shard + ".sock" + : "./httplib-server.sock"; + } + const std::string pathname_{make_sock_path()}; const std::string pattern_{"/hi"}; const std::string content_{"Hello World!"}; }; @@ -2370,28 +2381,32 @@ TEST(RedirectFromPageWithContent, Redirect) { TEST(RedirectFromPageWithContentIP6, Redirect) { Server svr; + auto port_str = std::to_string(PORT); + auto redirect_url = "http://[::1]:" + port_str + "/2"; + auto expected_host = "[::1]:" + port_str; + svr.Get("/1", [&](const Request & /*req*/, Response &res) { res.set_content("___", "text/plain"); // res.set_redirect("/2"); - res.set_redirect("http://[::1]:1234/2"); + res.set_redirect(redirect_url); }); svr.Get("/2", [&](const Request &req, Response &res) { auto host_header = req.headers.find("Host"); ASSERT_TRUE(host_header != req.headers.end()); - EXPECT_EQ("[::1]:1234", host_header->second); + EXPECT_EQ(expected_host, host_header->second); res.set_content("Hello World!", "text/plain"); }); - auto th = std::thread([&]() { svr.listen("::1", 1234); }); + auto th = std::thread([&]() { svr.listen("::1", PORT); }); auto se = detail::scope_exit([&] { svr.stop(); th.join(); ASSERT_FALSE(svr.is_running()); }); - // When IPV6 support isn't available svr.listen("::1", 1234) never + // When IPV6 support isn't available svr.listen("::1", PORT) never // actually starts anything, so the condition !svr.is_running() will // always remain true, and the loop never stops. // This basically counts how many milliseconds have passed since the @@ -2403,7 +2418,7 @@ TEST(RedirectFromPageWithContentIP6, Redirect) { } { - Client cli("http://[::1]:1234"); + Client cli("::1", PORT); cli.set_follow_location(true); std::string body; @@ -2418,7 +2433,7 @@ TEST(RedirectFromPageWithContentIP6, Redirect) { } { - Client cli("http://[::1]:1234"); + Client cli("::1", PORT); std::string body; auto res = cli.Get("/1", [&](const char *data, size_t data_length) { @@ -10471,7 +10486,8 @@ TEST(ClientImplMethods, GetSocketTest) { res.status = StatusCode::OK_200; }); - auto thread = std::thread([&]() { svr.listen("127.0.0.1", 3333); }); + auto port = svr.bind_to_any_port("127.0.0.1"); + auto thread = std::thread([&]() { svr.listen_after_bind(); }); auto se = detail::scope_exit([&] { svr.stop(); thread.join(); @@ -10481,7 +10497,7 @@ TEST(ClientImplMethods, GetSocketTest) { svr.wait_until_ready(); { - httplib::Client cli("http://127.0.0.1:3333"); + httplib::Client cli("127.0.0.1", port); cli.set_keep_alive(true); // Use the behavior of cpp-httplib of opening the connection @@ -10640,10 +10656,13 @@ TEST(HttpsToHttpRedirectTest3, SimpleInterface_Online) { } TEST(HttpToHttpsRedirectTest, CertFile) { + auto ssl_port = PORT + 1; + Server svr; ASSERT_TRUE(svr.is_valid()); svr.Get("/index", [&](const Request &, Response &res) { - res.set_redirect("https://127.0.0.1:1235/index"); + res.set_redirect("https://127.0.0.1:" + std::to_string(ssl_port) + + "/index"); svr.stop(); }); @@ -10655,7 +10674,8 @@ TEST(HttpToHttpsRedirectTest, CertFile) { }); thread t = thread([&]() { ASSERT_TRUE(svr.listen("127.0.0.1", PORT)); }); - thread t2 = thread([&]() { ASSERT_TRUE(ssl_svr.listen("127.0.0.1", 1235)); }); + thread t2 = + thread([&]() { ASSERT_TRUE(ssl_svr.listen("127.0.0.1", ssl_port)); }); auto se = detail::scope_exit([&] { t2.join(); t.join(); @@ -10677,10 +10697,13 @@ TEST(HttpToHttpsRedirectTest, CertFile) { } TEST(SSLClientRedirectTest, CertFile) { + auto ssl_port = PORT + 1; + SSLServer ssl_svr1(SERVER_CERT2_FILE, SERVER_PRIVATE_KEY_FILE); ASSERT_TRUE(ssl_svr1.is_valid()); ssl_svr1.Get("/index", [&](const Request &, Response &res) { - res.set_redirect("https://127.0.0.1:1235/index"); + res.set_redirect("https://127.0.0.1:" + std::to_string(ssl_port) + + "/index"); ssl_svr1.stop(); }); @@ -10693,7 +10716,7 @@ TEST(SSLClientRedirectTest, CertFile) { thread t = thread([&]() { ASSERT_TRUE(ssl_svr1.listen("127.0.0.1", PORT)); }); thread t2 = - thread([&]() { ASSERT_TRUE(ssl_svr2.listen("127.0.0.1", 1235)); }); + thread([&]() { ASSERT_TRUE(ssl_svr2.listen("127.0.0.1", ssl_port)); }); auto se = detail::scope_exit([&] { t2.join(); t.join(); @@ -10774,7 +10797,8 @@ TEST(MultipartFormDataTest, LargeData) { } }); - auto t = std::thread([&]() { svr.listen(HOST, 8080); }); + auto port = svr.bind_to_any_port(HOST); + auto t = std::thread([&]() { svr.listen_after_bind(); }); auto se = detail::scope_exit([&] { svr.stop(); t.join(); @@ -10788,7 +10812,7 @@ TEST(MultipartFormDataTest, LargeData) { std::stringstream buffer; buffer << data; - Client cli("https://localhost:8080"); + SSLClient cli(HOST, port); cli.enable_server_certificate_verification(false); UploadFormDataItems items{ @@ -10921,7 +10945,8 @@ TEST(MultipartFormDataTest, DataProviderItems) { EXPECT_EQ(items[3].content_type, ""); }); - auto t = std::thread([&]() { svr.listen("localhost", 8080); }); + auto port = svr.bind_to_any_port("localhost"); + auto t = std::thread([&]() { svr.listen_after_bind(); }); auto se = detail::scope_exit([&] { svr.stop(); t.join(); @@ -10931,7 +10956,7 @@ TEST(MultipartFormDataTest, DataProviderItems) { svr.wait_until_ready(); { - Client cli("https://localhost:8080"); + SSLClient cli("localhost", port); cli.enable_server_certificate_verification(false); UploadFormDataItems items{ @@ -11122,7 +11147,8 @@ TEST(MultipartFormDataTest, PostCustomBoundary) { } }); - auto t = std::thread([&]() { svr.listen("localhost", 8080); }); + auto port = svr.bind_to_any_port("localhost"); + auto t = std::thread([&]() { svr.listen_after_bind(); }); auto se = detail::scope_exit([&] { svr.stop(); t.join(); @@ -11136,7 +11162,7 @@ TEST(MultipartFormDataTest, PostCustomBoundary) { std::stringstream buffer; buffer << data; - Client cli("https://localhost:8080"); + SSLClient cli("localhost", port); cli.enable_server_certificate_verification(false); UploadFormDataItems items{ @@ -11205,7 +11231,8 @@ TEST(MultipartFormDataTest, PutFormData) { } }); - auto t = std::thread([&]() { svr.listen("localhost", 8080); }); + auto port = svr.bind_to_any_port("localhost"); + auto t = std::thread([&]() { svr.listen_after_bind(); }); auto se = detail::scope_exit([&] { svr.stop(); t.join(); @@ -11219,7 +11246,7 @@ TEST(MultipartFormDataTest, PutFormData) { std::stringstream buffer; buffer << data; - Client cli("https://localhost:8080"); + SSLClient cli("localhost", port); cli.enable_server_certificate_verification(false); UploadFormDataItems items{ @@ -11269,7 +11296,8 @@ TEST(MultipartFormDataTest, PutFormDataCustomBoundary) { } }); - auto t = std::thread([&]() { svr.listen("localhost", 8080); }); + auto port = svr.bind_to_any_port("localhost"); + auto t = std::thread([&]() { svr.listen_after_bind(); }); auto se = detail::scope_exit([&] { svr.stop(); t.join(); @@ -11283,7 +11311,7 @@ TEST(MultipartFormDataTest, PutFormDataCustomBoundary) { std::stringstream buffer; buffer << data; - Client cli("https://localhost:8080"); + SSLClient cli("localhost", port); cli.enable_server_certificate_verification(false); UploadFormDataItems items{ @@ -13176,7 +13204,8 @@ protected: } res.set_content(body, "text/plain"); }); - thread_ = std::thread([this]() { svr_.listen("127.0.0.1", 8787); }); + port_ = svr_.bind_to_any_port("127.0.0.1"); + thread_ = std::thread([this]() { svr_.listen_after_bind(); }); svr_.wait_until_ready(); } void TearDown() override { @@ -13185,17 +13214,18 @@ protected: } Server svr_; std::thread thread_; + int port_ = 0; }; TEST_F(OpenStreamTest, Basic) { - Client cli("127.0.0.1", 8787); + Client cli("127.0.0.1", port_); auto handle = cli.open_stream("GET", "/hello"); EXPECT_TRUE(handle.is_valid()); EXPECT_EQ("Hello World!", read_all(handle)); } TEST_F(OpenStreamTest, SmallBuffer) { - Client cli("127.0.0.1", 8787); + Client cli("127.0.0.1", port_); auto handle = cli.open_stream("GET", "/hello"); std::string result; char buf[4]; @@ -13206,14 +13236,15 @@ TEST_F(OpenStreamTest, SmallBuffer) { } TEST_F(OpenStreamTest, DefaultHeaders) { - Client cli("127.0.0.1", 8787); + Client cli("127.0.0.1", port_); // open_stream GET should include Host, User-Agent and Accept-Encoding { auto handle = cli.open_stream("GET", "/echo-headers"); ASSERT_TRUE(handle.is_valid()); auto body = read_all(handle); - EXPECT_NE(body.find("Host:127.0.0.1:8787"), std::string::npos); + EXPECT_NE(body.find("Host:127.0.0.1:" + std::to_string(port_)), + std::string::npos); EXPECT_NE(body.find("User-Agent:cpp-httplib/" CPPHTTPLIB_VERSION), std::string::npos); EXPECT_NE(body.find("Accept-Encoding:"), std::string::npos); @@ -13251,7 +13282,7 @@ TEST_F(OpenStreamTest, DefaultHeaders) { } TEST_F(OpenStreamTest, Large) { - Client cli("127.0.0.1", 8787); + Client cli("127.0.0.1", port_); auto handle = cli.open_stream("GET", "/large"); EXPECT_EQ(10000u, read_all(handle).size()); } @@ -13263,7 +13294,7 @@ TEST_F(OpenStreamTest, ConnectionError) { } TEST_F(OpenStreamTest, Chunked) { - Client cli("127.0.0.1", 8787); + Client cli("127.0.0.1", port_); auto handle = cli.open_stream("GET", "/chunked"); EXPECT_TRUE(handle.response && handle.response->get_header_value( "Transfer-Encoding") == "chunked"); @@ -13271,7 +13302,7 @@ TEST_F(OpenStreamTest, Chunked) { } TEST_F(OpenStreamTest, ProhibitedTrailersAreIgnored_Stream) { - Client cli("127.0.0.1", 8787); + Client cli("127.0.0.1", port_); auto handle = cli.open_stream("GET", "/streamed-chunked-with-prohibited-trailer"); ASSERT_TRUE(handle.is_valid()); @@ -13304,7 +13335,7 @@ TEST_F(OpenStreamTest, ProhibitedTrailersAreIgnored_Stream) { #ifdef CPPHTTPLIB_ZLIB_SUPPORT TEST_F(OpenStreamTest, Gzip) { - Client cli("127.0.0.1", 8787); + Client cli("127.0.0.1", port_); auto handle = cli.open_stream("GET", "/compressible", {}, {{"Accept-Encoding", "gzip"}}); EXPECT_EQ("gzip", handle.response->get_header_value("Content-Encoding")); @@ -13314,7 +13345,7 @@ TEST_F(OpenStreamTest, Gzip) { #ifdef CPPHTTPLIB_BROTLI_SUPPORT TEST_F(OpenStreamTest, Brotli) { - Client cli("127.0.0.1", 8787); + Client cli("127.0.0.1", port_); auto handle = cli.open_stream("GET", "/compressible", {}, {{"Accept-Encoding", "br"}}); EXPECT_EQ("br", handle.response->get_header_value("Content-Encoding")); @@ -13324,7 +13355,7 @@ TEST_F(OpenStreamTest, Brotli) { #ifdef CPPHTTPLIB_ZSTD_SUPPORT TEST_F(OpenStreamTest, Zstd) { - Client cli("127.0.0.1", 8787); + Client cli("127.0.0.1", port_); auto handle = cli.open_stream("GET", "/compressible", {}, {{"Accept-Encoding", "zstd"}}); EXPECT_EQ("zstd", handle.response->get_header_value("Content-Encoding")); @@ -13365,7 +13396,8 @@ protected: return true; }); }); - thread_ = std::thread([this]() { svr_.listen("127.0.0.1", 8788); }); + port_ = svr_.bind_to_any_port("127.0.0.1"); + thread_ = std::thread([this]() { svr_.listen_after_bind(); }); svr_.wait_until_ready(); } void TearDown() override { @@ -13374,10 +13406,11 @@ protected: } SSLServer svr_; std::thread thread_; + int port_ = 0; }; TEST_F(SSLOpenStreamTest, Basic) { - SSLClient cli("127.0.0.1", 8788); + SSLClient cli("127.0.0.1", port_); cli.enable_server_certificate_verification(false); auto handle = cli.open_stream("GET", "/hello"); ASSERT_TRUE(handle.is_valid()); @@ -13385,7 +13418,7 @@ TEST_F(SSLOpenStreamTest, Basic) { } TEST_F(SSLOpenStreamTest, Chunked) { - SSLClient cli("127.0.0.1", 8788); + SSLClient cli("127.0.0.1", port_); cli.enable_server_certificate_verification(false); auto handle = cli.open_stream("GET", "/chunked"); @@ -13399,7 +13432,7 @@ TEST_F(SSLOpenStreamTest, Chunked) { } TEST_F(SSLOpenStreamTest, Post) { - SSLClient cli("127.0.0.1", 8788); + SSLClient cli("127.0.0.1", port_); cli.enable_server_certificate_verification(false); auto handle = @@ -13413,7 +13446,7 @@ TEST_F(SSLOpenStreamTest, Post) { } TEST_F(SSLOpenStreamTest, PostChunked) { - SSLClient cli("127.0.0.1", 8788); + SSLClient cli("127.0.0.1", port_); cli.enable_server_certificate_verification(false); auto handle = cli.open_stream("POST", "/chunked-response", {}, {}, @@ -13738,7 +13771,8 @@ protected: svr_.Post("/echo", [](const httplib::Request &req, httplib::Response &res) { res.set_content(req.body, "text/plain"); }); - thread_ = std::thread([this]() { svr_.listen("127.0.0.1", 8803); }); + port_ = svr_.bind_to_any_port("127.0.0.1"); + thread_ = std::thread([this]() { svr_.listen_after_bind(); }); svr_.wait_until_ready(); } void TearDown() override { @@ -13747,10 +13781,11 @@ protected: } httplib::SSLServer svr_{"cert.pem", "key.pem"}; std::thread thread_; + int port_ = 0; }; TEST_F(SSLStreamApiTest, GetAndPost) { - httplib::SSLClient cli("127.0.0.1", 8803); + httplib::SSLClient cli("127.0.0.1", port_); cli.enable_server_certificate_verification(false); auto get = httplib::stream::Get(cli, "/hello"); EXPECT_EQ("Hello SSL!", read_body(get));