From 29e9c2190643e03c94c6cf6e8e5dbc5e0ea61fee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Sun, 22 Nov 2020 19:33:56 +0100 Subject: [PATCH 1/3] test: Fix the types of expected results --- test/unittests/api_test.cpp | 7 +++++-- test/unittests/execute_call_test.cpp | 4 ++-- test/unittests/execute_control_test.cpp | 14 +++++++------- test/unittests/execute_test.cpp | 7 ++++--- 4 files changed, 18 insertions(+), 14 deletions(-) diff --git a/test/unittests/api_test.cpp b/test/unittests/api_test.cpp index abc4fe3b2..5ff9caccc 100644 --- a/test/unittests/api_test.cpp +++ b/test/unittests/api_test.cpp @@ -269,7 +269,8 @@ TEST(api, find_exported_function) auto opt_function = find_exported_function(*instance, "foo"); ASSERT_TRUE(opt_function); - EXPECT_THAT(opt_function->function(*instance, {}, 0), Result(42)); + EXPECT_THAT(TypedExecutionResult(opt_function->function(*instance, {}, 0), ValType::i32), + Result(42_u32)); EXPECT_TRUE(opt_function->input_types.empty()); ASSERT_EQ(opt_function->output_types.size(), 1); EXPECT_EQ(opt_function->output_types[0], ValType::i32); @@ -296,7 +297,9 @@ TEST(api, find_exported_function) auto opt_reexported_function = find_exported_function(*instance_reexported_function, "foo"); ASSERT_TRUE(opt_reexported_function); - EXPECT_THAT(opt_reexported_function->function(*instance, {}, 0), Result(42)); + EXPECT_THAT( + TypedExecutionResult(opt_reexported_function->function(*instance, {}, 0), ValType::i32), + Result(42_u32)); EXPECT_TRUE(opt_reexported_function->input_types.empty()); ASSERT_EQ(opt_reexported_function->output_types.size(), 1); EXPECT_EQ(opt_reexported_function->output_types[0], ValType::i32); diff --git a/test/unittests/execute_call_test.cpp b/test/unittests/execute_call_test.cpp index 92fb8faa7..88514d5c7 100644 --- a/test/unittests/execute_call_test.cpp +++ b/test/unittests/execute_call_test.cpp @@ -172,7 +172,7 @@ TEST(execute_call, call_indirect) for (const auto param : {0u, 1u, 2u}) { - constexpr uint64_t expected_results[]{3, 2, 1}; + constexpr uint32_t expected_results[]{3, 2, 1}; EXPECT_THAT(execute(module, 5, {param}), Result(expected_results[param])); } @@ -257,7 +257,7 @@ TEST(execute_call, call_indirect_imported_table) for (const auto param : {0u, 1u, 2u}) { - constexpr uint64_t expected_results[]{3, 2, 1}; + constexpr uint32_t expected_results[]{3, 2, 1}; EXPECT_THAT(execute(*instance, 0, {param}), Result(expected_results[param])); } diff --git a/test/unittests/execute_control_test.cpp b/test/unittests/execute_control_test.cpp index 7a67b33f6..702a131e6 100644 --- a/test/unittests/execute_control_test.cpp +++ b/test/unittests/execute_control_test.cpp @@ -614,7 +614,7 @@ TEST(execute_control, br_if_with_result) for (const auto param : {0u, 1u}) { - constexpr uint64_t expected_results[]{ + constexpr uint32_t expected_results[]{ 3, // br_if not taken, result: 1 xor 2 == 3. 2, // br_if taken, result: 2, remaining item dropped. }; @@ -639,7 +639,7 @@ TEST(execute_control, br_if_out_of_function) for (const auto param : {0u, 1u}) { - constexpr uint64_t expected_results[]{ + constexpr uint32_t expected_results[]{ 1, // br_if not taken. 2, // br_if taken. }; @@ -717,7 +717,7 @@ TEST(execute_control, br_table) for (const auto param : {0u, 1u, 2u, 3u, 4u, 5u}) { - constexpr uint64_t expected_results[]{103, 102, 101, 100, 104, 104}; + constexpr uint32_t expected_results[]{103, 102, 101, 100, 104, 104}; EXPECT_THAT(execute(parse(bin), 0, {param}), Result(expected_results[param])); } @@ -840,7 +840,7 @@ TEST(execute_control, if_smoke) for (const auto param : {0u, 1u}) { - constexpr uint64_t expected_results[]{ + constexpr uint32_t expected_results[]{ 0, // no if branch. 4, // if branch. }; @@ -871,7 +871,7 @@ TEST(execute_control, if_else_smoke) for (const auto param : {0u, 1u}) { - constexpr uint64_t expected_results[]{ + constexpr uint32_t expected_results[]{ 2, // else branch. 1, // if branch. }; @@ -908,7 +908,7 @@ TEST(execute_control, if_return_from_branch) for (const auto param : {0u, 1u}) { - constexpr uint64_t expected_results[]{ + constexpr uint32_t expected_results[]{ 2, // else branch. 1, // if branch. }; @@ -947,7 +947,7 @@ TEST(execute_control, if_br_from_branch) for (const auto param : {0u, 1u}) { - constexpr uint64_t expected_results[]{ + constexpr uint32_t expected_results[]{ 2, // else branch. 21, // if branch. }; diff --git a/test/unittests/execute_test.cpp b/test/unittests/execute_test.cpp index a2fb58fcc..0dfdb34b0 100644 --- a/test/unittests/execute_test.cpp +++ b/test/unittests/execute_test.cpp @@ -422,7 +422,7 @@ TEST(execute, i32_load_all_variants) const auto memory_fill = "deb0b1b2b3ed"_bytes; - constexpr std::tuple test_cases[]{ + constexpr std::tuple test_cases[]{ {Instr::i32_load8_u, 0x000000b0}, {Instr::i32_load8_s, 0xffffffb0}, {Instr::i32_load16_u, 0x0000b1b0}, @@ -984,9 +984,10 @@ TEST(execute, reuse_args) auto instance = instantiate(parse(wasm)); - const std::vector args{20, 3}; + const std::vector args{20_u64, 3_u64}; const auto expected = args[0].i64 % (args[0].i64 / args[1].i64); - EXPECT_THAT(execute(*instance, 0, args.data()), Result(expected)); + EXPECT_THAT( + TypedExecutionResult(execute(*instance, 0, args.data()), ValType::i64), Result(expected)); EXPECT_THAT(args[0].i64, 20); EXPECT_THAT(args[1].i64, 3); From 1f7f6a46b59bf5b94b61f0cedf3c7e81d52639c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Sun, 22 Nov 2020 19:28:16 +0100 Subject: [PATCH 2/3] test: Add TypedExecutionResult, check result type --- test/unittests/execute_numeric_test.cpp | 4 +- test/unittests/test_utils_test.cpp | 52 +++++++++++++++++++++++-- test/utils/asserts.cpp | 38 +++++++++++++++++- test/utils/asserts.hpp | 42 ++++++++++++++++++-- test/utils/execute_helpers.hpp | 15 ++++--- test/utils/typed_value.hpp | 9 +++++ 6 files changed, 144 insertions(+), 16 deletions(-) diff --git a/test/unittests/execute_numeric_test.cpp b/test/unittests/execute_numeric_test.cpp index 9ea142f53..f3da4c0c0 100644 --- a/test/unittests/execute_numeric_test.cpp +++ b/test/unittests/execute_numeric_test.cpp @@ -16,7 +16,7 @@ using namespace fizzy::test; namespace { -ExecutionResult execute_unary_operation(Instr instr, TypedValue arg) +TypedExecutionResult execute_unary_operation(Instr instr, TypedValue arg) { const auto& instr_type = get_instruction_type_table()[static_cast(instr)]; EXPECT_EQ(instr_type.inputs.size(), 1); @@ -33,7 +33,7 @@ ExecutionResult execute_unary_operation(Instr instr, TypedValue arg) return execute(*instantiate(std::move(module)), 0, {arg}); } -ExecutionResult execute_binary_operation(Instr instr, TypedValue lhs, TypedValue rhs) +TypedExecutionResult execute_binary_operation(Instr instr, TypedValue lhs, TypedValue rhs) { const auto& instr_type = get_instruction_type_table()[static_cast(instr)]; EXPECT_EQ(instr_type.inputs.size(), 2); diff --git a/test/unittests/test_utils_test.cpp b/test/unittests/test_utils_test.cpp index f4f99c176..ad40e0c45 100644 --- a/test/unittests/test_utils_test.cpp +++ b/test/unittests/test_utils_test.cpp @@ -34,11 +34,11 @@ TEST(test_utils, from_hex) EXPECT_THROW_MESSAGE(from_hex("FG"), std::out_of_range, "not a hex digit"); } -TEST(test_utils, result_signed_int) +TEST(test_utils, result_signed_int_typed) { - EXPECT_THAT(ExecutionResult{Value{-1}}, Result(-1)); + EXPECT_THAT(TypedExecutionResult(Value{-1}, ValType::i32), Result(-1)); constexpr auto v = std::numeric_limits::min(); - EXPECT_THAT(ExecutionResult{Value{v}}, Result(v)); + EXPECT_THAT(TypedExecutionResult(Value{v}, ValType::i32), Result(v)); } TEST(test_utils, print_execution_result) @@ -70,3 +70,49 @@ TEST(test_utils, print_c_execution_result) str_value << FizzyExecutionResult{false, true, {42}}; EXPECT_EQ(str_value.str(), "result(42 [0x2a])"); } + +TEST(test_utils, print_typed_execution_result) +{ + std::stringstream str_trap; + str_trap << TypedExecutionResult{Trap, {}}; + EXPECT_EQ(str_trap.str(), "trapped"); + + std::stringstream str_void; + str_void << TypedExecutionResult{Void, {}}; + EXPECT_EQ(str_void.str(), "result()"); + + std::stringstream str_value_i32; + str_value_i32 << TypedExecutionResult{ExecutionResult{Value{42_u32}}, ValType::i32}; + EXPECT_EQ(str_value_i32.str(), "result(42 [0x2a] (i32))"); + str_value_i32.str({}); + str_value_i32 << TypedExecutionResult{ExecutionResult{Value{0x80000000_u32}}, ValType::i32}; + EXPECT_EQ(str_value_i32.str(), "result(2147483648 [0x80000000] (i32))"); + str_value_i32.str({}); + str_value_i32 << TypedExecutionResult{ExecutionResult{Value{-2_u32}}, ValType::i32}; + EXPECT_EQ(str_value_i32.str(), "result(4294967294 [0xfffffffe] (i32))"); + + + std::stringstream str_value_i64; + str_value_i64 << TypedExecutionResult{ExecutionResult{Value{42_u64}}, ValType::i64}; + EXPECT_EQ(str_value_i64.str(), "result(42 [0x2a] (i64))"); + str_value_i64.str({}); + str_value_i64 << TypedExecutionResult{ExecutionResult{Value{0x100000000_u64}}, ValType::i64}; + EXPECT_EQ(str_value_i64.str(), "result(4294967296 [0x100000000] (i64))"); + str_value_i64.str({}); + str_value_i64 << TypedExecutionResult{ExecutionResult{Value{-3_u64}}, ValType::i64}; + EXPECT_EQ(str_value_i64.str(), "result(18446744073709551613 [0xfffffffffffffffd] (i64))"); + + std::stringstream str_value_f32; + str_value_f32 << TypedExecutionResult{ExecutionResult{Value{1.125f}}, ValType::f32}; + EXPECT_EQ(str_value_f32.str(), "result(1.125 (f32))"); + str_value_f32.str({}); + str_value_f32 << TypedExecutionResult{ExecutionResult{Value{-1.125f}}, ValType::f32}; + EXPECT_EQ(str_value_f32.str(), "result(-1.125 (f32))"); + + std::stringstream str_value_f64; + str_value_f64 << TypedExecutionResult{ExecutionResult{Value{1.125}}, ValType::f64}; + EXPECT_EQ(str_value_f64.str(), "result(1.125 (f64))"); + str_value_f64.str({}); + str_value_f64 << TypedExecutionResult{ExecutionResult{Value{-1.125}}, ValType::f64}; + EXPECT_EQ(str_value_f64.str(), "result(-1.125 (f64))"); +} diff --git a/test/utils/asserts.cpp b/test/utils/asserts.cpp index 8ec902d5c..57589a320 100644 --- a/test/utils/asserts.cpp +++ b/test/utils/asserts.cpp @@ -19,7 +19,7 @@ inline void output_result(std::ostream& os, ResultT result) const auto format_flags = os.flags(); os << "result("; if (result.has_value) - os << result.value.i64 << " [0x" << std::hex << result.value.i64 << "]"; + os << std::dec << result.value.i64 << " [0x" << std::hex << result.value.i64 << "]"; os << ")"; os.flags(format_flags); } @@ -38,4 +38,40 @@ std::ostream& operator<<(std::ostream& os, ExecutionResult result) output_result(os, result); return os; } + +namespace test +{ +std::ostream& operator<<(std::ostream& os, const TypedExecutionResult& result) +{ + const auto format_flags = os.flags(); + + if (result.has_value) + { + os << "result("; + switch (result.type) + { + case ValType::i32: + os << std::dec << result.value.as() << " [0x" << std::hex + << result.value.as() << "] (i32)"; + break; + case ValType::i64: + os << std::dec << result.value.i64 << " [0x" << std::hex << result.value.i64 + << "] (i64)"; + break; + case ValType::f32: + os << result.value.f32 << " (f32)"; + break; + case ValType::f64: + os << result.value.f64 << " (f64)"; + break; + } + os << ")"; + } + else + os << ExecutionResult{result}; + + os.flags(format_flags); + return os; +} +} // namespace test } // namespace fizzy diff --git a/test/utils/asserts.hpp b/test/utils/asserts.hpp index 510cf5d9d..3cfa97671 100644 --- a/test/utils/asserts.hpp +++ b/test/utils/asserts.hpp @@ -9,7 +9,9 @@ #include #include #include +#include #include +#include MATCHER(Traps, "") // NOLINT(readability-redundant-string-init) { @@ -21,15 +23,45 @@ 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) + return false; + if (arg.trapped || !arg.has_value) return false; - if constexpr (std::is_floating_point_v) - return arg.value.template as() == fizzy::test::FP{value}; - else // always check 64 bit of result for all integers, including 32-bit results - return arg.value.i64 == static_cast>(value); + if constexpr (std::is_same_v) + { + // Type safe checks. + if constexpr (is_any_of) + { + return arg.type == ValType::i32 && arg.value.i64 == static_cast(value); + } + else if constexpr (is_any_of) + { + return arg.type == ValType::i64 && arg.value.i64 == static_cast(value); + } + else if constexpr (std::is_same_v) + { + return arg.type == ValType::f32 && arg.value.f32 == test::FP{value}; + } + else + { + return arg.type == ValType::f64 && arg.value.f64 == test::FP{value}; + } + } } MATCHER(CTraps, "") // NOLINT(readability-redundant-string-init) @@ -101,4 +133,6 @@ inline uint32_t as_uint32(fizzy::Value value) EXPECT_EQ(value.i64 & 0xffffffff00000000, 0); return static_cast(value.i64); } + +std::ostream& operator<<(std::ostream& os, const TypedExecutionResult&); } // namespace fizzy::test diff --git a/test/utils/execute_helpers.hpp b/test/utils/execute_helpers.hpp index db277dd03..51a43e60a 100644 --- a/test/utils/execute_helpers.hpp +++ b/test/utils/execute_helpers.hpp @@ -12,8 +12,8 @@ namespace fizzy::test { -inline ExecutionResult execute( - Instance& instance, FuncIdx func_idx, std::initializer_list typed_args) +inline TypedExecutionResult execute(Instance& instance, FuncIdx func_idx, + std::initializer_list typed_args, int depth = 0) { const auto& func_type = instance.module->get_function_type(func_idx); const auto [typed_arg_it, type_it] = std::mismatch(std::cbegin(typed_args), @@ -31,13 +31,16 @@ inline ExecutionResult execute( std::transform(std::cbegin(typed_args), std::cend(typed_args), std::begin(args), [](const auto& typed_arg) { return typed_arg.value; }); - return fizzy::execute(instance, func_idx, args.data()); + const auto result = fizzy::execute(instance, func_idx, args.data(), depth); + assert(func_type.outputs.size() <= 1); + const auto result_type = func_type.outputs.empty() ? ValType{} : func_type.outputs[0]; + return {result, result_type}; } -inline ExecutionResult execute(const std::unique_ptr& module, FuncIdx func_idx, - std::initializer_list typed_args) +inline auto execute(const std::unique_ptr& module, FuncIdx func_idx, + std::initializer_list typed_args, int depth = 0) { auto instance = instantiate(*module); - return test::execute(*instance, func_idx, typed_args); + return test::execute(*instance, func_idx, typed_args, depth); } } // namespace fizzy::test diff --git a/test/utils/typed_value.hpp b/test/utils/typed_value.hpp index bb068dbe5..3009ff9be 100644 --- a/test/utils/typed_value.hpp +++ b/test/utils/typed_value.hpp @@ -3,6 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 #pragma once +#include "execute.hpp" #include "types.hpp" #include "value.hpp" @@ -39,4 +40,12 @@ struct TypedValue constexpr TypedValue(float v) noexcept : TypedValue{ValType::f32, v} {} constexpr TypedValue(double v) noexcept : TypedValue{ValType::f64, v} {} }; + +struct TypedExecutionResult : ExecutionResult +{ + ValType type; + constexpr TypedExecutionResult(ExecutionResult result, ValType ty) noexcept + : ExecutionResult{result}, type{ty} + {} +}; } // namespace fizzy::test From 603aca812b00a80625deb2b09327bacc00a752bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 23 Nov 2020 11:52:34 +0100 Subject: [PATCH 3/3] test: Rework Result() matcher --- test/unittests/test_utils_test.cpp | 64 ++++++++++++++++++++++++++++++ test/utils/asserts.hpp | 51 +++++++++++++++--------- 2 files changed, 96 insertions(+), 19 deletions(-) diff --git a/test/unittests/test_utils_test.cpp b/test/unittests/test_utils_test.cpp index ad40e0c45..99bf20419 100644 --- a/test/unittests/test_utils_test.cpp +++ b/test/unittests/test_utils_test.cpp @@ -116,3 +116,67 @@ 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))); + + // Comparing with non-wasm types always return false. + EXPECT_THAT(TypedExecutionResult(Value{1_u32}, ValType::i32), Not(Result(uint8_t{1}))); + EXPECT_THAT(TypedExecutionResult(Value{1_u64}, ValType::i64), Not(Result(uint8_t{1}))); +} + +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 3cfa97671..638ed19d5 100644 --- a/test/utils/asserts.hpp +++ b/test/utils/asserts.hpp @@ -23,44 +23,57 @@ 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) + } + else { - // Type safe checks. - if constexpr (is_any_of) + if (arg.trapped || !arg.has_value) + return false; + + 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; } }