Skip to content

Commit f9eef12

Browse files
committed
feat: split nginx test util into optional module
1 parent 28d5f2c commit f9eef12

File tree

3 files changed

+347
-0
lines changed

3 files changed

+347
-0
lines changed

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ default = ["vendored"]
3333
# This could be disabled with `--no-default-features` to minimize the dependency tree
3434
# when building against an existing copy of the NGINX with the NGX_OBJS variable.
3535
vendored = ["nginx-sys/vendored"]
36+
# test utility
37+
test_util = []
38+
3639

3740
[badges]
3841
maintenance = { status = "experimental" }

src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,12 @@ pub mod http;
5656
/// This module provides an interface into the NGINX logger framework.
5757
pub mod log;
5858

59+
/// The test utility module.
60+
///
61+
/// This module provides utilities for integration tests with bundled NGINX.
62+
#[cfg(feature = "test_util")]
63+
pub mod test_util;
64+
5965
/// Define modules exported by this library.
6066
///
6167
/// These are normally generated by the Nginx module system, but need to be

src/test_util.rs

Lines changed: 338 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,338 @@
1+
use std::borrow::Cow;
2+
use std::ffi::CStr;
3+
use std::fs;
4+
use std::fs::read_dir;
5+
use std::io::Result;
6+
use std::os::unix::ffi::OsStrExt;
7+
use std::path::Path;
8+
use std::path::PathBuf;
9+
use std::process::Command;
10+
use std::process::Output;
11+
12+
use crate::ffi::{
13+
NGX_CONF_PATH, NGX_CONF_PREFIX, NGX_ERROR_LOG_PATH, NGX_HTTP_CLIENT_TEMP_PATH, NGX_HTTP_FASTCGI_TEMP_PATH,
14+
NGX_HTTP_LOG_PATH, NGX_HTTP_PROXY_TEMP_PATH, NGX_HTTP_SCGI_TEMP_PATH, NGX_HTTP_UWSGI_TEMP_PATH, NGX_LOCK_PATH,
15+
NGX_PID_PATH, NGX_PREFIX, NGX_SBIN_PATH,
16+
};
17+
18+
/// Convert a CStr to a PathBuf
19+
pub fn cstr_to_path(val: &std::ffi::CStr) -> Option<Cow<Path>> {
20+
if val.is_empty() {
21+
return None;
22+
}
23+
24+
#[cfg(unix)]
25+
{
26+
let str = std::ffi::OsStr::from_bytes(val.to_bytes());
27+
Some(Cow::Borrowed(str.as_ref()))
28+
}
29+
#[cfg(not(unix))]
30+
{
31+
let str = std::str::from_utf8(val.to_bytes()).ok()?;
32+
Some(&str.as_ref())
33+
}
34+
}
35+
36+
fn target_dir_cands() -> Option<Vec<PathBuf>> {
37+
#[cfg(target_os = "macos")]
38+
{
39+
match std::env::var("DYLD_FALLBACK_LIBRARY_PATH") {
40+
Ok(cands) => Some(cands.split(':').map(PathBuf::from).collect()),
41+
Err(_) => None,
42+
}
43+
}
44+
#[cfg(target_os = "linux")]
45+
{
46+
match std::env::var("LD_LIBRARY_PATH") {
47+
Ok(cands) => Some(cands.split(':').map(PathBuf::from).collect()),
48+
Err(_) => None,
49+
}
50+
}
51+
}
52+
53+
/// search path and return the path to the target
54+
pub fn target_path(target_name: &str) -> std::io::Result<PathBuf> {
55+
if let Some(cands) = target_dir_cands() {
56+
for dir in cands {
57+
if let Ok(iter) = read_dir(dir) {
58+
for entry in iter.flatten() {
59+
if entry.file_name() == target_name {
60+
return Ok(entry.path());
61+
}
62+
}
63+
}
64+
}
65+
}
66+
Err(std::io::ErrorKind::NotFound.into())
67+
}
68+
69+
/// harness to test nginx
70+
#[allow(dead_code)]
71+
pub struct Nginx {
72+
// these paths have options to change them from default paths (in prefix dir)
73+
// most of them are not used, but keep them for future uses
74+
prefix: PathBuf,
75+
sbin_path: PathBuf,
76+
modules_prefix: PathBuf, // and only this path is not embedded in bindings.rs, since the module root is same to the prefix
77+
conf_path: PathBuf,
78+
conf_prefix: PathBuf,
79+
error_log_path: PathBuf,
80+
pid_path: PathBuf,
81+
lock_path: PathBuf,
82+
http_log_path: PathBuf,
83+
http_client_body_temp_path: PathBuf,
84+
http_proxy_temp_path: PathBuf,
85+
http_fastcgi_temp_path: PathBuf,
86+
http_uwsgi_temp_path: PathBuf,
87+
http_scgi_temp_path: PathBuf,
88+
// here all path are absolute
89+
}
90+
91+
/// nginx harness builder
92+
pub struct NginxBuilder {
93+
prefix: PathBuf,
94+
sbin_path: PathBuf,
95+
modules_prefix: PathBuf,
96+
conf_path: PathBuf,
97+
conf_prefix: PathBuf,
98+
error_log_path: PathBuf,
99+
pid_path: PathBuf,
100+
lock_path: PathBuf,
101+
http_log_path: PathBuf,
102+
http_client_body_temp_path: PathBuf,
103+
http_proxy_temp_path: PathBuf,
104+
http_fastcgi_temp_path: PathBuf,
105+
http_uwsgi_temp_path: PathBuf,
106+
http_scgi_temp_path: PathBuf,
107+
// in builder path could be relative
108+
}
109+
110+
impl Default for NginxBuilder {
111+
fn default() -> Self {
112+
fn conv(raw_path: &CStr) -> Option<PathBuf> {
113+
cstr_to_path(raw_path).map(|p| p.to_path_buf())
114+
}
115+
Self {
116+
prefix: conv(NGX_PREFIX).expect("installation prefix"),
117+
sbin_path: conv(NGX_SBIN_PATH).expect("nginx executable path"),
118+
modules_prefix: conv(NGX_PREFIX).expect("module prefix"),
119+
conf_path: conv(NGX_CONF_PATH).expect("configuration file path"),
120+
conf_prefix: conv(NGX_CONF_PREFIX).expect("configuration file prefix"),
121+
error_log_path: conv(NGX_ERROR_LOG_PATH).expect("error log file path"),
122+
pid_path: conv(NGX_PID_PATH).expect("pid file path"),
123+
lock_path: conv(NGX_LOCK_PATH).expect("lock file path"),
124+
http_log_path: conv(NGX_HTTP_LOG_PATH).expect("http log file path"),
125+
http_client_body_temp_path: conv(NGX_HTTP_CLIENT_TEMP_PATH).expect("client body temp file path"),
126+
http_proxy_temp_path: conv(NGX_HTTP_PROXY_TEMP_PATH).expect("proxy temp file path"),
127+
http_fastcgi_temp_path: conv(NGX_HTTP_FASTCGI_TEMP_PATH).expect("fastcgi temp file path"),
128+
http_uwsgi_temp_path: conv(NGX_HTTP_UWSGI_TEMP_PATH).expect("uwsgi temp file path"),
129+
http_scgi_temp_path: conv(NGX_HTTP_SCGI_TEMP_PATH).expect("scgi temp file path"),
130+
}
131+
}
132+
}
133+
134+
impl NginxBuilder {
135+
/// set alternative configuration path
136+
pub fn conf_path(mut self, path: PathBuf) -> Self {
137+
self.conf_path = path;
138+
self
139+
}
140+
141+
/// build nginx harness
142+
pub fn build(self) -> Nginx {
143+
let prefix = self.prefix;
144+
145+
let add_prefix = |p: PathBuf| -> PathBuf {
146+
if p.is_relative() {
147+
prefix.join(p)
148+
} else {
149+
p.to_path_buf()
150+
}
151+
};
152+
153+
let sbin_path = add_prefix(self.sbin_path);
154+
let modules_path = add_prefix(self.modules_prefix);
155+
let conf_path = add_prefix(self.conf_path);
156+
let conf_prefix = add_prefix(self.conf_prefix);
157+
let error_log_path = add_prefix(self.error_log_path);
158+
let pid_path = add_prefix(self.pid_path);
159+
let lock_path = add_prefix(self.lock_path);
160+
let http_log_path = add_prefix(self.http_log_path);
161+
let http_client_body_temp_path = add_prefix(self.http_client_body_temp_path);
162+
let http_proxy_temp_path = add_prefix(self.http_proxy_temp_path);
163+
let http_fastcgi_temp_path = add_prefix(self.http_fastcgi_temp_path);
164+
let http_uwsgi_temp_path = add_prefix(self.http_uwsgi_temp_path);
165+
let http_scgi_temp_path = add_prefix(self.http_scgi_temp_path);
166+
167+
Nginx {
168+
prefix,
169+
sbin_path,
170+
modules_prefix: modules_path,
171+
conf_path,
172+
conf_prefix,
173+
error_log_path,
174+
pid_path,
175+
lock_path,
176+
http_log_path,
177+
http_client_body_temp_path,
178+
http_proxy_temp_path,
179+
http_fastcgi_temp_path,
180+
http_uwsgi_temp_path,
181+
http_scgi_temp_path,
182+
}
183+
}
184+
}
185+
186+
impl Nginx {
187+
/// execute nginx process with arguments
188+
pub fn cmd(&mut self, args: &[&str]) -> Result<Output> {
189+
let result = Command::new(&self.sbin_path).args(args).output();
190+
191+
match result {
192+
Err(e) => Err(e),
193+
194+
Ok(output) => {
195+
println!("status: {}", output.status);
196+
println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
197+
println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
198+
Ok(output)
199+
}
200+
}
201+
}
202+
203+
/// complete stop the nginx binary
204+
pub fn stop(&mut self) -> Result<Output> {
205+
self.cmd(&["-s", "stop"])
206+
}
207+
208+
/// start the nginx binary
209+
pub fn start(&mut self) -> Result<Output> {
210+
self.cmd(&[])
211+
}
212+
213+
/// make sure we stop existing nginx and start new master process
214+
/// intentinally ignore failure in stop
215+
pub fn restart(&mut self) -> Result<Output> {
216+
let _ = self.stop();
217+
self.start()
218+
}
219+
220+
/// replace main config with another config
221+
pub fn copy_main_config<P: AsRef<Path>>(&mut self, conf_path_from: P) -> Result<u64> {
222+
let conf_path_to = &self.conf_path;
223+
println!(
224+
"copying main config from: {} to: {}",
225+
conf_path_from.as_ref().display(),
226+
conf_path_to.display()
227+
); // replace with logging
228+
fs::copy(conf_path_from, conf_path_to)
229+
}
230+
231+
/// replace config with another config
232+
pub fn copy_config<P: AsRef<Path>, Q: AsRef<Path>>(
233+
&mut self,
234+
conf_path_from: P,
235+
conf_path_rel_to: Q,
236+
) -> Result<u64> {
237+
if conf_path_rel_to.as_ref().is_relative() {
238+
let conf_path_to = self.conf_prefix.join(conf_path_rel_to.as_ref());
239+
println!(
240+
"copying config from: {} to: {}",
241+
conf_path_from.as_ref().display(),
242+
conf_path_to.display()
243+
); // replace with logging
244+
fs::copy(conf_path_from, conf_path_to)
245+
} else {
246+
panic!("absolute path");
247+
}
248+
}
249+
/// create config from &str
250+
pub fn create_config_from_str<Q: AsRef<Path>>(&mut self, conf_path_rel_to: Q, conf_content: &str) -> Result<()> {
251+
if conf_path_rel_to.as_ref().is_relative() {
252+
let conf_path_to = self.conf_prefix.join(conf_path_rel_to.as_ref());
253+
println!(
254+
"creating config to: {} content: {}",
255+
conf_path_to.display(),
256+
conf_content
257+
); // replace with logging
258+
fs::write(conf_path_to, conf_content)
259+
} else {
260+
panic!("absolute path");
261+
}
262+
}
263+
/// copy or replace module
264+
pub fn copy_module<P: AsRef<Path>, Q: AsRef<Path>>(
265+
&mut self,
266+
module_path_from: P,
267+
module_path_rel_to: Q,
268+
) -> Result<u64> {
269+
if module_path_rel_to.as_ref().is_relative() {
270+
let module_path_to = self.modules_prefix.join(module_path_rel_to.as_ref());
271+
println!(
272+
"copying module from: {} to: {}",
273+
module_path_from.as_ref().display(),
274+
module_path_to.display()
275+
); // replace with logging
276+
fs::copy(module_path_from, module_path_to)
277+
} else {
278+
panic!("absolute path");
279+
}
280+
}
281+
282+
/// get prefix of nginx instance
283+
pub fn prefix(&self) -> &Path {
284+
&self.prefix
285+
}
286+
/// get bin path to nginx instance
287+
pub fn bin_path(&self) -> &Path {
288+
&self.sbin_path
289+
}
290+
/// get module prefix
291+
pub fn modules_prefix(&self) -> &Path {
292+
&self.modules_prefix
293+
}
294+
/// get configuration file path
295+
pub fn conf_path(&self) -> &Path {
296+
&self.conf_path
297+
}
298+
/// get configuration file prefix
299+
pub fn conf_prefix(&self) -> &Path {
300+
&self.conf_prefix
301+
}
302+
/// get error log file path
303+
pub fn error_log_path(&self) -> &Path {
304+
&self.error_log_path
305+
}
306+
/// get pid file path
307+
pub fn pid_path(&self) -> &Path {
308+
&self.pid_path
309+
}
310+
/// get lock file path
311+
pub fn lock_path(&self) -> &Path {
312+
&self.lock_path
313+
}
314+
/// get http log file path
315+
pub fn http_log_path(&self) -> &Path {
316+
&self.http_log_path
317+
}
318+
/// get client body temp file path
319+
pub fn http_client_body_temp_path(&self) -> &Path {
320+
&self.http_client_body_temp_path
321+
}
322+
/// get proxy temp file path
323+
pub fn http_proxy_temp_path(&self) -> &Path {
324+
&self.http_proxy_temp_path
325+
}
326+
/// get fastcgi temp file path
327+
pub fn http_fastcgi_temp_path(&self) -> &Path {
328+
&self.http_fastcgi_temp_path
329+
}
330+
/// get uwsgi temp file path
331+
pub fn http_uwsgi_temp_path(&self) -> &Path {
332+
&self.http_uwsgi_temp_path
333+
}
334+
/// get scgi temp file path
335+
pub fn http_scgi_temp_path(&self) -> &Path {
336+
&self.http_scgi_temp_path
337+
}
338+
}

0 commit comments

Comments
 (0)