Skip to content

Commit

Permalink
Merge pull request #43 from fitzgen/libiberty-test-compat
Browse files Browse the repository at this point in the history
Initial import of libiberty tests
  • Loading branch information
fitzgen authored Feb 5, 2017
2 parents c77592a + 9e75c27 commit a4f6aee
Show file tree
Hide file tree
Showing 5 changed files with 4,833 additions and 16 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ Cargo.lock
*.rs.bk
out
tests/afl_seeds.rs
tests/libiberty.rs
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ version = "0.1.5"

[features]
fuzz = ["afl", "afl-plugin"]
libiberty_compat = []
logging = []
nightly = []

Expand Down
147 changes: 131 additions & 16 deletions build.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,39 @@
use std::env;
use std::fs;
use std::io::{self, Write};
use std::io::{self, BufRead, Write};
use std::path;

// Generate tests that ensure that we don't panic when parsing and demangling
// the seed test cases that we pass to AFL.rs assert (including the failing test
// cases historically found by AFL.rs).
fn get_crate_dir() -> io::Result<path::PathBuf> {
Ok(path::PathBuf::from(try!(env::var("CARGO_MANIFEST_DIR")
.map_err(|_| io::Error::new(io::ErrorKind::Other, "no CARGO_MANIFEST_DIR")))))
}

fn get_test_path(file_name: &str) -> io::Result<path::PathBuf> {
let mut test_path = try!(get_crate_dir());
test_path.push("tests");
assert!(test_path.is_dir());
test_path.push(file_name);
Ok(test_path)
}

/// Generate tests that ensure that we don't panic when parsing and demangling
/// the seed test cases that we pass to AFL.rs assert (including the failing
/// test cases historically found by AFL.rs).
fn generate_sanity_tests_from_afl_seeds() -> io::Result<()> {
println!("cargo:rerun-if-changed=in/*");
println!("cargo:rerun-if-changed=tests/afl_seeds.rs");

let crate_dir = try!(env::var("CARGO_MANIFEST_DIR")
.map_err(|_| io::Error::new(io::ErrorKind::Other, "no CARGO_MANIFEST_DIR")));

let mut test_path = path::PathBuf::from(&crate_dir);
test_path.push("tests");
let _ = fs::create_dir(&test_path);
test_path.push("afl_seeds.rs");
let test_path = try!(get_test_path("afl_seeds.rs"));
let mut test_file = try!(fs::File::create(test_path));

try!(writeln!(&mut test_file, "
try!(writeln!(&mut test_file,
"
extern crate cpp_demangle;
use std::fs;
use std::io::Read;
"));

let mut in_dir = path::PathBuf::from(crate_dir);
let mut in_dir = try!(get_crate_dir());
in_dir.push("in");
assert!(in_dir.is_dir());

Expand All @@ -35,13 +43,12 @@ use std::io::Read;
let entry = try!(entry);

let path = entry.path();
let file_name = try!(path
.file_name()
let file_name = try!(path.file_name()
.ok_or(io::Error::new(io::ErrorKind::Other,
"no file name for AFL.rs seed test case")));

try!(writeln!(&mut test_file,
r#"
r#"
#[test]
fn test_afl_seed_{}() {{
let mut file = fs::File::open("{}").unwrap();
Expand All @@ -58,9 +65,117 @@ fn test_afl_seed_{}() {{
Ok(())
}

/// Read `tests/libiberty-demangle-expected`, parse its input mangled symbols,
/// and expected output demangled symbols, and generate test cases for them.
///
/// We do not support all of the options that the libiberty demangler does,
/// therefore we skip tests that use options we do not intend to
/// support. Basically, we only support `--format=gnu-v3` (which is the System V
/// C++ ABI), and none of the legacy C/C++ compiler formats, nor Java/D/etc
/// language symbol mangling.
fn generate_compatibility_tests_from_libiberty() -> io::Result<()> {
println!("cargo:rerun-if-changed=tests/libiberty-demangle-expected");
println!("cargo:rerun-if-changed=tests/libiberty.rs");

let test_path = try!(get_test_path("libiberty.rs"));
let _ = fs::remove_file(&test_path);
let mut test_file = try!(fs::File::create(test_path));

try!(writeln!(&mut test_file,
"
extern crate cpp_demangle;
"));

let libiberty_tests = try!(get_test_path("libiberty-demangle-expected"));
let libiberty_tests = try!(fs::File::open(libiberty_tests));
let libiberty_tests = io::BufReader::new(libiberty_tests);

let mut lines = libiberty_tests.lines()
.filter(|line| {
line.as_ref()
.map(|l| !l.starts_with('#'))
.unwrap_or(true)
});

let mut n = 0;

loop {
let options = match lines.next() {
None => break,
Some(Ok(line)) => line,
Some(Err(e)) => return Err(e),
};

let mangled = match lines.next() {
Some(Ok(line)) => line,
None => {
return Err(io::Error::new(io::ErrorKind::Other,
"expected a line with a mangled symbol"))
}
Some(Err(e)) => return Err(e),
};

let demangled = match lines.next() {
Some(Ok(line)) => line,
None => {
return Err(io::Error::new(io::ErrorKind::Other,
"expected a line with the demangled symbol"))
}
Some(Err(e)) => return Err(e),
};

if options.find("--no-params").is_some() {
// This line is the expected demangled output without function and
// template parameters, but we don't currently have such an option
// in `cpp_demangle`, so just consume and ignore the line.
match lines.next() {
Some(Ok(_)) => {}
None => {
return Err(io::Error::new(io::ErrorKind::Other,
"expected a line with the demangled symbol without parameters"))
}
Some(Err(e)) => return Err(e),
}
}

// Skip tests for unsupported languages or options.
if options.find("--format=gnu-v3").is_none() ||
options.find("--is-v3-ctor").is_some() ||
options.find("--is-v3-dtor").is_some() ||
options.find("--ret-postfix").is_some() {
continue;
}

try!(writeln!(test_file,
r###"
#[test]
#[cfg(feature = "libiberty_compat")]
fn test_libiberty_demangle_{}() {{
let mangled = br#"{}"#;
println!("Parsing symbol: {{}}", String::from_utf8_lossy(mangled));
let sym = cpp_demangle::Symbol::new(&mangled[..])
.expect("should parse mangled symbol");
let expected = r#"{}"#;
let actual = format!("{{}}", sym);
assert_eq!(expected, actual);
}}
"###,
n,
mangled.trim(),
demangled.trim()));

n += 1;
}

Ok(())
}

fn main() {
println!("cargo:rerun-if-changed=build.rs");

generate_sanity_tests_from_afl_seeds()
.expect("should generate sanity tests from AFL.rs seed test cases");

generate_compatibility_tests_from_libiberty()
.expect("should generate compatibility tests from tests/libiberty-demangle-expected");
}
9 changes: 9 additions & 0 deletions tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# `libiberty-demangle-expected`

This file is copied from gcc, revision 244567:
https://gcc.gnu.org/viewcvs/gcc/trunk/libiberty/testsuite/demangle-expected?revision=244567&view=markup

It is licensed under the GNU Lesser General Public License Version 2.1:
https://gcc.gnu.org/viewcvs/gcc/trunk/libiberty/COPYING.LIB?revision=184997&view=markup

We include it here under the "mere aggregation" clause.
Loading

0 comments on commit a4f6aee

Please sign in to comment.