Skip to content

Commit 5d227dc

Browse files
committed
Move mp4parse_capi's build_ffi_test to new test_ffi crate.
The existing process invoked a Makefile from a Rust test, building test.cc and a statically linkable mp4parse_capi, then running a series of C API tests. This wasn't very portable due the reliable on a Makefile, which also made assumptions about the layout of cargo's build artifacts. The new process introduces a new crate (test_ffi) and changes mp4parse_capi to additionally build as a cdylib. A build.rs in test_ffi builds test.cc (which must now be written with a simple library API, rather than as a binary with a main() due to cc-rs limitations) into a static libtest, which is then linked to test_ffi's Rust binary in main.rs. test.cc is now split into run_main for the binary dumping tool and test_main for the API tests. `cargo run -p test_ffi path/to/a.mp4` invokes run_main, and test_ffi's ffi_test invokes test_main. Fixes mozilla#197
1 parent 5720602 commit 5d227dc

File tree

11 files changed

+113
-97
lines changed

11 files changed

+113
-97
lines changed

.travis.yml

+3-5
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,12 @@ before_script:
1818
- if [[ "$TRAVIS_RUST_VERSION" != *nightly* ]]; then rustup component add rustfmt; fi
1919

2020
script:
21-
- cargo test --all --verbose $RELEASE
22-
# We cannot run `cargo test` with the mp4parse_infallible feature enabled
21+
# We cannot run `cargo test` with the mp4parse_fallible feature enabled
2322
# (see comment where the feature is defined in mp4parse_capi/Cargo.toml),
2423
# but we can at least check for changes behind features that would break the
25-
# build. Also note that we can't run `cargo check` before `cargo test` or
26-
# else it breaks `build_ffi_test` for complicated reasons.
27-
# See https://github.com/mozilla/mp4parse-rust/issues/197
24+
# build.
2825
- cargo check --all --verbose $RELEASE --tests --all-features
26+
- cargo test --all --verbose $RELEASE
2927
- cargo doc --package mp4parse_capi
3028
# The `false` after `echo` is necessary to avoid false negatives due to `echo` returning success
3129
- if [[ "$TRAVIS_RUST_VERSION" != *nightly* ]]; then cargo fmt -- --check || (echo "Please reformat your code with 'cargo fmt' (version $(cargo fmt -- --version))"; false); fi

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
members = [
33
"mp4parse",
44
"mp4parse_capi",
5+
"test_ffi",
56
]
67

78
[profile.release]

README.md

+5-8
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ API is intended to wrap the rust parser. As such, features should primarily
1717
be implemented in the rust parser and exposed via the C API, rather than the C
1818
API implementing features on its own.
1919

20+
`test_ffi` builds and links `test_ffi/src/test.cc` into a small Rust test
21+
harness in `test_ffi/src/main.rs` that verifies the C API against the
22+
generated `mp4parse.h`.
23+
2024
## Tests
2125

2226
Test coverage comes from several sources:
@@ -25,11 +29,4 @@ Test coverage comes from several sources:
2529
`mp4parse_capi/tests`. These tests can be run via `cargo test`.
2630
- Examples are included under `mp4parse_capi/examples`. These programs should
2731
continue to build and run after changes are made. Note, these programs are not
28-
typically run by `cargo test`, so manual verification is required. However,
29-
`test.cc` is used to test the foreign function interface via
30-
`build_ffi_test.rs` on non-Windows platforms and will be run by `cargo test`.
31-
- Examples with `afl` related names are for use with the
32-
[american fuzzy lop](http://lcamtuf.coredump.cx/afl/) fuzzer. These examples can
33-
be run without `afl`, but they can be built specifically to receive input from
34-
`afl` via by setting the `fuzz` feature when building. E.g. `cargo build
35-
--features fuzz` would build the examples with fuzzing options.
32+
typically run by `cargo test`, so manual verification is required.

mp4parse_capi/Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ exclude = [
1818
"*.mp4",
1919
]
2020

21+
[lib]
22+
crate-type = ["lib", "cdylib"]
23+
2124
[badges]
2225
travis-ci = { repository = "https://github.com/mozilla/mp4parse-rust" }
2326

mp4parse_capi/build.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ extern crate cbindgen;
33
use cbindgen::{Config, RenameRule};
44

55
fn main() {
6-
let crate_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
7-
86
println!("cargo:rerun-if-changed=src/lib.rs");
97

108
let config = {
@@ -41,6 +39,7 @@ extern "C" {
4139
};
4240

4341
// Generate mp4parse.h.
42+
let crate_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
4443
cbindgen::generate_with_config(&crate_dir, config)
4544
.expect("Could not generate header")
4645
.write_to_file("include/mp4parse.h");

mp4parse_capi/examples/Makefile

-47
This file was deleted.

mp4parse_capi/tests/build_ffi_test.rs

-20
This file was deleted.

test_ffi/Cargo.toml

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[package]
2+
name = "test_ffi"
3+
version = "0.1.0"
4+
authors = ["Matthew Gregan <kinetik@flim.org>"]
5+
edition = "2018"
6+
7+
[dependencies]
8+
mp4parse_capi = { path = "../mp4parse_capi" }
9+
10+
[build-dependencies]
11+
mp4parse_capi = { path = "../mp4parse_capi" }
12+
cc = "1.0"

test_ffi/build.rs

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
extern crate cc;
2+
3+
fn main() {
4+
println!("cargo:rerun-if-changed=src/main.rs");
5+
println!("cargo:rerun-if-changed=src/test.cc");
6+
7+
cc::Build::new()
8+
.file("src/test.cc")
9+
.cpp(true)
10+
.flag_if_supported("-std=c++11")
11+
.include("../mp4parse_capi/include")
12+
.compile("libtest.a");
13+
14+
#[cfg(unix)]
15+
let suffix = "";
16+
#[cfg(windows)]
17+
let suffix = ".dll";
18+
println!("cargo:rustc-link-lib=dylib=mp4parse_capi{}", suffix);
19+
20+
let profile = std::env::var("PROFILE").unwrap();
21+
println!("cargo:rustc-link-search=native=target/{}", profile);
22+
}

test_ffi/src/main.rs

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
use std::convert::TryInto as _;
2+
use std::ffi::CString;
3+
use std::os::raw::{c_char, c_int};
4+
5+
fn main() {
6+
#[link(name = "test", kind = "static")]
7+
extern "C" {
8+
fn run_main(npaths: c_int, paths: *const *const c_char) -> c_int;
9+
}
10+
11+
let args: Vec<_> = std::env::args()
12+
.map(|arg| CString::new(arg).unwrap())
13+
.collect();
14+
let argv = {
15+
let mut a: Vec<_> = args.iter().map(|arg| arg.as_ptr()).collect();
16+
a.push(std::ptr::null());
17+
a
18+
};
19+
20+
let argc = argv.len() - 1;
21+
unsafe {
22+
assert_eq!(run_main(argc.try_into().unwrap(), argv.as_ptr()), 0);
23+
}
24+
}
25+
26+
#[test]
27+
fn ffi_test() {
28+
use std::path::PathBuf;
29+
30+
extern "C" {
31+
fn test_main(test_path: *const c_char) -> c_int;
32+
}
33+
34+
const TEST_PATH: &str = "../mp4parse/tests/minimal.mp4";
35+
36+
let path = {
37+
let base = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()).join(TEST_PATH);
38+
let path = base.canonicalize().unwrap();
39+
40+
#[cfg(unix)]
41+
{
42+
use std::os::unix::ffi::OsStrExt as _;
43+
CString::new(path.as_os_str().as_bytes()).unwrap()
44+
}
45+
#[cfg(windows)]
46+
{
47+
CString::new(path.to_str().unwrap()).unwrap()
48+
}
49+
};
50+
51+
unsafe {
52+
assert_eq!(test_main(path.as_ptr()), 0);
53+
}
54+
}

mp4parse_capi/examples/test.cc test_ffi/src/test.cc

+12-15
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
#include "mp4parse.h"
1818

19-
intptr_t error_read(uint8_t *buffer, uintptr_t size, void *userdata)
19+
intptr_t error_read(uint8_t *, uintptr_t, void *)
2020
{
2121
return -1;
2222
}
@@ -91,9 +91,9 @@ void test_arg_validation_with_parser()
9191
assert(dummy_value == 42);
9292
}
9393

94-
void test_arg_validation_with_data(const std::string& filename)
94+
void test_arg_validation_with_data(const char* filename)
9595
{
96-
FILE* f = fopen(filename.c_str(), "rb");
96+
FILE* f = fopen(filename, "rb");
9797
assert(f != nullptr);
9898
Mp4parseIo io = { io_read, f };
9999
Mp4parseParser *parser = nullptr;
@@ -210,23 +210,20 @@ int32_t read_file(const char* filename)
210210
return MP4PARSE_STATUS_OK;
211211
}
212212

213-
int main(int argc, char* argv[])
213+
extern "C"
214+
int test_main(const char* test_path)
214215
{
215-
// Parse command line options.
216-
std::vector<std::string> args(argv + 1, argv + argc);
217-
218216
test_arg_validation();
219217
test_arg_validation_with_parser();
218+
test_arg_validation_with_data(test_path);
219+
return 0;
220+
}
220221

221-
// Find our test file relative to our executable file path.
222-
char* real = realpath(argv[0], NULL);
223-
std::string path(real);
224-
free(real);
225-
auto split = path.rfind('/');
226-
path.replace(split, path.length() - split, "/../../mp4parse/tests/minimal.mp4");
227-
test_arg_validation_with_data(path);
222+
extern "C"
223+
int run_main(int argc, char* argv[])
224+
{
225+
std::vector<std::string> args(argv + 1, argv + argc);
228226

229-
// Run any other test files passed on the command line.
230227
for (auto arg: args) {
231228
read_file(arg.c_str());
232229
}

0 commit comments

Comments
 (0)