Selaa lähdekoodia

Merge pull request #1428 from mickel8/tests

Improve testing framework
Paul-Louis Ageneau 1 kuukausi sitten
vanhempi
commit
5f78be4559

+ 2 - 1
.gitignore

@@ -12,4 +12,5 @@ compile_commands.json
 /tests
 .DS_Store
 .idea
-
+# clangd cache
+.cache

+ 6 - 2
CMakeLists.txt

@@ -226,6 +226,10 @@ set(TESTS_SOURCES
     ${CMAKE_CURRENT_SOURCE_DIR}/test/benchmark.cpp
 )
 
+set(TESTS_HEADERS 
+	${CMAKE_CURRENT_SOURCE_DIR}/test/test.hpp
+)
+
 set(TESTS_UWP_RESOURCES
 	${CMAKE_CURRENT_SOURCE_DIR}/test/uwp/tests/Logo.png
 	${CMAKE_CURRENT_SOURCE_DIR}/test/uwp/tests/package.appxManifest
@@ -546,9 +550,9 @@ install(FILES
 if(NOT NO_TESTS)
 	if(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
 		# Add resource files needed for UWP apps.
-		add_executable(datachannel-tests ${TESTS_SOURCES} ${TESTS_UWP_RESOURCES})
+		add_executable(datachannel-tests ${TESTS_SOURCES} ${TESTS_HEADERS} ${TESTS_UWP_RESOURCES})
 	else()
-		add_executable(datachannel-tests ${TESTS_SOURCES})
+		add_executable(datachannel-tests ${TESTS_SOURCES} ${TESTS_HEADERS})
 	endif()
 
 	set_target_properties(datachannel-tests PROPERTIES

+ 4 - 2
test/capi_connectivity.cpp

@@ -6,6 +6,7 @@
  * file, You can obtain one at https://mozilla.org/MPL/2.0/.
  */
 
+#include "test.hpp"
 #include <rtc/rtc.h>
 
 #include <cstdio>
@@ -392,7 +393,8 @@ error:
 
 #include <stdexcept>
 
-void test_capi_connectivity() {
+TestResult test_capi_connectivity() {
 	if (test_capi_connectivity_main())
-		throw std::runtime_error("Connection failed");
+		return TestResult(false, "Connection failed");
+	return TestResult(true);
 }

+ 4 - 2
test/capi_track.cpp

@@ -6,6 +6,7 @@
  * file, You can obtain one at https://mozilla.org/MPL/2.0/.
  */
 
+#include "test.hpp"
 #include <rtc/rtc.h>
 
 #include <cstdio>
@@ -238,7 +239,8 @@ error:
 
 #include <stdexcept>
 
-void test_capi_track() {
+TestResult test_capi_track() {
 	if (test_capi_track_main())
-		throw std::runtime_error("Connection failed");
+		return TestResult(false, "Connection failed");
+	return TestResult(true);
 }

+ 4 - 2
test/capi_websocketserver.cpp

@@ -6,6 +6,7 @@
  * file, You can obtain one at https://mozilla.org/MPL/2.0/.
  */
 
+#include "test.hpp"
 #include <rtc/rtc.h>
 
 #if RTC_ENABLE_WEBSOCKET
@@ -164,9 +165,10 @@ error:
 
 #include <stdexcept>
 
-void test_capi_websocketserver() {
+TestResult test_capi_websocketserver() {
 	if (test_capi_websocketserver_main())
-		throw std::runtime_error("WebSocketServer test failed");
+		return TestResult(false, "WebSocketServer test failed");
+	return TestResult(true);
 }
 
 #endif

+ 32 - 19
test/connectivity.cpp

@@ -7,6 +7,7 @@
  */
 
 #include "rtc/rtc.hpp"
+#include "test.hpp"
 
 #include <atomic>
 #include <chrono>
@@ -21,7 +22,13 @@ using namespace std;
 
 template <class T> weak_ptr<T> make_weak_ptr(shared_ptr<T> ptr) { return ptr; }
 
-void test_connectivity(bool signal_wrong_fingerprint) {
+TestResult test_connectivity(bool);
+
+TestResult test_connectivity() { return test_connectivity(false); }
+
+TestResult test_connectivity_fail_on_wrong_fingerprint() { return test_connectivity(true); }
+
+TestResult test_connectivity(bool signal_wrong_fingerprint) {
 	InitLogger(LogLevel::Debug);
 
 	Configuration config1;
@@ -128,7 +135,7 @@ void test_connectivity(bool signal_wrong_fingerprint) {
 	auto dc1 = pc1.createDataChannel("test");
 
 	if (dc1->id().has_value())
-		throw std::runtime_error("DataChannel stream id assigned before connection");
+		return TestResult(false, "DataChannel stream id assigned before connection");
 
 	dc1->onOpen([wdc1 = make_weak_ptr(dc1)]() {
 		if (auto dc1 = wdc1.lock()) {
@@ -152,30 +159,35 @@ void test_connectivity(bool signal_wrong_fingerprint) {
 		this_thread::sleep_for(1s);
 
 	if (pc1.state() != PeerConnection::State::Connected ||
-	    pc2.state() != PeerConnection::State::Connected)
-		throw runtime_error("PeerConnection is not connected");
+	    pc2.state() != PeerConnection::State::Connected) {
+		if (signal_wrong_fingerprint) {
+			return TestResult(true);
+		} else {
+			return TestResult(false, "PeerConnection is not connected");
+		}
+	}
 
 	if ((pc1.iceState() != PeerConnection::IceState::Connected &&
 	     pc1.iceState() != PeerConnection::IceState::Completed) ||
 	    (pc2.iceState() != PeerConnection::IceState::Connected &&
 	     pc2.iceState() != PeerConnection::IceState::Completed))
-		throw runtime_error("ICE is not connected");
+		return TestResult(false, "ICE is not connected");
 
 	if (!adc2 || !adc2->isOpen() || !dc1->isOpen())
-		throw runtime_error("DataChannel is not open");
+		return TestResult(false, "DataChannel is not open");
 
 	if (adc2->label() != "test")
-		throw runtime_error("Wrong DataChannel label");
+		return TestResult(false, "Wrong DataChannel label");
 
 	if (dc1->maxMessageSize() != CUSTOM_MAX_MESSAGE_SIZE ||
 	    dc2->maxMessageSize() != CUSTOM_MAX_MESSAGE_SIZE)
-		throw runtime_error("DataChannel max message size is incorrect");
+		return TestResult(false, "DataChannel max message size is incorrect");
 
 	if (!dc1->id().has_value())
-		throw runtime_error("DataChannel stream id is not assigned");
+		return TestResult(false, "DataChannel stream id is not assigned");
 
 	if (dc1->id().value() != adc2->id().value())
-		throw runtime_error("DataChannel stream ids do not match");
+		return TestResult(false, "DataChannel stream ids do not match");
 
 	if (auto addr = pc1.localAddress())
 		cout << "Local address 1:  " << *addr << endl;
@@ -244,16 +256,16 @@ void test_connectivity(bool signal_wrong_fingerprint) {
 		this_thread::sleep_for(1s);
 
 	if (!asecond2 || !asecond2->isOpen() || !second1->isOpen())
-		throw runtime_error("Second DataChannel is not open");
+		return TestResult(false, "Second DataChannel is not open");
 
 	if (asecond2->label() != "second")
-		throw runtime_error("Wrong second DataChannel label");
+		return TestResult(false, "Wrong second DataChannel label");
 
 	if (!second2->id().has_value() || !asecond2->id().has_value())
-		throw runtime_error("Second DataChannel stream id is not assigned");
+		return TestResult(false, "Second DataChannel stream id is not assigned");
 
 	if (second2->id().value() != asecond2->id().value())
-		throw runtime_error("Second DataChannel stream ids do not match");
+		return TestResult(false, "Second DataChannel stream ids do not match");
 
 	// Delay close of peer 2 to check closing works properly
 	pc1.close();
@@ -261,7 +273,7 @@ void test_connectivity(bool signal_wrong_fingerprint) {
 	pc2.close();
 	this_thread::sleep_for(1s);
 
-	cout << "Success" << endl;
+	return TestResult(true);
 }
 
 const char* key_pem =
@@ -284,7 +296,7 @@ const char* cert_pem =
 "Ma9ayzQy\n"
 "-----END CERTIFICATE-----\n";
 
-void test_pem() {
+TestResult test_pem() {
 	InitLogger(LogLevel::Debug);
 
 	Configuration config1;
@@ -310,8 +322,9 @@ void test_pem() {
 
 	cout << "Fingerprint: " << f << endl;
 
-	if (f != "07:E5:6F:2A:1A:0C:2C:32:0E:C1:C3:9C:34:5A:78:4E:A5:8B:32:05:D1:57:D6:F4:E7:02:41:12:E6:01:C6:8F")
-		throw runtime_error("The fingerprint of the specified certificate do not match");
+	if (f != "07:E5:6F:2A:1A:0C:2C:32:0E:C1:C3:9C:34:5A:78:4E:A5:8B:32:05:D1:57:D6:F4:E7:02:41:12:"
+	         "E6:01:C6:8F")
+		return TestResult(false, "The fingerprint of the specified certificate do not match");
 
-	cout << "Success" << endl;
+	return TestResult(true);
 }

+ 85 - 138
test/main.cpp

@@ -10,22 +10,29 @@
 #include <iostream>
 #include <thread>
 
+#include "test.hpp"
 #include <rtc/rtc.hpp>
 
 using namespace std;
 using namespace chrono_literals;
 
-void test_connectivity(bool signal_wrong_fingerprint);
-void test_pem();
-void test_negotiated();
-void test_reliability();
-void test_turn_connectivity();
-void test_track();
-void test_capi_connectivity();
-void test_capi_track();
-void test_websocket();
-void test_websocketserver();
-void test_capi_websocketserver();
+using chrono::duration_cast;
+using chrono::milliseconds;
+using chrono::seconds;
+using chrono::steady_clock;
+
+TestResult test_connectivity();
+TestResult test_connectivity_fail_on_wrong_fingerprint();
+TestResult test_pem();
+TestResult test_negotiated();
+TestResult test_reliability();
+TestResult test_turn_connectivity();
+TestResult test_track();
+TestResult test_capi_connectivity();
+TestResult test_capi_track();
+TestResult test_websocket();
+TestResult test_websocketserver();
+TestResult test_capi_websocketserver();
 size_t benchmark(chrono::milliseconds duration);
 
 void test_benchmark() {
@@ -39,149 +46,89 @@ void test_benchmark() {
 		throw runtime_error("Goodput is too low");
 }
 
-int main(int argc, char **argv) {
-	// C++ API tests
+TestResult testCppCleanup() {
 	try {
-		cout << endl << "*** Running WebRTC connectivity test..." << endl;
-		test_connectivity(false);
-		cout << "*** Finished WebRTC connectivity test" << endl;
+		// Every created object must have been destroyed, otherwise the wait will block
+		if (rtc::Cleanup().wait_for(10s) == future_status::timeout)
+			return TestResult(false, "timeout");
+		return TestResult(true);
 	} catch (const exception &e) {
-		cerr << "WebRTC connectivity test failed: " << e.what() << endl;
-		return -1;
-	}
-	try {
-		cout << endl << "*** Running WebRTC broken fingerprint test..." << endl;
-		test_connectivity(true);
-		cerr << "WebRTC connectivity test failed to detect broken fingerprint" << endl;
-		return -1;
-	} catch (const exception &) {
+		return TestResult(false, e.what());
 	}
+}
 
+TestResult testCCleanup() {
 	try {
-		cout << endl << "*** Running pem test..." << endl;
-		test_pem();
+		rtcCleanup();
+		return TestResult(true);
 	} catch (const exception &e) {
-		cerr << "pem test failed: " << e.what() << endl;
-		return -1;
+		return TestResult(false, e.what());
 	}
+}
 
-// TODO: Temporarily disabled as the Open Relay TURN server is unreliable
-/*
-	try {
-		cout << endl << "*** Running WebRTC TURN connectivity test..." << endl;
-		test_turn_connectivity();
-		cout << "*** Finished WebRTC TURN connectivity test" << endl;
-	} catch (const exception &e) {
-		cerr << "WebRTC TURN connectivity test failed: " << e.what() << endl;
-		return -1;
-	}
-*/
-	try {
-		cout << endl << "*** Running WebRTC negotiated DataChannel test..." << endl;
-		test_negotiated();
-		cout << "*** Finished WebRTC negotiated DataChannel test" << endl;
-	} catch (const exception &e) {
-		cerr << "WebRTC negotiated DataChannel test failed: " << e.what() << endl;
-		return -1;
-	}
-	try {
-		cout << endl << "*** Running WebRTC reliability mode test..." << endl;
-		test_reliability();
-		cout << "*** Finished WebRTC reliaility mode test" << endl;
-	} catch (const exception &e) {
-		cerr << "WebRTC reliability test failed: " << e.what() << endl;
-		return -1;
-	}
+static const vector<Test> tests = {
+    // C++ API tests
+    Test("WebRTC connectivity", test_connectivity),
+    Test("WebRTC broken fingerprint", test_connectivity_fail_on_wrong_fingerprint),
+    Test("pem", test_pem),
+    // TODO: Temporarily disabled as the Open Relay TURN server is unreliable
+    // new Test("WebRTC TURN connectivity", test_turn_connectivity),
+    Test("WebRTC negotiated DataChannel", test_negotiated),
+    Test("WebRTC reliability mode", test_reliability),
 #if RTC_ENABLE_MEDIA
-	try {
-		cout << endl << "*** Running WebRTC Track test..." << endl;
-		test_track();
-		cout << "*** Finished WebRTC Track test" << endl;
-	} catch (const exception &e) {
-		cerr << "WebRTC Track test failed: " << e.what() << endl;
-		return -1;
-	}
+    Test("WebRTC track", test_track),
 #endif
 #if RTC_ENABLE_WEBSOCKET
-// TODO: Temporarily disabled as the echo service is unreliable
-/*
-	try {
-		cout << endl << "*** Running WebSocket test..." << endl;
-		test_websocket();
-		cout << "*** Finished WebSocket test" << endl;
-	} catch (const exception &e) {
-		cerr << "WebSocket test failed: " << e.what() << endl;
-		return -1;
-	}
-*/
-	try {
-		cout << endl << "*** Running WebSocketServer test..." << endl;
-		test_websocketserver();
-		cout << "*** Finished WebSocketServer test" << endl;
-	} catch (const exception &e) {
-		cerr << "WebSocketServer test failed: " << e.what() << endl;
-		return -1;
-	}
+    // TODO: Temporarily disabled as the echo service is unreliable
+    // new Test("WebSocket", test_websocket),
+    Test("WebSocketServer", test_websocketserver),
 #endif
-	try {
-		// Every created object must have been destroyed, otherwise the wait will block
-		cout << endl << "*** Running cleanup..." << endl;
-		if(rtc::Cleanup().wait_for(10s) == future_status::timeout)
-			throw std::runtime_error("Timeout");
-		cout << "*** Finished cleanup..." << endl;
-	} catch (const exception &e) {
-		cerr << "Cleanup failed: " << e.what() << endl;
-		return -1;
-	}
-
-	// C API tests
-	try {
-		cout << endl << "*** Running WebRTC C API connectivity test..." << endl;
-		test_capi_connectivity();
-		cout << "*** Finished WebRTC C API connectivity test" << endl;
-	} catch (const exception &e) {
-		cerr << "WebRTC C API connectivity test failed: " << e.what() << endl;
-		return -1;
-	}
+    Test("WebRTC Cpp API cleanup", testCppCleanup),
+    // C API tests
+    Test("WebRTC C API connectivity", test_capi_connectivity),
 #if RTC_ENABLE_MEDIA
-	try {
-		cout << endl << "*** Running WebRTC C API track test..." << endl;
-		test_capi_track();
-		cout << "*** Finished WebRTC C API track test" << endl;
-	} catch (const exception &e) {
-		cerr << "WebRTC C API track test failed: " << e.what() << endl;
-		return -1;
-	}
+    Test("WebRTC C API track", test_capi_track),
 #endif
 #if RTC_ENABLE_WEBSOCKET
-	try {
-		cout << endl << "*** Running WebSocketServer C API test..." << endl;
-		test_capi_websocketserver();
-		cout << "*** Finished WebSocketServer C API test" << endl;
-	} catch (const exception &e) {
-		cerr << "WebSocketServer C API test failed: " << e.what() << endl;
-		return -1;
-	}
+    Test("WebSocketServer C API", test_capi_websocketserver),
 #endif
-	try {
-		cout << endl << "*** Running C API cleanup..." << endl;
-		rtcCleanup();
-		cout << "*** Finished C API cleanup..." << endl;
-	} catch (const exception &e) {
-		cerr << "C API cleanup failed: " << e.what() << endl;
-		return -1;
-	}
-/*
-	// Benchmark
-	try {
-		cout << endl << "*** Running WebRTC benchmark..." << endl;
-		test_benchmark();
-		cout << "*** Finished WebRTC benchmark" << endl;
-	} catch (const exception &e) {
-		cerr << "WebRTC benchmark failed: " << e.what() << endl;
-		std::this_thread::sleep_for(2s);
-		return -1;
+    Test("WebRTC C API cleanup", testCCleanup),
+};
+
+int main(int argc, char **argv) {
+	int success_tests = 0;
+	int failed_tests = 0;
+	steady_clock::time_point startTime, endTime;
+
+	startTime = steady_clock::now();
+
+	for (auto test : tests) {
+		auto res = test.run();
+		if (res.success) {
+			success_tests++;
+		} else {
+			failed_tests++;
+		}
 	}
-*/
+
+	endTime = steady_clock::now();
+
+	auto durationMs = duration_cast<milliseconds>(endTime - startTime);
+	auto durationS = duration_cast<seconds>(endTime - startTime);
+	cout << "Finished " << success_tests + failed_tests << " tests in " << durationS.count()
+	     << "s (" << durationMs.count() << " ms). Succeeded: " << success_tests
+	     << ". Failed: " << failed_tests << "." << endl;
+	/*
+	    // Benchmark
+	    try {
+	        cout << endl << "*** Running WebRTC benchmark..." << endl;
+	        test_benchmark();
+	        cout << "*** Finished WebRTC benchmark" << endl;
+	    } catch (const exception &e) {
+	        cerr << "WebRTC benchmark failed: " << e.what() << endl;
+	        std::this_thread::sleep_for(2s);
+	        return -1;
+	    }
+	*/
 	return 0;
 }

+ 6 - 5
test/negotiated.cpp

@@ -7,6 +7,7 @@
  */
 
 #include "rtc/rtc.hpp"
+#include "test.hpp"
 
 #include <atomic>
 #include <chrono>
@@ -17,7 +18,7 @@
 using namespace rtc;
 using namespace std;
 
-void test_negotiated() {
+TestResult test_negotiated() {
 	InitLogger(LogLevel::Debug);
 
 	Configuration config1;
@@ -66,10 +67,10 @@ void test_negotiated() {
 
 	if (pc1.state() != PeerConnection::State::Connected ||
 	    pc2.state() != PeerConnection::State::Connected)
-		throw runtime_error("PeerConnection is not connected");
+		return TestResult(false, "PeerConnection is not connected");
 
 	if (!negotiated1->isOpen() || !negotiated2->isOpen())
-		throw runtime_error("Negotiated DataChannel is not open");
+		return TestResult(false, "Negotiated DataChannel is not open");
 
 	std::atomic<bool> received = false;
 	negotiated2->onMessage([&received](const variant<binary, string> &message) {
@@ -87,7 +88,7 @@ void test_negotiated() {
 		this_thread::sleep_for(1s);
 
 	if (!received)
-		throw runtime_error("Negotiated DataChannel failed");
+		return TestResult(false, "Negotiated DataChannel failed");
 
 	// Delay close of peer 2 to check closing works properly
 	pc1.close();
@@ -95,5 +96,5 @@ void test_negotiated() {
 	pc2.close();
 	this_thread::sleep_for(1s);
 
-	cout << "Success" << endl;
+	return TestResult(true);
 }

+ 6 - 5
test/reliability.cpp

@@ -7,6 +7,7 @@
  */
 
 #include "rtc/rtc.hpp"
+#include "test.hpp"
 
 #include <atomic>
 #include <chrono>
@@ -17,7 +18,7 @@
 using namespace rtc;
 using namespace std;
 
-void test_reliability() {
+TestResult test_reliability() {
 	InitLogger(LogLevel::Debug);
 
 	Configuration config1;
@@ -114,15 +115,15 @@ void test_reliability() {
 
 	if (pc1.state() != PeerConnection::State::Connected ||
 	    pc2.state() != PeerConnection::State::Connected)
-		throw runtime_error("PeerConnection is not connected");
+		return TestResult(false, "PeerConnection is not connected");
 
 	if (failed)
-		throw runtime_error("Incorrect reliability settings");
+		return TestResult(false, "Incorrect reliability settings");
 
 	if (count != 4)
-		throw runtime_error("Some DataChannels are not open");
+		return TestResult(false, "Some DataChannels are not open");
 
 	pc1.close();
 
-	cout << "Success" << endl;
+	return TestResult(true);
 }

+ 40 - 0
test/test.hpp

@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2025 Michal Sledz
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <functional>
+#include <iostream>
+
+using namespace std;
+
+class TestResult {
+public:
+	bool success;
+	string err_reason;
+
+	TestResult(bool success, string err_reason = "") : success(success), err_reason(err_reason) {}
+};
+
+class Test {
+public:
+	string name;
+	function<TestResult(void)> f;
+
+	Test(string name, std::function<TestResult(void)> testFunc) : name(name), f(testFunc) {}
+
+	TestResult run() {
+		cout << endl << "*** Running " << name << " test" << endl;
+		TestResult res = this->f();
+		if (res.success) {
+			cout << "*** Finished " << name << " test" << endl;
+		} else {
+			cerr << name << " test failed. Reason: " << res.err_reason << endl;
+		}
+
+		return res;
+	}
+};

+ 8 - 7
test/track.cpp

@@ -8,6 +8,7 @@
 
 #include "rtc/rtc.hpp"
 #include "rtc/rtp.hpp"
+#include "test.hpp"
 
 #include <atomic>
 #include <cstring>
@@ -21,7 +22,7 @@ using namespace std;
 
 template <class T> weak_ptr<T> make_weak_ptr(shared_ptr<T> ptr) { return ptr; }
 
-void test_track() {
+TestResult test_track() {
 	InitLogger(LogLevel::Debug);
 
 	Configuration config1;
@@ -109,7 +110,7 @@ void test_track() {
 	const auto mediaSdp2 = string(Description::Media(mediaSdp1));
 	if (mediaSdp2 != mediaSdp1) {
 		cout << mediaSdp2 << endl;
-		throw runtime_error("Media description parsing test failed");
+		return TestResult(false, "Media description parsing test failed");
 	}
 
 	auto t1 = pc1.addTrack(media);
@@ -123,10 +124,10 @@ void test_track() {
 
 	if (pc1.state() != PeerConnection::State::Connected ||
 	    pc2.state() != PeerConnection::State::Connected)
-		throw runtime_error("PeerConnection is not connected");
+		return TestResult(false, "PeerConnection is not connected");
 
 	if (!at2 || !at2->isOpen() || !t1->isOpen())
-		throw runtime_error("Track is not open");
+		return TestResult(false, "Track is not open");
 
 	// Test renegotiation
 	newTrackMid = "added";
@@ -148,7 +149,7 @@ void test_track() {
 		this_thread::sleep_for(1s);
 
 	if (!at2 || !at2->isOpen() || !t1->isOpen())
-		throw runtime_error("Renegotiated track is not open");
+		return TestResult(false, "Renegotiated track is not open");
 
 	std::vector<std::byte> payload = {std::byte{0}, std::byte{1}, std::byte{2}, std::byte{3}};
 	std::vector<std::byte> rtpRaw(sizeof(RtpHeader) + payload.size());
@@ -187,7 +188,7 @@ void test_track() {
 	this_thread::sleep_for(1s);
 
 	if (!t1->isClosed() || !t2->isClosed())
-		throw runtime_error("Track is not closed");
+		return TestResult(false, "Track is not closed");
 
-	cout << "Success" << endl;
+	return TestResult(true);
 }

+ 11 - 10
test/turn_connectivity.cpp

@@ -7,6 +7,7 @@
  */
 
 #include "rtc/rtc.hpp"
+#include "test.hpp"
 
 #include <atomic>
 #include <chrono>
@@ -19,7 +20,7 @@ using namespace std;
 
 template <class T> weak_ptr<T> make_weak_ptr(shared_ptr<T> ptr) { return ptr; }
 
-void test_turn_connectivity() {
+TestResult test_turn_connectivity() {
 	InitLogger(LogLevel::Debug);
 
 	Configuration config1;
@@ -133,16 +134,16 @@ void test_turn_connectivity() {
 
 	if (pc1.state() != PeerConnection::State::Connected ||
 	    pc2.state() != PeerConnection::State::Connected)
-		throw runtime_error("PeerConnection is not connected");
+		return TestResult(false, "PeerConnection is not connected");
 
 	if ((pc1.iceState() != PeerConnection::IceState::Connected &&
 	     pc1.iceState() != PeerConnection::IceState::Completed) ||
 	    (pc2.iceState() != PeerConnection::IceState::Connected &&
 	     pc2.iceState() != PeerConnection::IceState::Completed))
-		throw runtime_error("ICE is not connected");
+		return TestResult(false, "ICE is not connected");
 
 	if (!adc2 || !adc2->isOpen() || !dc1->isOpen())
-		throw runtime_error("DataChannel is not open");
+		return TestResult(false, "DataChannel is not open");
 
 	if (auto addr = pc1.localAddress())
 		cout << "Local address 1:  " << *addr << endl;
@@ -155,13 +156,13 @@ void test_turn_connectivity() {
 
 	Candidate local, remote;
 	if (!pc1.getSelectedCandidatePair(&local, &remote))
-		throw runtime_error("getSelectedCandidatePair failed");
+		return TestResult(false, "getSelectedCandidatePair failed");
 
 	cout << "Local candidate 1:  " << local << endl;
 	cout << "Remote candidate 1: " << remote << endl;
 
 	if (local.type() != Candidate::Type::Relayed)
-		throw runtime_error("Connection is not relayed as expected");
+		return TestResult(false, "Connection is not relayed as expected");
 
 	// Try to open a second data channel with another label
 	shared_ptr<DataChannel> second2;
@@ -214,7 +215,7 @@ void test_turn_connectivity() {
 		this_thread::sleep_for(1s);
 
 	if (!asecond2 || !asecond2->isOpen() || !second1->isOpen())
-		throw runtime_error("Second DataChannel is not open");
+		return TestResult(false, "Second DataChannel is not open");
 
 	// Try to open a negotiated channel
 	DataChannelInit init;
@@ -224,7 +225,7 @@ void test_turn_connectivity() {
 	auto negotiated2 = pc2.createDataChannel("negoctated", init);
 
 	if (!negotiated1->isOpen() || !negotiated2->isOpen())
-		throw runtime_error("Negotiated DataChannel is not open");
+		return TestResult(false, "Negotiated DataChannel is not open");
 
 	std::atomic<bool> received = false;
 	negotiated2->onMessage([&received](const variant<binary, string> &message) {
@@ -242,7 +243,7 @@ void test_turn_connectivity() {
 		this_thread::sleep_for(1s);
 
 	if (!received)
-		throw runtime_error("Negotiated DataChannel failed");
+		return TestResult(false, "Negotiated DataChannel failed");
 
 	// Delay close of peer 2 to check closing works properly
 	pc1.close();
@@ -250,5 +251,5 @@ void test_turn_connectivity() {
 	pc2.close();
 	this_thread::sleep_for(1s);
 
-	cout << "Success" << endl;
+	return TestResult(true);
 }

+ 5 - 4
test/websocket.cpp

@@ -7,6 +7,7 @@
  */
 
 #include "rtc/rtc.hpp"
+#include "test.hpp"
 
 #if RTC_ENABLE_WEBSOCKET
 
@@ -21,7 +22,7 @@ using namespace std;
 
 template <class T> weak_ptr<T> make_weak_ptr(shared_ptr<T> ptr) { return ptr; }
 
-void test_websocket() {
+TestResult test_websocket() {
 	InitLogger(LogLevel::Debug);
 
 	const string myMessage = "Hello world from libdatachannel";
@@ -57,15 +58,15 @@ void test_websocket() {
 		this_thread::sleep_for(1s);
 
 	if (!ws.isOpen())
-		throw runtime_error("WebSocket is not open");
+		return TestResult(false, "WebSocket is not open");
 
 	if (!received)
-		throw runtime_error("Expected message not received");
+		return TestResult(false, "Expected message not received");
 
 	ws.close();
 	this_thread::sleep_for(1s);
 
-	cout << "Success" << endl;
+	return TestResult(true);
 }
 
 #endif

+ 11 - 13
test/websocketserver.cpp

@@ -7,6 +7,7 @@
  */
 
 #include "rtc/rtc.hpp"
+#include "test.hpp"
 
 #if RTC_ENABLE_WEBSOCKET
 
@@ -21,7 +22,7 @@ using namespace std;
 
 template <class T> weak_ptr<T> make_weak_ptr(shared_ptr<T> ptr) { return ptr; }
 
-void test_websocketserver() {
+TestResult test_websocketserver() {
 	InitLogger(LogLevel::Debug);
 
 	WebSocketServer::Configuration serverConfig;
@@ -38,22 +39,20 @@ void test_websocketserver() {
 		cout << "WebSocketServer: Client connection received" << endl;
 		client = incoming;
 
-		if(auto addr = client->remoteAddress())
+		if (auto addr = client->remoteAddress())
 			cout << "WebSocketServer: Client remote address is " << *addr << endl;
 
 		client->onOpen([wclient = make_weak_ptr(client)]() {
 			cout << "WebSocketServer: Client connection open" << endl;
-			if(auto client = wclient.lock())
-				if(auto path = client->path())
+			if (auto client = wclient.lock())
+				if (auto path = client->path())
 					cout << "WebSocketServer: Requested path is " << *path << endl;
 		});
 
-		client->onClosed([]() {
-			cout << "WebSocketServer: Client connection closed" << endl;
-		});
+		client->onClosed([]() { cout << "WebSocketServer: Client connection closed" << endl; });
 
 		client->onMessage([wclient = make_weak_ptr(client)](variant<binary, string> message) {
-			if(auto client = wclient.lock())
+			if (auto client = wclient.lock())
 				client->send(std::move(message));
 		});
 	});
@@ -81,8 +80,7 @@ void test_websocketserver() {
 				cout << "WebSocket: Received expected message" << endl;
 			else
 				cout << "WebSocket: Received UNEXPECTED message" << endl;
-		}
-		else {
+		} else {
 			binary bin = std::move(get<binary>(message));
 			if ((maxSizeReceived = (bin.size() == 1000)))
 				cout << "WebSocket: Received large message truncated at max size" << endl;
@@ -98,10 +96,10 @@ void test_websocketserver() {
 		this_thread::sleep_for(1s);
 
 	if (!ws.isOpen())
-		throw runtime_error("WebSocket is not open");
+		return TestResult(false, "WebSocket is not open");
 
 	if (!received || !maxSizeReceived)
-		throw runtime_error("Expected messages not received");
+		return TestResult(false, "Expected messages not received");
 
 	ws.close();
 	this_thread::sleep_for(1s);
@@ -109,7 +107,7 @@ void test_websocketserver() {
 	server.stop();
 	this_thread::sleep_for(1s);
 
-	cout << "Success" << endl;
+	return TestResult(true);
 }
 
 #endif