为磁盘上的文件对生成单元测试
Generating unit tests for pairs of files on disk
几个问题(比如How can I create parameterized tests in Rust?) deal with using macros to create parameterised unit tests in Rust. I need to use this technique to generate a pair of unit tests for every pair of input files in a directory。单元测试本身只是调用了一个简单的函数:
fn check_files(path1: &str, path2: &str, msg: &str) {
assert!(true, "FAILURE: {}: {} and {}.", msg, path1, path2);
}
我使用 lazy_static
生成输入文件列表:
#![feature(plugin)]
#![plugin(interpolate_idents)]
extern crate glob;
#[macro_use]
extern crate lazy_static;
use glob::glob;
lazy_static! {
/// Glob all example files in the `tests/` directory.
static ref TEST_FILES: Vec<String> = glob("tests/*.java")
.expect("Failed to read glob pattern")
.into_iter()
.map(|res| res.unwrap().to_str().unwrap().to_string())
.collect::<Vec<String>>();
}
然后宏使用 interpolate idents 板条箱连接标识符以创建单元测试名称:
#[test]
fn test_glob_runner() {
// Define unit tests for a single pair of filenames.
macro_rules! define_tests {
($name1:tt, $name2:tt, $fname1:expr, $fname2:expr) => ( interpolate_idents! {
#[test]
fn [test_globbed_ $name1 _ $name2 _null]() {
check_files($fname1, $fname2, "null test");
}
#[test]
fn [test_globbed_ $name1 _ $name2 _non_null]() {
check_files($fname1, $fname2, "non-null test");
}
} )
}
// Write out unit tests for all pairs of given list of filenames.
macro_rules! test_globbed_files {
($d:expr) => {
for fname1 in $d.iter() {
for fname2 in $d.iter() {
// Remove directory and extension from `fname1`, `fname2`.
let name1 = &fname1[6..].split(".").next().unwrap();
let name2 = &fname1[6..].split(".").next().unwrap();
|| { define_tests!(name1, name2, fname1, fname2) };
}
}
}
}
// Test all pairs of files in the `tests/` directory.
test_globbed_files!(TEST_FILES);
}
这会产生以下编译器错误:
error: expected expression, found keyword `fn`
--> tests/test.rs:14:13
|
14 | fn [test_globbed_ $name1 _ $name2 _null]() {
| ^^
这个错误消息对我来说意义不大,尤其是因为 define_tests
宏与 code here 相似。但是,我不确定是否真的可以在单元测试名称中使用 name1
和 name2
。
有个complete but simplified example project on GitHub,直接clone然后运行 cargo test
看编译报错
您在参数化测试中尝试的方法的问题在于 TEST_FILES
仅在运行时计算,而您希望能够在编译时使用它来消除几个 #[test]
函数。
为了使这项工作有效,您将需要一些方法在编译时计算 TEST_FILES
。一种可能性是通过一个构建脚本,该脚本在构建时迭代 glob 并将 #[test]
函数写出到一个可以从您的测试目录中包含的文件中。
在Cargo.toml
中:
[package]
# ...
build = "build.rs"
[build-dependencies]
glob = "0.2"
在build.rs
中:
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::Path;
extern crate glob;
use glob::glob;
fn main() {
let test_files = glob("tests/*.java")
.expect("Failed to read glob pattern")
.into_iter();
let outfile_path = Path::new(&env::var("OUT_DIR").unwrap()).join("gen_tests.rs");
let mut outfile = File::create(outfile_path).unwrap();
for file in test_files {
let java_file = file.unwrap().to_str().unwrap().to_string();
// FIXME: fill these in with your own logic for manipulating the filename.
let name = java_file;
let name1 = "NAME1";
let name2 = "NAME2";
write!(outfile, r#"
#[test]
fn test_globbed_{name}_null() {{
check_files({name1}, {name2}, "null test");
}}
#[test]
fn test_globbed_{name}_non_null() {{
check_files({name1}, {name2}, "non-null test");
}}
"#, name=name, name1=name1, name2=name2).unwrap();
}
}
在tests/tests.rs
中:
include!(concat!(env!("OUT_DIR"), "/gen_tests.rs"));
几个问题(比如How can I create parameterized tests in Rust?) deal with using macros to create parameterised unit tests in Rust. I need to use this technique to generate a pair of unit tests for every pair of input files in a directory。单元测试本身只是调用了一个简单的函数:
fn check_files(path1: &str, path2: &str, msg: &str) {
assert!(true, "FAILURE: {}: {} and {}.", msg, path1, path2);
}
我使用 lazy_static
生成输入文件列表:
#![feature(plugin)]
#![plugin(interpolate_idents)]
extern crate glob;
#[macro_use]
extern crate lazy_static;
use glob::glob;
lazy_static! {
/// Glob all example files in the `tests/` directory.
static ref TEST_FILES: Vec<String> = glob("tests/*.java")
.expect("Failed to read glob pattern")
.into_iter()
.map(|res| res.unwrap().to_str().unwrap().to_string())
.collect::<Vec<String>>();
}
然后宏使用 interpolate idents 板条箱连接标识符以创建单元测试名称:
#[test]
fn test_glob_runner() {
// Define unit tests for a single pair of filenames.
macro_rules! define_tests {
($name1:tt, $name2:tt, $fname1:expr, $fname2:expr) => ( interpolate_idents! {
#[test]
fn [test_globbed_ $name1 _ $name2 _null]() {
check_files($fname1, $fname2, "null test");
}
#[test]
fn [test_globbed_ $name1 _ $name2 _non_null]() {
check_files($fname1, $fname2, "non-null test");
}
} )
}
// Write out unit tests for all pairs of given list of filenames.
macro_rules! test_globbed_files {
($d:expr) => {
for fname1 in $d.iter() {
for fname2 in $d.iter() {
// Remove directory and extension from `fname1`, `fname2`.
let name1 = &fname1[6..].split(".").next().unwrap();
let name2 = &fname1[6..].split(".").next().unwrap();
|| { define_tests!(name1, name2, fname1, fname2) };
}
}
}
}
// Test all pairs of files in the `tests/` directory.
test_globbed_files!(TEST_FILES);
}
这会产生以下编译器错误:
error: expected expression, found keyword `fn`
--> tests/test.rs:14:13
|
14 | fn [test_globbed_ $name1 _ $name2 _null]() {
| ^^
这个错误消息对我来说意义不大,尤其是因为 define_tests
宏与 code here 相似。但是,我不确定是否真的可以在单元测试名称中使用 name1
和 name2
。
有个complete but simplified example project on GitHub,直接clone然后运行 cargo test
看编译报错
您在参数化测试中尝试的方法的问题在于 TEST_FILES
仅在运行时计算,而您希望能够在编译时使用它来消除几个 #[test]
函数。
为了使这项工作有效,您将需要一些方法在编译时计算 TEST_FILES
。一种可能性是通过一个构建脚本,该脚本在构建时迭代 glob 并将 #[test]
函数写出到一个可以从您的测试目录中包含的文件中。
在Cargo.toml
中:
[package]
# ...
build = "build.rs"
[build-dependencies]
glob = "0.2"
在build.rs
中:
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::Path;
extern crate glob;
use glob::glob;
fn main() {
let test_files = glob("tests/*.java")
.expect("Failed to read glob pattern")
.into_iter();
let outfile_path = Path::new(&env::var("OUT_DIR").unwrap()).join("gen_tests.rs");
let mut outfile = File::create(outfile_path).unwrap();
for file in test_files {
let java_file = file.unwrap().to_str().unwrap().to_string();
// FIXME: fill these in with your own logic for manipulating the filename.
let name = java_file;
let name1 = "NAME1";
let name2 = "NAME2";
write!(outfile, r#"
#[test]
fn test_globbed_{name}_null() {{
check_files({name1}, {name2}, "null test");
}}
#[test]
fn test_globbed_{name}_non_null() {{
check_files({name1}, {name2}, "non-null test");
}}
"#, name=name, name1=name1, name2=name2).unwrap();
}
}
在tests/tests.rs
中:
include!(concat!(env!("OUT_DIR"), "/gen_tests.rs"));