Skip to content

Commit

Permalink
test: Rework Result() matcher
Browse files Browse the repository at this point in the history
  • Loading branch information
chfast committed Jan 4, 2021
1 parent 81b48aa commit 57fcaf4
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 14 deletions.
60 changes: 60 additions & 0 deletions test/unittests/test_utils_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,63 @@ TEST(test_utils, print_typed_execution_result)
str_value_f64 << TypedExecutionResult{ExecutionResult{Value{-1.125}}, ValType::f64};
EXPECT_EQ(str_value_f64.str(), "result(-1.125 (f64))");
}

TEST(test_utils, result_value_matcher)
{
// Exercise every check in Result(value) implementation.
// The implementation and checks below are organized by the value's type in Result(value).
using testing::Not;

// TypedExecutionResult is required to be matched against Result(value).
EXPECT_THAT(ExecutionResult(Value{1_u64}), Not(Result(1_u64)));

EXPECT_THAT(TypedExecutionResult(fizzy::Void, {}), Not(Result(0)));
EXPECT_THAT(TypedExecutionResult(fizzy::Trap, {}), Not(Result(0)));

EXPECT_THAT(TypedExecutionResult(Value{0.0f}, ValType::f32), Result(0.0f));
EXPECT_THAT(TypedExecutionResult(Value{0.0}, ValType::f64), Not(Result(0.0f)));

EXPECT_THAT(TypedExecutionResult(Value{0.0}, ValType::f64), Result(0.0));
EXPECT_THAT(TypedExecutionResult(Value{0.0f}, ValType::f32), Not(Result(0.0)));

EXPECT_THAT(TypedExecutionResult(Value{0_u64}, ValType::i64), Result(0_u64));
EXPECT_THAT(TypedExecutionResult(Value{0_u32}, ValType::i32), Not(Result(0_u64)));

EXPECT_THAT(TypedExecutionResult(Value{0_u32}, ValType::i32), Result(0_u32));

// For non-negative values zero-extension is conveniently allowed.
EXPECT_THAT(TypedExecutionResult(Value{0_u64}, ValType::i64), Result(0));
EXPECT_THAT(TypedExecutionResult(Value{0_u64}, ValType::i64), Result(0_u32));

EXPECT_THAT(TypedExecutionResult(Value{-1_u32}, ValType::i32), Result(-1));
EXPECT_THAT(TypedExecutionResult(Value{-1_u32}, ValType::i32), Result(-1_u32));
EXPECT_THAT(TypedExecutionResult(Value{-1_u32}, ValType::i32), Not(Result(-1_u64)));

EXPECT_THAT(TypedExecutionResult(Value{-1_u64}, ValType::i64), Result(-1_u64));
EXPECT_THAT(TypedExecutionResult(Value{-1_u64}, ValType::i64), Not(Result(-1)));
EXPECT_THAT(TypedExecutionResult(Value{-1_u64}, ValType::i64), Not(Result(-1_u32)));
}

TEST(test_utils, result_value_matcher_explain_missing_result_type)
{
const auto result = testing::internal::MakePredicateFormatterFromMatcher(Result(1_u64))(
"<value>", ExecutionResult(Value{1_u64}));
EXPECT_FALSE(result);
EXPECT_STREQ(result.message(),
"Value of: <value>\n"
"Expected: result 1\n"
" Actual: result(1 [0x1]) (of type fizzy::ExecutionResult), "
"TypedExecutionResult expected");
}

TEST(test_utils, result_value_matcher_explain_non_wasm_type)
{
const auto result = testing::internal::MakePredicateFormatterFromMatcher(Result(char{1}))(
"<value>", TypedExecutionResult(Value{1_u32}, ValType::i32));
EXPECT_FALSE(result);
EXPECT_STREQ(result.message(),
"Value of: <value>\n"
"Expected: result '\\x1' (1)\n"
" Actual: result(1 [0x1] (i32)) (of type fizzy::test::TypedExecutionResult), "
"expected value has non-wasm type");
}
42 changes: 28 additions & 14 deletions test/utils/asserts.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,45 +23,59 @@ MATCHER(Result, "empty result")
return !arg.trapped && !arg.has_value;
}

template <typename T, typename... Ts>
constexpr bool is_any_of = std::disjunction_v<std::is_same<T, Ts>...>;

MATCHER_P(Result, value, "") // NOLINT(readability-redundant-string-init)
{
using namespace fizzy;

static_assert(is_any_of<value_type, uint32_t, int32_t, uint64_t, int64_t, float, double>);

// Require the arg to be TypedExecutionResult.
// This can be a static_assert, but just returning false and failing a test provides better
// location of the error.
using result_type = std::remove_cv_t<std::remove_reference_t<arg_type>>;
if constexpr (!std::is_same_v<result_type, test::TypedExecutionResult>)
{
if (result_listener->IsInterested())
*result_listener << "TypedExecutionResult expected";
return false;
}

if (arg.trapped || !arg.has_value)
return false;

if constexpr (std::is_same_v<result_type, test::TypedExecutionResult>)
{
// Type safe checks.
if constexpr (is_any_of<value_type, uint32_t, int32_t>)
if constexpr (std::is_same_v<value_type, float>)
{
return arg.type == ValType::i32 && arg.value.i64 == static_cast<uint32_t>(value);
return arg.type == ValType::f32 && arg.value.f32 == test::FP{value};
}
else if constexpr (is_any_of<value_type, uint64_t, int64_t>)

if constexpr (std::is_same_v<value_type, double>)
{
return arg.type == ValType::i64 && arg.value.i64 == static_cast<uint64_t>(value);
return arg.type == ValType::f64 && arg.value.f64 == test::FP{value};
}
else if constexpr (std::is_same_v<value_type, float>)

if constexpr (std::is_integral_v<value_type> && sizeof(value_type) == sizeof(uint64_t))
{
return arg.type == ValType::f32 && arg.value.f32 == test::FP{value};
return arg.type == ValType::i64 && arg.value.i64 == static_cast<uint64_t>(value);
}
else

if constexpr (std::is_integral_v<value_type> && sizeof(value_type) == sizeof(uint32_t))
{
return arg.type == ValType::f64 && arg.value.f64 == test::FP{value};
if (arg.type == ValType::i32)
{
return arg.value.i64 == static_cast<uint32_t>(value);
}
else if (arg.type == ValType::i64 && value >= 0)
{
// Here allow convenient zero-extension of the expected value u32 -> u64.
return arg.value.i64 == static_cast<uint32_t>(value);
}
return false;
}
}

if (result_listener->IsInterested())
*result_listener << "expected value has non-wasm type";
return false;
}

MATCHER(CTraps, "") // NOLINT(readability-redundant-string-init)
Expand Down

0 comments on commit 57fcaf4

Please sign in to comment.