summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibTest/Macros.h
blob: ee500be68893491136416e2cde68ce3dd5151874 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
/*
 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
 * Copyright (c) 2021, Andrew Kaster <akaster@serenityos.org>
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#pragma once

#include <AK/Assertions.h>
#include <AK/CheckedFormatString.h>
#include <AK/Math.h>
#include <LibTest/CrashTest.h>

namespace AK {
template<typename... Parameters>
void warnln(CheckedFormatString<Parameters...>&& fmtstr, Parameters const&...);
}

namespace Test {
// Declare a helper so that we can call it from VERIFY in included headers
void current_test_case_did_fail();
}

#undef VERIFY
#define VERIFY(x)                                                                                    \
    do {                                                                                             \
        if (!(x)) {                                                                                  \
            ::AK::warnln("\033[31;1mFAIL\033[0m: {}:{}: VERIFY({}) failed", __FILE__, __LINE__, #x); \
            ::Test::current_test_case_did_fail();                                                    \
        }                                                                                            \
    } while (false)

#undef VERIFY_NOT_REACHED
#define VERIFY_NOT_REACHED()                                                                           \
    do {                                                                                               \
        ::AK::warnln("\033[31;1mFAIL\033[0m: {}:{}: VERIFY_NOT_REACHED() called", __FILE__, __LINE__); \
        ::abort();                                                                                     \
    } while (false)

#undef TODO
#define TODO()                                                                           \
    do {                                                                                 \
        ::AK::warnln("\033[31;1mFAIL\033[0m: {}:{}: TODO() called", __FILE__, __LINE__); \
        ::abort();                                                                       \
    } while (false)

#define EXPECT_EQ(a, b)                                                                                                                                                                      \
    do {                                                                                                                                                                                     \
        auto lhs = (a);                                                                                                                                                                      \
        auto rhs = (b);                                                                                                                                                                      \
        if (lhs != rhs) {                                                                                                                                                                    \
            ::AK::warnln("\033[31;1mFAIL\033[0m: {}:{}: EXPECT_EQ({}, {}) failed with lhs={} and rhs={}", __FILE__, __LINE__, #a, #b, FormatIfSupported { lhs }, FormatIfSupported { rhs }); \
            ::Test::current_test_case_did_fail();                                                                                                                                            \
        }                                                                                                                                                                                    \
    } 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) {                                                                                           \
            ::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::current_test_case_did_fail();                                                                         \
        }                                                                                                                 \
    } 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) {                                                                                                                        \
            ::AK::warnln("\033[31;1mFAIL\033[0m: {}:{}: EXPECT_EQ({}, {}) failed with lhs={} and rhs={}", __FILE__, __LINE__, #a, #b, lhs, rhs); \
            ::Test::current_test_case_did_fail();                                                                                                \
        }                                                                                                                                        \
    } while (false)

#define EXPECT_NE(a, b)                                                                                                                                                                      \
    do {                                                                                                                                                                                     \
        auto lhs = (a);                                                                                                                                                                      \
        auto rhs = (b);                                                                                                                                                                      \
        if (lhs == rhs) {                                                                                                                                                                    \
            ::AK::warnln("\033[31;1mFAIL\033[0m: {}:{}: EXPECT_NE({}, {}) failed with lhs={} and rhs={}", __FILE__, __LINE__, #a, #b, FormatIfSupported { lhs }, FormatIfSupported { rhs }); \
            ::Test::current_test_case_did_fail();                                                                                                                                            \
        }                                                                                                                                                                                    \
    } while (false)

#define EXPECT(x)                                                                                    \
    do {                                                                                             \
        if (!(x)) {                                                                                  \
            ::AK::warnln("\033[31;1mFAIL\033[0m: {}:{}: EXPECT({}) failed", __FILE__, __LINE__, #x); \
            ::Test::current_test_case_did_fail();                                                    \
        }                                                                                            \
    } 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)) {                                                              \
            ::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::current_test_case_did_fail();                                                               \
        }                                                                                                       \
    } while (false)

#define EXPECT_APPROXIMATE(a, b) EXPECT_APPROXIMATE_WITH_ERROR(a, b, 0.0000005)

#define FAIL(message)                                                                  \
    do {                                                                               \
        ::AK::warnln("\033[31;1mFAIL\033[0m: {}:{}: {}", __FILE__, __LINE__, message); \
        ::Test::current_test_case_did_fail();                                          \
    } while (false)

// To use, specify the lambda to execute in a sub process and verify it exits:
//  EXPECT_CRASH("This should fail", []{
//      return Test::Crash::Failure::DidNotCrash;
//  });
#define EXPECT_CRASH(test_message, test_func)       \
    do {                                            \
        Test::Crash crash(test_message, test_func); \
        if (!crash.run())                           \
            ::Test::current_test_case_did_fail();   \
    } while (false)

#define EXPECT_CRASH_WITH_SIGNAL(test_message, signal, test_func) \
    do {                                                          \
        Test::Crash crash(test_message, test_func, (signal));     \
        if (!crash.run())                                         \
            ::Test::current_test_case_did_fail();                 \
    } while (false)

#define EXPECT_NO_CRASH(test_message, test_func)       \
    do {                                               \
        Test::Crash crash(test_message, test_func, 0); \
        if (!crash.run())                              \
            ::Test::current_test_case_did_fail();      \
    } while (false)

#define TRY_OR_FAIL(expression)                                                                      \
    ({                                                                                               \
        /* Ignore -Wshadow to allow nesting the macro. */                                            \
        AK_IGNORE_DIAGNOSTIC("-Wshadow",                                                             \
            auto&& _temporary_result = (expression));                                                \
        static_assert(!::AK::Detail::IsLvalueReference<decltype(_temporary_result.release_value())>, \
            "Do not return a reference from a fallible expression");                                 \
        if (_temporary_result.is_error()) [[unlikely]] {                                             \
            FAIL(_temporary_result.release_error());                                                 \
            return;                                                                                  \
        }                                                                                            \
        _temporary_result.release_value();                                                           \
    })