blob: c2b8740366de34980945cef68fbd79a057b5f02d [file] [log] [blame]
#!/bin/bash
# Part of the Crubit project, under the Apache License v2.0 with LLVM
# Exceptions. See /LICENSE for license information.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
# This file provides test coverage for aspects of `cc_bindings_from_rs.rs` that
# are not covered by the native Rust tests (from the same source file -
# `cc_bindings_from_rs.rs`).
source gbash.sh || exit
source module gbash_unit.sh
readonly STDERR_PATH="${TEST_TMPDIR}/stderr.txt"
readonly STDOUT_PATH="${TEST_TMPDIR}/stdout.txt"
readonly H_OUT_PATH="${TEST_TMPDIR}/cc_api.h"
readonly RS_OUT_PATH="${TEST_TMPDIR}/cc_api_impl.rs"
function delete_all_test_outputs() {
rm -rf "$STDERR_PATH" "$STDOUT_PATH" "$H_OUT_PATH" "$RS_OUT_PATH" "$TARGET_JSON_PATH"
}
readonly CC_BINDINGS_FROM_RS_PATH="${RUNFILES}/cc_bindings_from_rs/cc_bindings_from_rs"
readonly SYSROOT_PATH="${RUNFILES}/${G3_SYSROOT_PATH}"
readonly RUSTFMT_PATH="third_party/crosstool/rust/unstable/main_sysroot/bin/rustfmt"
readonly DEFAULT_CLANG_FORMAT_EXE_PATH="${RUNFILES}/google3/third_party/crosstool/google3_users/clang-format"
# When using a rustc --target json, copy it (dereferencing symlinks) to an
# arbitrary location using a consistent filename.
# See cs/GOOGLE3_RUSTC_TARGET_JSON for code related to this.
readonly TARGET_JSON_PATH="${TEST_TMPDIR}/google3_rustc_target.json"
function rustc_target_arg() {
if [[ ! -z "${RUSTC_TARGET_PATH}" ]]; then
cp -L "${RUSTC_TARGET_PATH}" "${TARGET_JSON_PATH}"
echo "--target=${TARGET_JSON_PATH}"
fi
}
# This tests a simple happy, errors-free code path.
function test::happy_path() {
local RS_INPUT_PATH="${TEST_TMPDIR}/crate_name.rs"
echo >"$RS_INPUT_PATH" "
#[no_mangle]
pub extern \"C\" fn public_function() {
private_function()
}
fn private_function() {}
"
delete_all_test_outputs
EXPECT_SUCCEED \
"\"$CC_BINDINGS_FROM_RS_PATH\" >\"$STDOUT_PATH\" 2>\"$STDERR_PATH\" \
\"--h-out=${H_OUT_PATH}\" \
\"--rs-out=${RS_OUT_PATH}\" \
\"--crubit-support-path-format=<crubit/support/{header}>\" \
\"--clang-format-exe-path=${DEFAULT_CLANG_FORMAT_EXE_PATH}\" \
\"--rustfmt-exe-path=$RUSTFMT_PATH\" \
-- \
\"$RS_INPUT_PATH\" \
--crate-type=lib \
--sysroot=$SYSROOT_PATH \
$(rustc_target_arg)" \
"Expecting that this invocation of cc_bindings_from_rs will succeed"
EXPECT_STR_EMPTY "$(cat $STDOUT_PATH)"
EXPECT_STR_EMPTY "$(cat $STDERR_PATH)"
EXPECT_FILE_NOT_EMPTY "${H_OUT_PATH}"
EXPECT_SUCCEED \
"grep 'Automatically @generated C++ bindings for the following Rust crate:' \
\"$H_OUT_PATH\" >/dev/null" \
"The emitted .h file should contain a header comment"
EXPECT_SUCCEED \
"grep 'extern \"C\" void public_function();' \
\"$H_OUT_PATH\" >/dev/null" \
"The emitted .h file should contain C++ bindings"
EXPECT_FILE_NOT_EMPTY "${RS_OUT_PATH}"
EXPECT_SUCCEED \
"grep 'Automatically @generated C++ bindings for the following Rust crate:' \
\"$H_OUT_PATH\" >/dev/null" \
"The emitted .h file should contain a header comment"
# TODO(b/254097223): Cover the contents of the generated `.rs` file once they
# actually contain Rust thunks.
}
# This tests that `main` special-cases errors received from the `clap` crate.
# In particular, calling `clap::Error::exit` should return zero exit code
# when `--help` flag is used.
function test::crubit_help() {
delete_all_test_outputs
EXPECT_SUCCEED \
"\"$CC_BINDINGS_FROM_RS_PATH\" >\"$STDOUT_PATH\" 2>\"$STDERR_PATH\" \
--help" \
"--help should print the error message and return with 0 exit code"
EXPECT_STR_EMPTY "$(cat $STDERR_PATH)"
EXPECT_SUCCEED \
"grep 'Generates C++ bindings for a Rust crate' \"$STDOUT_PATH\" >/dev/null" \
"The help message should contain the introduction"
EXPECT_SUCCEED \
"grep 'Output path for C++ header file with bindings' \
\"$STDOUT_PATH\" >/dev/null" \
"The help message should contain flag-specific snippets"
}
# This tests that `main` correctly special-cases errors received from the `clap`
# crate. In particular, calling `clap::Error::exit` should return zero exit
# code when an unrecognized Crubit flag is used.
function test::unrecognized_crubit_flag() {
delete_all_test_outputs
EXPECT_FAIL \
"\"$CC_BINDINGS_FROM_RS_PATH\" >\"$STDOUT_PATH\" 2>\"$STDERR_PATH\" \
--no-such-crubit-flag $(rustc_target_arg)" \
"Unrecognized cmdline flag should result in non-0 exit code"
EXPECT_STR_EMPTY "$(cat $STDOUT_PATH)"
EXPECT_SUCCEED \
"grep 'unexpected argument .*--no-such-crubit-flag.*' \"$STDERR_PATH\" >/dev/null" \
"The error message should complain about the unrecognized flag"
}
# This tests that `main` correctly propagates a non-`clap`-related error.
function test::invalid_h_out() {
local RS_INPUT_PATH="${TEST_TMPDIR}/crate_name.rs"
echo >"$RS_INPUT_PATH" "
pub fn public_function() {}
"
# Test what happens when the `--h-out` argument below is invalid. This
# particular error condition results in 1) non-`clap`-error and 2) in an error
# chain containing Crubit-level and std::fs-level errors..
delete_all_test_outputs
EXPECT_FAIL \
"\"$CC_BINDINGS_FROM_RS_PATH\" >\"$STDOUT_PATH\" 2>\"$STDERR_PATH\" \
--h-out=../.. \
--rs-out=blah \
\"--crubit-support-path-format=<crubit/support/{header}>\" \
\"--clang-format-exe-path=${DEFAULT_CLANG_FORMAT_EXE_PATH}\" \
\"--rustfmt-exe-path=$RUSTFMT_PATH\" \
-- \
\"$RS_INPUT_PATH\" \
--crate-type=lib \
--sysroot=$SYSROOT_PATH \
$(rustc_target_arg)" \
"Invalid --h-out path should result in non-0 exit code"
EXPECT_STR_EMPTY "$(cat $STDOUT_PATH)"
EXPECT_SUCCEED \
"grep 'Error when writing to ../..' \
\"$STDERR_PATH\" >/dev/null" \
"Crubit-generated part of the error message should be present"
EXPECT_SUCCEED \
"grep 'Is a directory (os error 21)' \
\"$STDERR_PATH\" >/dev/null" \
"std::fs-generated part of the error message should be present"
}
# This test verifies that running `cc_bindings_from_rs` will not emit the
# following warning: use of deprecated function `deprecated_function` (note:
# `#[warn(deprecated)]` on by default). The motivation for silencing the
# warnings is the desire to avoid reporting warnings twice (once via `rustc` +
# once via `cc_bindings_from_rs`).
function test::rustc_warnings_are_silenced() {
local RS_INPUT_PATH="${TEST_TMPDIR}/crate_name.rs"
echo >"$RS_INPUT_PATH" "
#[deprecated]
fn deprecated_function() {
unimplemented!()
}
pub fn public_function() {
deprecated_function()
}
"
delete_all_test_outputs
EXPECT_SUCCEED \
"\"$CC_BINDINGS_FROM_RS_PATH\" >\"$STDOUT_PATH\" 2>\"$STDERR_PATH\" \
\"--h-out=${H_OUT_PATH}\" \
\"--rs-out=${RS_OUT_PATH}\" \
\"--crubit-support-path-format=<crubit/support/{header}>\" \
\"--clang-format-exe-path=${DEFAULT_CLANG_FORMAT_EXE_PATH}\" \
\"--rustfmt-exe-path=$RUSTFMT_PATH\" \
-- \
\"$RS_INPUT_PATH\" \
--crate-type=lib \
$(rustc_target_arg) \
--sysroot=$SYSROOT_PATH" \
"Expecting that this invocation of cc_bindings_from_rs will succeed"
EXPECT_STR_EMPTY "$(cat $STDOUT_PATH)"
EXPECT_STR_EMPTY "$(cat $STDERR_PATH)"
}
gbash::unit::main "$@"