Skip to content

Commit 7c65a75

Browse files
committed
fix(tests): fix concurrency issues with integration tests
1 parent 6a0b6c5 commit 7c65a75

File tree

3 files changed

+48
-9
lines changed

3 files changed

+48
-9
lines changed

libcoap/tests/common/mod.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ use libcoap_rs::{CoapContext, CoapRequestHandler, CoapResource};
66
use std::net::{SocketAddr, UdpSocket};
77
use std::rc::Rc;
88
use std::sync::atomic::{AtomicBool, Ordering};
9+
use std::sync::{Arc, Condvar, Mutex};
10+
use std::thread::JoinHandle;
911
use std::time::Duration;
1012

1113
pub(crate) fn get_unused_server_addr() -> SocketAddr {
@@ -25,6 +27,39 @@ pub(crate) fn get_unused_server_addr() -> SocketAddr {
2527
.expect("Failed to get server socket address")
2628
}
2729

30+
/// Spawns a test server in a new thread and waits for context_configurator to complete before
31+
/// returning.
32+
/// As the context_configurator closure is responsible for binding to sockets, this can be used to
33+
/// spawn a test server and wait for it to be ready to accept requests before returning (avoiding
34+
/// test failure due to "Connection Refused" errors).
35+
pub(crate) fn spawn_test_server<F: FnOnce(&mut CoapContext) + Send + 'static>(
36+
context_configurator: F,
37+
) -> JoinHandle<()> {
38+
let ready_condition = Arc::new((Mutex::new(false), Condvar::new()));
39+
let ready_condition2 = Arc::clone(&ready_condition);
40+
41+
let server_handle = std::thread::spawn(move || {
42+
let (ready_var, ready_cond) = &*ready_condition2;
43+
run_test_server(|context| {
44+
context_configurator(context);
45+
let mut ready_var = ready_var.lock().expect("ready condition mutex is poisoned");
46+
*ready_var = true;
47+
ready_cond.notify_all();
48+
});
49+
});
50+
51+
let (ready_var, ready_cond) = &*ready_condition;
52+
drop(
53+
ready_cond
54+
.wait_while(ready_var.lock().expect("ready condition mutex is poisoned"), |ready| {
55+
!*ready
56+
})
57+
.expect("ready condition mutex is poisoned"),
58+
);
59+
server_handle
60+
}
61+
62+
/// Configures and starts a test server in the current thread.
2863
pub(crate) fn run_test_server<F: FnOnce(&mut CoapContext)>(context_configurator: F) {
2964
let mut context = CoapContext::new().unwrap();
3065
context_configurator(&mut context);

libcoap/tests/dtls_client_server_test.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#![cfg(feature = "dtls")]
22
use std::fmt::Debug;
3+
use std::sync::{Arc, Condvar, Mutex};
34
use std::time::Duration;
45

56
use libcoap_rs::crypto::{
@@ -20,6 +21,13 @@ mod common;
2021
struct DummyCryptoProvider;
2122

2223
impl CoapServerCryptoProvider for DummyCryptoProvider {
24+
fn provide_key_for_identity(
25+
&mut self,
26+
identity: &CoapCryptoPskIdentity,
27+
) -> CoapCryptoProviderResponse<Box<CoapCryptoPskData>> {
28+
CoapCryptoProviderResponse::UseCurrent
29+
}
30+
2331
fn provide_default_info(&mut self) -> CoapCryptoPskInfo {
2432
CoapCryptoPskInfo {
2533
identity: String::from("dtls_test_identity").into_boxed_str().into(),
@@ -48,11 +56,9 @@ impl CoapClientCryptoProvider for DummyCryptoProvider {
4856
pub fn dtls_client_server_request() {
4957
let server_address = common::get_unused_server_addr();
5058

51-
let server_handle = std::thread::spawn(move || {
52-
common::run_test_server(|context| {
53-
context.set_server_crypto_provider(Some(Box::new(DummyCryptoProvider {})));
54-
context.add_endpoint_dtls(server_address).unwrap();
55-
});
59+
let server_handle = common::spawn_test_server(move |context| {
60+
context.set_server_crypto_provider(Some(Box::new(DummyCryptoProvider {})));
61+
context.add_endpoint_dtls(server_address).unwrap();
5662
});
5763

5864
let mut context = CoapContext::new().unwrap();
@@ -65,7 +71,7 @@ pub fn dtls_client_server_request() {
6571
for response in session.poll_handle(&req_handle) {
6672
assert_eq!(response.code(), CoapMessageCode::Response(CoapResponseCode::Content));
6773
assert_eq!(response.data().unwrap().as_ref(), "Hello World!".as_bytes());
68-
server_handle.join().unwrap();
74+
server_handle.join().expect("Test server crashed with failure.");
6975
return;
7076
}
7177
}

libcoap/tests/udp_client_server_test.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,7 @@ mod common;
1313
pub fn basic_client_server_request() {
1414
let server_address = common::get_unused_server_addr();
1515

16-
let server_handle = std::thread::spawn(move || {
17-
common::run_test_server(|context| context.add_endpoint_udp(server_address).unwrap());
18-
});
16+
let server_handle = common::spawn_test_server(move |context| context.add_endpoint_udp(server_address).unwrap());
1917

2018
let mut context = CoapContext::new().unwrap();
2119
let session = CoapClientSession::connect_udp(&mut context, server_address).unwrap();

0 commit comments

Comments
 (0)