|
@@ -10,16 +10,13 @@
|
|
|
#include <AK/Assertions.h>
|
|
|
#include <AK/CheckedFormatString.h>
|
|
|
#include <AK/Math.h>
|
|
|
+#include <AK/SourceLocation.h>
|
|
|
#include <LibTest/CrashTest.h>
|
|
|
#include <LibTest/Randomized/RandomnessSource.h>
|
|
|
#include <LibTest/TestResult.h>
|
|
|
|
|
|
-namespace AK {
|
|
|
-template<typename... Parameters>
|
|
|
-void warnln(CheckedFormatString<Parameters...>&& fmtstr, Parameters const&...);
|
|
|
-}
|
|
|
-
|
|
|
namespace Test {
|
|
|
+
|
|
|
// Declare helpers so that we can call them from VERIFY in included headers
|
|
|
// the setter for TestResult is already declared in TestResult.h
|
|
|
TestResult current_test_result();
|
|
@@ -32,102 +29,147 @@ void enable_reporting();
|
|
|
void disable_reporting();
|
|
|
|
|
|
u64 randomized_runs();
|
|
|
+
|
|
|
+template<typename T>
|
|
|
+void expect(T const& expression, StringView expression_string, SourceLocation location = SourceLocation::current())
|
|
|
+{
|
|
|
+ if (!static_cast<bool>(expression)) {
|
|
|
+ if (is_reporting_enabled())
|
|
|
+ warnln("\033[31;1mFAIL\033[0m: {}:{}: EXPECT({}) failed", location.filename(), location.line_number(), expression_string);
|
|
|
+
|
|
|
+ set_current_test_result(TestResult::Failed);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+template<typename LHS, typename RHS>
|
|
|
+void expect_equality(LHS const& lhs, RHS const& rhs, StringView lhs_string, StringView rhs_string, SourceLocation location = SourceLocation::current())
|
|
|
+{
|
|
|
+ if (lhs != rhs) {
|
|
|
+ if (is_reporting_enabled()) {
|
|
|
+ warnln("\033[31;1mFAIL\033[0m: {}:{}: EXPECT_EQ({}, {}) failed with lhs={} and rhs={}",
|
|
|
+ location.filename(), location.line_number(), lhs_string, rhs_string,
|
|
|
+ FormatIfSupported { lhs }, FormatIfSupported { rhs });
|
|
|
+ }
|
|
|
+
|
|
|
+ set_current_test_result(TestResult::Failed);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-#define EXPECT_EQ(a, b) \
|
|
|
- do { \
|
|
|
- auto lhs = (a); \
|
|
|
- auto rhs = (b); \
|
|
|
- if (lhs != rhs) { \
|
|
|
- if (::Test::is_reporting_enabled()) \
|
|
|
- ::AK::warnln("\033[31;1mFAIL\033[0m: {}:{}: EXPECT_EQ({}, {}) failed with lhs={} and rhs={}", \
|
|
|
- __FILE__, __LINE__, #a, #b, FormatIfSupported { lhs }, FormatIfSupported { rhs }); \
|
|
|
- ::Test::set_current_test_result(::Test::TestResult::Failed); \
|
|
|
- } \
|
|
|
+template<typename LHS, typename RHS>
|
|
|
+void expect_truthy_equality(LHS const& lhs, RHS const& rhs, StringView lhs_string, StringView rhs_string, SourceLocation location = SourceLocation::current())
|
|
|
+{
|
|
|
+ if (static_cast<bool>(lhs) != static_cast<bool>(rhs)) {
|
|
|
+ if (is_reporting_enabled()) {
|
|
|
+ warnln("\033[31;1mFAIL\033[0m: {}:{}: EXPECT_EQ_TRUTH({}, {}) failed with lhs={} ({}) and rhs={} ({})",
|
|
|
+ location.filename(), location.line_number(), lhs_string, rhs_string,
|
|
|
+ FormatIfSupported { lhs }, static_cast<bool>(lhs),
|
|
|
+ FormatIfSupported { rhs }, static_cast<bool>(rhs));
|
|
|
+ }
|
|
|
+
|
|
|
+ set_current_test_result(TestResult::Failed);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+template<typename LHS, typename RHS>
|
|
|
+void expect_equality_with_forced_logging(LHS const& lhs, RHS const& rhs, StringView lhs_string, StringView rhs_string, SourceLocation location = SourceLocation::current())
|
|
|
+{
|
|
|
+ if (lhs != rhs) {
|
|
|
+ if (is_reporting_enabled()) {
|
|
|
+ warnln("\033[31;1mFAIL\033[0m: {}:{}: EXPECT_EQ({}, {}) failed with lhs={} and rhs={}",
|
|
|
+ location.filename(), location.line_number(), lhs_string, rhs_string,
|
|
|
+ lhs, rhs);
|
|
|
+ }
|
|
|
+
|
|
|
+ set_current_test_result(TestResult::Failed);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+template<typename LHS, typename RHS>
|
|
|
+void expect_inequality(LHS const& lhs, RHS const& rhs, StringView lhs_string, StringView rhs_string, SourceLocation location = SourceLocation::current())
|
|
|
+{
|
|
|
+ if (lhs == rhs) {
|
|
|
+ if (is_reporting_enabled()) {
|
|
|
+ warnln("\033[31;1mFAIL\033[0m: {}:{}: EXPECT_NE({}, {}) failed with lhs={} and rhs={}",
|
|
|
+ location.filename(), location.line_number(), lhs_string, rhs_string,
|
|
|
+ FormatIfSupported { lhs }, FormatIfSupported { rhs });
|
|
|
+ }
|
|
|
+
|
|
|
+ set_current_test_result(TestResult::Failed);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+template<FloatingPoint LHS, FloatingPoint RHS>
|
|
|
+void expect_approximate(LHS lhs, RHS rhs, StringView lhs_string, StringView rhs_string, double tolerance, SourceLocation location = SourceLocation::current())
|
|
|
+{
|
|
|
+ auto diff = static_cast<double>(lhs) - static_cast<double>(rhs);
|
|
|
+
|
|
|
+ if (AK::fabs(diff) > tolerance) {
|
|
|
+ if (is_reporting_enabled()) {
|
|
|
+ warnln("\033[31;1mFAIL\033[0m: {}:{}: EXPECT_APPROXIMATE({}, {}) failed with lhs={} and rhs={}, (lhs-rhs)={}",
|
|
|
+ location.filename(), location.line_number(), lhs_string, rhs_string,
|
|
|
+ lhs, rhs, diff);
|
|
|
+ }
|
|
|
+
|
|
|
+ set_current_test_result(TestResult::Failed);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+template<typename T>
|
|
|
+bool assume(T const& expression, StringView expression_string, SourceLocation location = SourceLocation::current())
|
|
|
+{
|
|
|
+ if (!static_cast<bool>(expression)) {
|
|
|
+ if (is_reporting_enabled()) {
|
|
|
+ warnln("\033[31;1mREJECTED\033[0m: {}:{}: Couldn't generate random value satisfying ASSUME({})",
|
|
|
+ location.filename(), location.line_number(), expression_string);
|
|
|
+ }
|
|
|
+
|
|
|
+ set_current_test_result(TestResult::Rejected);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+#define EXPECT(x) \
|
|
|
+ do { \
|
|
|
+ ::Test::expect(x, #x##sv); \
|
|
|
} while (false)
|
|
|
|
|
|
-#define EXPECT_EQ_TRUTH(a, b) \
|
|
|
- do { \
|
|
|
- auto lhs = (a); \
|
|
|
- auto rhs = (b); \
|
|
|
- bool ltruth = static_cast<bool>(lhs); \
|
|
|
- bool rtruth = static_cast<bool>(rhs); \
|
|
|
- if (ltruth != rtruth) { \
|
|
|
- if (::Test::is_reporting_enabled()) \
|
|
|
- ::AK::warnln("\033[31;1mFAIL\033[0m: {}:{}: EXPECT_EQ_TRUTH({}, {}) failed with lhs={} ({}) and rhs={} ({})", \
|
|
|
- __FILE__, __LINE__, #a, #b, FormatIfSupported { lhs }, ltruth, FormatIfSupported { rhs }, rtruth); \
|
|
|
- ::Test::set_current_test_result(::Test::TestResult::Failed); \
|
|
|
- } \
|
|
|
+#define EXPECT_EQ(a, b) \
|
|
|
+ do { \
|
|
|
+ ::Test::expect_equality(a, b, #a##sv, #b##sv); \
|
|
|
} while (false)
|
|
|
|
|
|
-// If you're stuck and `EXPECT_EQ` seems to refuse to print anything useful,
|
|
|
-// try this: It'll spit out a nice compiler error telling you why it doesn't print.
|
|
|
-#define EXPECT_EQ_FORCE(a, b) \
|
|
|
- do { \
|
|
|
- auto lhs = (a); \
|
|
|
- auto rhs = (b); \
|
|
|
- if (lhs != rhs) { \
|
|
|
- if (::Test::is_reporting_enabled()) \
|
|
|
- ::AK::warnln("\033[31;1mFAIL\033[0m: {}:{}: EXPECT_EQ({}, {}) failed with lhs={} and rhs={}", \
|
|
|
- __FILE__, __LINE__, #a, #b, lhs, rhs); \
|
|
|
- ::Test::set_current_test_result(::Test::TestResult::Failed); \
|
|
|
- } \
|
|
|
+#define EXPECT_EQ_TRUTH(a, b) \
|
|
|
+ do { \
|
|
|
+ ::Test::expect_truthy_equality(a, b, #a##sv, #b##sv); \
|
|
|
} while (false)
|
|
|
|
|
|
-#define EXPECT_NE(a, b) \
|
|
|
- do { \
|
|
|
- auto lhs = (a); \
|
|
|
- auto rhs = (b); \
|
|
|
- if (lhs == rhs) { \
|
|
|
- if (::Test::is_reporting_enabled()) \
|
|
|
- ::AK::warnln("\033[31;1mFAIL\033[0m: {}:{}: EXPECT_NE({}, {}) failed with lhs={} and rhs={}", \
|
|
|
- __FILE__, __LINE__, #a, #b, FormatIfSupported { lhs }, FormatIfSupported { rhs }); \
|
|
|
- ::Test::set_current_test_result(::Test::TestResult::Failed); \
|
|
|
- } \
|
|
|
+// If you're stuck and `EXPECT_EQ` seems to refuse to print anything useful,
|
|
|
+// try this: It'll spit out a nice compiler error telling you why it doesn't print.
|
|
|
+#define EXPECT_EQ_FORCE(a, b) \
|
|
|
+ do { \
|
|
|
+ ::Test::expect_equality_with_forced_logging(a, b, #a##sv, #b##sv); \
|
|
|
} while (false)
|
|
|
|
|
|
-#define EXPECT(x) \
|
|
|
- do { \
|
|
|
- if (!(x)) { \
|
|
|
- if (::Test::is_reporting_enabled()) \
|
|
|
- ::AK::warnln("\033[31;1mFAIL\033[0m: {}:{}: EXPECT({}) failed", __FILE__, __LINE__, #x); \
|
|
|
- ::Test::set_current_test_result(::Test::TestResult::Failed); \
|
|
|
- } \
|
|
|
+#define EXPECT_NE(a, b) \
|
|
|
+ do { \
|
|
|
+ ::Test::expect_inequality(a, b, #a##sv, #b##sv); \
|
|
|
} while (false)
|
|
|
|
|
|
-#define EXPECT_APPROXIMATE_WITH_ERROR(a, b, err) \
|
|
|
- do { \
|
|
|
- auto expect_close_lhs = a; \
|
|
|
- auto expect_close_rhs = b; \
|
|
|
- auto expect_close_diff = static_cast<double>(expect_close_lhs) - static_cast<double>(expect_close_rhs); \
|
|
|
- if (AK::fabs(expect_close_diff) > (err)) { \
|
|
|
- if (::Test::is_reporting_enabled()) \
|
|
|
- ::AK::warnln("\033[31;1mFAIL\033[0m: {}:{}: EXPECT_APPROXIMATE({}, {})" \
|
|
|
- " failed with lhs={}, rhs={}, (lhs-rhs)={}", \
|
|
|
- __FILE__, __LINE__, #a, #b, expect_close_lhs, expect_close_rhs, expect_close_diff); \
|
|
|
- ::Test::set_current_test_result(::Test::TestResult::Failed); \
|
|
|
- } \
|
|
|
+#define EXPECT_APPROXIMATE_WITH_ERROR(a, b, err) \
|
|
|
+ do { \
|
|
|
+ ::Test::expect_approximate(a, b, #a##sv, #b##sv, err); \
|
|
|
} while (false)
|
|
|
|
|
|
#define EXPECT_APPROXIMATE(a, b) EXPECT_APPROXIMATE_WITH_ERROR(a, b, 0.0000005)
|
|
|
|
|
|
-#define REJECT(message) \
|
|
|
- do { \
|
|
|
- if (::Test::is_reporting_enabled()) \
|
|
|
- ::AK::warnln("\033[31;1mREJECTED\033[0m: {}:{}: {}", \
|
|
|
- __FILE__, __LINE__, #message); \
|
|
|
- ::Test::set_current_test_result(::Test::TestResult::Rejected); \
|
|
|
- } while (false)
|
|
|
-
|
|
|
-#define ASSUME(x) \
|
|
|
- do { \
|
|
|
- if (!(x)) { \
|
|
|
- if (::Test::is_reporting_enabled()) \
|
|
|
- ::AK::warnln("\033[31;1mREJECTED\033[0m: {}:{}: Couldn't generate random value satisfying ASSUME({})", \
|
|
|
- __FILE__, __LINE__, #x); \
|
|
|
- ::Test::set_current_test_result(::Test::TestResult::Rejected); \
|
|
|
- return; \
|
|
|
- } \
|
|
|
+#define ASSUME(x) \
|
|
|
+ do { \
|
|
|
+ if (!::Test::assume(x, #x##sv)) \
|
|
|
+ return; \
|
|
|
} while (false)
|
|
|
|
|
|
#define FAIL(message) \
|