-
Notifications
You must be signed in to change notification settings - Fork 34
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
test: Add TypedExecutionResult, check result type #659
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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<int32_t>::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,113 @@ 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))"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add a test with negative integer values? |
||
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))"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add a test with negative floating point values? |
||
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))"); | ||
} | ||
|
||
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))); | ||
gumb0 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
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))); | ||
gumb0 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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))( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well this is clearly something internal to google.mock, I'm not sure it's a good idea to use it. For coverage tests with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah for coverage you mentioned that tests wth There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is to test the message output. Using |
||
"<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"); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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<uint32_t>() << " [0x" << std::hex | ||
<< result.value.as<uint32_t>() << "] (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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah this is the reset. So is decimal mode on by default according to the std? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No. It resets to what state it was before. So explicit |
||
return os; | ||
} | ||
} // namespace test | ||
} // namespace fizzy |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,7 +9,9 @@ | |
#include <fizzy/fizzy.h> | ||
#include <gmock/gmock.h> | ||
#include <test/utils/floating_point_utils.hpp> | ||
#include <test/utils/typed_value.hpp> | ||
#include <iosfwd> | ||
#include <type_traits> | ||
|
||
MATCHER(Traps, "") // NOLINT(readability-redundant-string-init) | ||
{ | ||
|
@@ -23,13 +25,56 @@ MATCHER(Result, "empty result") | |
|
||
MATCHER_P(Result, value, "") // NOLINT(readability-redundant-string-init) | ||
{ | ||
if (arg.trapped || !arg.has_value) | ||
using namespace fizzy; | ||
|
||
// 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()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where is this part of google.test documented? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I found this in implementation of builtin matchers. This seems not to be strictly needed, but this is false e.q. when There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems to be undocumented feature. The only mention of |
||
*result_listener << "TypedExecutionResult expected"; | ||
return false; | ||
} | ||
gumb0 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
else | ||
{ | ||
if (arg.trapped || !arg.has_value) | ||
return false; | ||
|
||
if constexpr (std::is_floating_point_v<value_type>) | ||
return arg.value.template as<value_type>() == fizzy::test::FP{value}; | ||
else // always check 64 bit of result for all integers, including 32-bit results | ||
return arg.value.i64 == static_cast<std::make_unsigned_t<value_type>>(value); | ||
if constexpr (std::is_same_v<value_type, float>) | ||
{ | ||
return arg.type == ValType::f32 && arg.value.f32 == test::FP{value}; | ||
} | ||
|
||
if constexpr (std::is_same_v<value_type, double>) | ||
{ | ||
return arg.type == ValType::f64 && arg.value.f64 == test::FP{value}; | ||
} | ||
|
||
if constexpr (std::is_integral_v<value_type> && sizeof(value_type) == sizeof(uint64_t)) | ||
{ | ||
return arg.type == ValType::i64 && arg.value.i64 == static_cast<uint64_t>(value); | ||
} | ||
|
||
if constexpr (std::is_integral_v<value_type> && sizeof(value_type) == sizeof(uint32_t)) | ||
{ | ||
if (arg.type == ValType::i32) | ||
{ | ||
return arg.value.i64 == static_cast<uint32_t>(value); | ||
} | ||
else if (arg.type == ValType::i64 && value >= 0) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you plan to remove this and change the tests at some point? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure. Probably make sense to always use |
||
{ | ||
// 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) | ||
|
@@ -101,4 +146,6 @@ inline uint32_t as_uint32(fizzy::Value value) | |
EXPECT_EQ(value.i64 & 0xffffffff00000000, 0); | ||
return static_cast<uint32_t>(value.i64); | ||
} | ||
|
||
std::ostream& operator<<(std::ostream& os, const TypedExecutionResult&); | ||
} // namespace fizzy::test |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps have a second test with
>2^32-1
value.