Skip to content

Commit 661cd0e

Browse files
authored
Add integration test infrastructure for example modules (#137)
1 parent 5bd9f73 commit 661cd0e

File tree

3 files changed

+130
-0
lines changed

3 files changed

+130
-0
lines changed

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ num-traits = "^0.2"
5757
strum_macros = "0.21"
5858
#failure = "0.1"
5959

60+
[dev-dependencies]
61+
anyhow = "1.0.38"
62+
redis = "0.20.0"
63+
6064
[build-dependencies]
6165
bindgen = "0.58"
6266
cc = "1.0"

tests/integration.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
mod utils;
2+
3+
use anyhow::Result;
4+
use utils::{get_redis_connection, prepare_dirs, start_redis_server_with_module};
5+
6+
#[test]
7+
#[ignore] // test will be run explicitly by a script
8+
fn test_hello() -> Result<()> {
9+
prepare_dirs()?;
10+
11+
let _guards = vec![start_redis_server_with_module("hello")?];
12+
let mut con = get_redis_connection()?;
13+
14+
let res: Vec<i32> = redis::cmd("hello.mul").arg(&[3, 4]).query(&mut con)?;
15+
assert_eq!(res, vec![3, 4, 12]);
16+
17+
Ok(())
18+
}

tests/utils.rs

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
use anyhow::{Context, Result};
2+
3+
use redis::Connection;
4+
use std::fs;
5+
use std::io::ErrorKind;
6+
use std::path::PathBuf;
7+
use std::process::Command;
8+
use std::time::Duration;
9+
10+
const LOG_DIR: &str = "tests/log";
11+
const REDIS_PORT: u16 = 6666;
12+
13+
/// Ensure child process is killed both on normal exit and when panicking due to a failed test.
14+
pub struct ChildGuard {
15+
name: &'static str,
16+
child: std::process::Child,
17+
}
18+
19+
impl Drop for ChildGuard {
20+
fn drop(&mut self) {
21+
if let Err(e) = self.child.kill() {
22+
println!("Could not kill {}: {}", self.name, e)
23+
}
24+
if let Err(e) = self.child.wait() {
25+
println!("Could not wait for {}: {}", self.name, e)
26+
}
27+
}
28+
}
29+
30+
pub fn prepare_dirs() -> Result<()> {
31+
let dirs = &[LOG_DIR];
32+
33+
for dir in dirs {
34+
remove_tree(dir).context(dir)?;
35+
fs::create_dir(dir).context(dir)?;
36+
}
37+
38+
Ok(())
39+
}
40+
41+
fn remove_tree(dir: &str) -> Result<()> {
42+
match fs::remove_dir_all(dir) {
43+
Err(e) if e.kind() == ErrorKind::NotFound => Ok(()),
44+
res => res.with_context(|| dir.to_string()),
45+
}
46+
}
47+
48+
pub fn start_redis_server_with_module(module_name: &str) -> Result<ChildGuard> {
49+
let extension = if cfg!(target_os = "macos") {
50+
"dylib"
51+
} else {
52+
"so"
53+
};
54+
55+
let module_path: PathBuf = [
56+
std::env::current_dir()?,
57+
PathBuf::from(format!(
58+
"target/debug/examples/lib{}.{}",
59+
module_name, extension
60+
)),
61+
]
62+
.iter()
63+
.collect();
64+
65+
assert!(fs::metadata(&module_path)
66+
.with_context(|| format!("Loading redis module: {}", module_path.display()))?
67+
.is_file());
68+
69+
let module_path = format!("{}", module_path.display());
70+
let port = REDIS_PORT.to_string();
71+
72+
#[rustfmt::skip]
73+
let args = &[
74+
"--port", port.as_str(),
75+
"--loadmodule", module_path.as_str(),
76+
"--dir", LOG_DIR,
77+
"--logfile", "redis.log",
78+
];
79+
80+
let redis_server = Command::new("redis-server")
81+
.args(args)
82+
.spawn()
83+
.map(|c| ChildGuard {
84+
name: "redis_server",
85+
child: c,
86+
})?;
87+
88+
Ok(redis_server)
89+
}
90+
91+
// Get connection to Redis
92+
pub fn get_redis_connection() -> Result<Connection> {
93+
let client = redis::Client::open(format!("redis://127.0.0.1:{}/", REDIS_PORT))?;
94+
loop {
95+
let res = client.get_connection();
96+
match res {
97+
Ok(con) => return Ok(con),
98+
Err(e) => {
99+
if e.is_connection_refusal() {
100+
// Redis not ready yet, sleep and retry
101+
std::thread::sleep(Duration::from_millis(50));
102+
} else {
103+
Err(e)?;
104+
}
105+
}
106+
}
107+
}
108+
}

0 commit comments

Comments
 (0)