1 // Formatting library for C++ - custom Google Test assertions
2 //
3 // Copyright (c) 2012 - present, Victor Zverovich
4 // All rights reserved.
5 //
6 // For the license information refer to format.h.
7 
8 #ifndef FMT_GTEST_EXTRA_H_
9 #define FMT_GTEST_EXTRA_H_
10 
11 #include <string>
12 
13 #include "fmt/os.h"
14 #include "gmock.h"
15 
16 #define FMT_TEST_THROW_(statement, expected_exception, expected_message, fail) \
17   GTEST_AMBIGUOUS_ELSE_BLOCKER_                                                \
18   if (::testing::AssertionResult gtest_ar = ::testing::AssertionSuccess()) {   \
19     std::string gtest_expected_message = expected_message;                     \
20     bool gtest_caught_expected = false;                                        \
21     try {                                                                      \
22       GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement);               \
23     } catch (expected_exception const& e) {                                    \
24       if (gtest_expected_message != e.what()) {                                \
25         gtest_ar << #statement                                                 \
26             " throws an exception with a different message.\n"                 \
27                  << "Expected: " << gtest_expected_message << "\n"             \
28                  << "  Actual: " << e.what();                                  \
29         goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__);            \
30       }                                                                        \
31       gtest_caught_expected = true;                                            \
32     } catch (...) {                                                            \
33       gtest_ar << "Expected: " #statement                                      \
34                   " throws an exception of type " #expected_exception          \
35                   ".\n  Actual: it throws a different type.";                  \
36       goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__);              \
37     }                                                                          \
38     if (!gtest_caught_expected) {                                              \
39       gtest_ar << "Expected: " #statement                                      \
40                   " throws an exception of type " #expected_exception          \
41                   ".\n  Actual: it throws nothing.";                           \
42       goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__);              \
43     }                                                                          \
44   } else                                                                       \
45     GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__)                      \
46         : fail(gtest_ar.failure_message())
47 
48 // Tests that the statement throws the expected exception and the exception's
49 // what() method returns expected message.
50 #define EXPECT_THROW_MSG(statement, expected_exception, expected_message) \
51   FMT_TEST_THROW_(statement, expected_exception, expected_message,        \
52                   GTEST_NONFATAL_FAILURE_)
53 
54 std::string format_system_error(int error_code, fmt::string_view message);
55 
56 #define EXPECT_SYSTEM_ERROR(statement, error_code, message) \
57   EXPECT_THROW_MSG(statement, fmt::system_error,            \
58                    format_system_error(error_code, message))
59 
60 #if FMT_USE_FCNTL
61 
62 // Captures file output by redirecting it to a pipe.
63 // The output it can handle is limited by the pipe capacity.
64 class OutputRedirect {
65  private:
66   FILE* file_;
67   fmt::file original_;  // Original file passed to redirector.
68   fmt::file read_end_;  // Read end of the pipe where the output is redirected.
69 
70   GTEST_DISALLOW_COPY_AND_ASSIGN_(OutputRedirect);
71 
72   void flush();
73   void restore();
74 
75  public:
76   explicit OutputRedirect(FILE* file);
77   ~OutputRedirect() FMT_NOEXCEPT;
78 
79   // Restores the original file, reads output from the pipe into a string
80   // and returns it.
81   std::string restore_and_read();
82 };
83 
84 #  define FMT_TEST_WRITE_(statement, expected_output, file, fail)              \
85     GTEST_AMBIGUOUS_ELSE_BLOCKER_                                              \
86     if (::testing::AssertionResult gtest_ar = ::testing::AssertionSuccess()) { \
87       std::string gtest_expected_output = expected_output;                     \
88       OutputRedirect gtest_redir(file);                                        \
89       GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement);               \
90       std::string gtest_output = gtest_redir.restore_and_read();               \
91       if (gtest_output != gtest_expected_output) {                             \
92         gtest_ar << #statement " produces different output.\n"                 \
93                  << "Expected: " << gtest_expected_output << "\n"              \
94                  << "  Actual: " << gtest_output;                              \
95         goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__);            \
96       }                                                                        \
97     } else                                                                     \
98       GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__)                    \
99           : fail(gtest_ar.failure_message())
100 
101 // Tests that the statement writes the expected output to file.
102 #  define EXPECT_WRITE(file, statement, expected_output) \
103     FMT_TEST_WRITE_(statement, expected_output, file, GTEST_NONFATAL_FAILURE_)
104 
105 #  ifdef _MSC_VER
106 
107 // Suppresses Windows assertions on invalid file descriptors, making
108 // POSIX functions return proper error codes instead of crashing on Windows.
109 class SuppressAssert {
110  private:
111   _invalid_parameter_handler original_handler_;
112   int original_report_mode_;
113 
handle_invalid_parameter(const wchar_t *,const wchar_t *,const wchar_t *,unsigned,uintptr_t)114   static void handle_invalid_parameter(const wchar_t*, const wchar_t*,
115                                        const wchar_t*, unsigned, uintptr_t) {}
116 
117  public:
SuppressAssert()118   SuppressAssert()
119       : original_handler_(
120             _set_invalid_parameter_handler(handle_invalid_parameter)),
121         original_report_mode_(_CrtSetReportMode(_CRT_ASSERT, 0)) {}
~SuppressAssert()122   ~SuppressAssert() {
123     _set_invalid_parameter_handler(original_handler_);
124     _CrtSetReportMode(_CRT_ASSERT, original_report_mode_);
125   }
126 };
127 
128 #    define SUPPRESS_ASSERT(statement) \
129       {                                \
130         SuppressAssert sa;             \
131         statement;                     \
132       }
133 #  else
134 #    define SUPPRESS_ASSERT(statement) statement
135 #  endif  // _MSC_VER
136 
137 #  define EXPECT_SYSTEM_ERROR_NOASSERT(statement, error_code, message) \
138     EXPECT_SYSTEM_ERROR(SUPPRESS_ASSERT(statement), error_code, message)
139 
140 // Attempts to read count characters from a file.
141 std::string read(fmt::file& f, size_t count);
142 
143 #  define EXPECT_READ(file, expected_content) \
144     EXPECT_EQ(expected_content, \
145               read(file, fmt::string_view(expected_content).size()))
146 
147 #else
148 #  define EXPECT_WRITE(file, statement, expected_output) \
149     do {                                                 \
150       (void)(file);                                      \
151       (void)(statement);                                 \
152       (void)(expected_output);                           \
153       SUCCEED();                                         \
154     } while (false)
155 #endif  // FMT_USE_FCNTL
156 
157 template <typename Mock> struct ScopedMock : testing::StrictMock<Mock> {
ScopedMockScopedMock158   ScopedMock() { Mock::instance = this; }
~ScopedMockScopedMock159   ~ScopedMock() { Mock::instance = nullptr; }
160 };
161 
162 #endif  // FMT_GTEST_EXTRA_H_
163