diff --git a/test/unittests/test_utils_test.cpp b/test/unittests/test_utils_test.cpp index ad40e0c455..cafa425016 100644 --- a/test/unittests/test_utils_test.cpp +++ b/test/unittests/test_utils_test.cpp @@ -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))( + "", ExecutionResult(Value{1_u64})); + EXPECT_FALSE(result); + EXPECT_STREQ(result.message(), + "Value of: \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}))( + "", TypedExecutionResult(Value{1_u32}, ValType::i32)); + EXPECT_FALSE(result); + EXPECT_STREQ(result.message(), + "Value of: \n" + "Expected: result '\\x1' (1)\n" + " Actual: result(1 [0x1] (i32)) (of type fizzy::test::TypedExecutionResult), " + "expected value has non-wasm type"); +} diff --git a/test/utils/asserts.hpp b/test/utils/asserts.hpp index 3cfa976715..5abcea2835 100644 --- a/test/utils/asserts.hpp +++ b/test/utils/asserts.hpp @@ -23,45 +23,59 @@ MATCHER(Result, "empty result") return !arg.trapped && !arg.has_value; } -template -constexpr bool is_any_of = std::disjunction_v...>; - MATCHER_P(Result, value, "") // NOLINT(readability-redundant-string-init) { using namespace fizzy; - static_assert(is_any_of); - // 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>; if constexpr (!std::is_same_v) + { + if (result_listener->IsInterested()) + *result_listener << "TypedExecutionResult expected"; return false; + } if (arg.trapped || !arg.has_value) return false; if constexpr (std::is_same_v) { - // Type safe checks. - if constexpr (is_any_of) + if constexpr (std::is_same_v) { - return arg.type == ValType::i32 && arg.value.i64 == static_cast(value); + return arg.type == ValType::f32 && arg.value.f32 == test::FP{value}; } - else if constexpr (is_any_of) + + if constexpr (std::is_same_v) { - return arg.type == ValType::i64 && arg.value.i64 == static_cast(value); + return arg.type == ValType::f64 && arg.value.f64 == test::FP{value}; } - else if constexpr (std::is_same_v) + + if constexpr (std::is_integral_v && 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(value); } - else + + if constexpr (std::is_integral_v && 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(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(value); + } + return false; } } + + if (result_listener->IsInterested()) + *result_listener << "expected value has non-wasm type"; + return false; } MATCHER(CTraps, "") // NOLINT(readability-redundant-string-init)