Skip to content

Commit 1d91dd3

Browse files
committed
Avoid uses of Receiver::recv_timeout to avoid panic in tests
cf. rust-lang/rust#39364 Also take this opportunity to centralize the expect threads.
1 parent 89a9dfb commit 1d91dd3

File tree

3 files changed

+103
-67
lines changed

3 files changed

+103
-67
lines changed

src/util.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
1+
2+
use std;
13
use std::collections::HashMap;
4+
use std::sync::mpsc::{self, Receiver};
5+
use std::thread;
26

37
use time;
48

9+
use errors::*;
10+
511
fn escape_for_slack(str: &str) -> String {
612
str.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
713
}
@@ -88,6 +94,33 @@ pub fn parse_query(query_params: Option<&str>) -> HashMap<String, String> {
8894
.collect::<HashMap<_, _>>()
8995
}
9096

97+
// cf. https://github.com/rust-lang/rust/issues/39364
98+
pub fn recv_timeout<T>(rx: &Receiver<T>, timeout: std::time::Duration) -> Result<T> {
99+
let sleep_time = std::time::Duration::from_millis(50);
100+
let mut time_left = timeout;
101+
loop {
102+
match rx.try_recv() {
103+
Ok(r) => {
104+
return Ok(r);
105+
}
106+
Err(mpsc::TryRecvError::Empty) => {
107+
match time_left.checked_sub(sleep_time) {
108+
Some(sub) => {
109+
time_left = sub;
110+
thread::sleep(sleep_time);
111+
}
112+
None => {
113+
return Err("Timed out waiting".into());
114+
}
115+
}
116+
}
117+
Err(mpsc::TryRecvError::Disconnected) => {
118+
return Err("Channel disconnected!".into());
119+
}
120+
};
121+
}
122+
}
123+
91124
#[cfg(test)]
92125
mod tests {
93126
use super::*;

tests/github_handler_test.rs

Lines changed: 68 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ use octobot::repo_version::RepoVersionRequest;
2525
use octobot::repos;
2626
use octobot::server::github_handler::GithubEventHandler;
2727
use octobot::slack::{self, SlackAttachmentBuilder};
28+
use octobot::util;
2829
use octobot::worker::{WorkMessage, WorkSender};
2930

3031
use mocks::mock_github::MockGithub;
@@ -56,7 +57,9 @@ impl GithubHandlerTest {
5657
let rx = self.pr_merge_rx.take().unwrap();
5758
thread::spawn(move || {
5859
for branch in branches {
59-
let msg = rx.recv_timeout(timeout).expect(&format!("expected to recv msg for branch: {}", branch));
60+
let msg = util::recv_timeout(&rx, timeout).expect(
61+
&format!("expected to recv msg for branch: {}", branch),
62+
);
6063
match msg {
6164
WorkMessage::WorkItem(req) => {
6265
assert_eq!(branch, req.target_branch);
@@ -67,7 +70,61 @@ impl GithubHandlerTest {
6770
};
6871
}
6972

70-
let last_message = rx.recv_timeout(timeout);
73+
let last_message = util::recv_timeout(&rx, timeout);
74+
assert!(last_message.is_err());
75+
})
76+
}
77+
78+
fn expect_will_force_push_notify(&mut self, before_hash: &'static str, after_hash: &'static str) -> JoinHandle<()> {
79+
let timeout = Duration::from_millis(300);
80+
let rx = self.force_push_rx.take().expect("force-push message");
81+
thread::spawn(move || {
82+
let msg = util::recv_timeout(&rx, timeout).expect(&format!(
83+
"expected to recv force-push for {} -> {}",
84+
before_hash,
85+
after_hash
86+
));
87+
match msg {
88+
WorkMessage::WorkItem(req) => {
89+
assert_eq!(before_hash, req.before_hash);
90+
assert_eq!(after_hash, req.after_hash);
91+
}
92+
_ => {
93+
panic!("Unexpected messages: {:?}", msg);
94+
}
95+
};
96+
97+
let last_message = util::recv_timeout(&rx, timeout);
98+
assert!(last_message.is_err());
99+
})
100+
}
101+
102+
fn expect_will_not_force_push_notify(&mut self) -> JoinHandle<()> {
103+
let timeout = Duration::from_millis(300);
104+
let rx = self.force_push_rx.take().expect("force-push message");
105+
thread::spawn(move || {
106+
let last_message = util::recv_timeout(&rx, timeout);
107+
assert!(last_message.is_err());
108+
})
109+
}
110+
111+
fn expect_will_run_version_script(&mut self, branch: &'static str, commit_hash: &'static str) -> JoinHandle<()> {
112+
// Note: no expectations are set on mock_jira since we have stubbed out the background worker thread
113+
let timeout = Duration::from_millis(300);
114+
let rx = self.repo_version_rx.take().unwrap();
115+
thread::spawn(move || {
116+
let msg = util::recv_timeout(&rx, timeout).expect(&format!("expected to recv version script msg"));
117+
match msg {
118+
WorkMessage::WorkItem(req) => {
119+
assert_eq!(branch, req.branch);
120+
assert_eq!(commit_hash, req.commit_hash);
121+
}
122+
_ => {
123+
panic!("Unexpected messages: {:?}", msg);
124+
}
125+
};
126+
127+
let last_message = util::recv_timeout(&rx, timeout);
71128
assert!(last_message.is_err());
72129
})
73130
}
@@ -1239,26 +1296,7 @@ fn test_push_force_notify() {
12391296
]);
12401297

12411298
// Setup background thread to validate force-push msg
1242-
let expect_thread;
1243-
{
1244-
let timeout = Duration::from_millis(300);
1245-
let rx = test.force_push_rx.take().expect("force-push message");
1246-
expect_thread = thread::spawn(move || {
1247-
let msg = rx.recv_timeout(timeout).expect(&format!("expected to recv msg"));
1248-
match msg {
1249-
WorkMessage::WorkItem(req) => {
1250-
assert_eq!("abcdef0000", req.before_hash);
1251-
assert_eq!("1111abcdef", req.after_hash);
1252-
}
1253-
_ => {
1254-
panic!("Unexpected messages: {:?}", msg);
1255-
}
1256-
};
1257-
1258-
let last_message = rx.recv_timeout(timeout);
1259-
assert!(last_message.is_err());
1260-
});
1261-
}
1299+
let expect_thread = test.expect_will_force_push_notify("abcdef0000", "1111abcdef");
12621300

12631301
let resp = test.handler.handle_event().expect("handled event");
12641302
assert_eq!((StatusCode::Ok, "push".into()), resp);
@@ -1296,15 +1334,7 @@ fn test_push_force_notify_wip() {
12961334
// Note: not slack expectations here. It should not notify slack for WIP PRs.
12971335

12981336
// Setup background thread to validate force-push msg
1299-
let expect_thread;
1300-
{
1301-
let timeout = Duration::from_millis(300);
1302-
let rx = test.force_push_rx.take().unwrap();
1303-
expect_thread = thread::spawn(move || {
1304-
let last_message = rx.recv_timeout(timeout);
1305-
assert!(last_message.is_err());
1306-
});
1307-
}
1337+
let expect_thread = test.expect_will_not_force_push_notify();
13081338

13091339
let resp = test.handler.handle_event().unwrap();
13101340
assert_eq!((StatusCode::Ok, "push".into()), resp);
@@ -1358,15 +1388,7 @@ fn test_push_force_notify_ignored() {
13581388
]);
13591389

13601390
// Setup background thread to validate force-push msg
1361-
let expect_thread;
1362-
{
1363-
let timeout = Duration::from_millis(300);
1364-
let rx = test.force_push_rx.take().unwrap();
1365-
expect_thread = thread::spawn(move || {
1366-
let last_message = rx.recv_timeout(timeout);
1367-
assert!(last_message.is_err());
1368-
});
1369-
}
1391+
let expect_thread = test.expect_will_not_force_push_notify();
13701392

13711393
let resp = test.handler.handle_event().unwrap();
13721394
assert_eq!((StatusCode::Ok, "push".into()), resp);
@@ -1416,11 +1438,11 @@ fn some_jira_commits() -> Vec<Commit> {
14161438

14171439
fn many_jira_commits() -> Vec<Commit> {
14181440
let commit = Commit {
1419-
sha: "ffeedd00110011".into(),
1420-
html_url: "http://commit/ffeedd00110011".into(),
1421-
author: Some(User::new("bob-author")),
1422-
commit: CommitDetails { message: "Fix [SER-1] Add the feature\n\nThe body ([OTHER-123])".into() },
1423-
};
1441+
sha: "ffeedd00110011".into(),
1442+
html_url: "http://commit/ffeedd00110011".into(),
1443+
author: Some(User::new("bob-author")),
1444+
commit: CommitDetails { message: "Fix [SER-1] Add the feature\n\nThe body ([OTHER-123])".into() },
1445+
};
14241446

14251447
return (0..21).collect::<Vec<u32>>().into_iter().map(|_| commit.clone()).collect();
14261448
}
@@ -1657,27 +1679,7 @@ fn test_jira_push_triggers_version_script() {
16571679
test.handler.data.commits = Some(some_jira_push_commits());
16581680

16591681
// Setup background thread to validate version msg
1660-
// Note: no expectations are set on mock_jira since we have stubbed out the background worker thread
1661-
let expect_thread;
1662-
{
1663-
let timeout = Duration::from_millis(300);
1664-
let rx = test.repo_version_rx.take().unwrap();
1665-
expect_thread = thread::spawn(move || {
1666-
let msg = rx.recv_timeout(timeout).expect(&format!("expected to recv msg"));
1667-
match msg {
1668-
WorkMessage::WorkItem(req) => {
1669-
assert_eq!("master", req.branch);
1670-
assert_eq!("1111abcdef", req.commit_hash);
1671-
}
1672-
_ => {
1673-
panic!("Unexpected messages: {:?}", msg);
1674-
}
1675-
};
1676-
1677-
let last_message = rx.recv_timeout(timeout);
1678-
assert!(last_message.is_err());
1679-
});
1680-
}
1682+
let expect_thread = test.expect_will_run_version_script("master", "1111abcdef");
16811683

16821684
let resp = test.handler.handle_event().unwrap();
16831685
assert_eq!((StatusCode::Ok, "push".into()), resp);

tests/mocks/mock_slack.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use std::thread::{self, JoinHandle};
44
use std::time::Duration;
55

66
use octobot::slack::SlackRequest;
7+
use octobot::util;
78
use octobot::worker::{WorkMessage, WorkSender};
89

910
pub struct MockSlack {
@@ -25,7 +26,7 @@ impl MockSlack {
2526
let thread = Some(thread::spawn(move || {
2627
let timeout = Duration::from_millis(1000);
2728
loop {
28-
let req = slack_rx.recv_timeout(timeout);
29+
let req = util::recv_timeout(&slack_rx, timeout);
2930
match req {
3031
Ok(WorkMessage::WorkItem(req)) => {
3132
let front = expected_calls2.lock().unwrap().pop();

0 commit comments

Comments
 (0)