• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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   void flush();
71   void restore();
72 
73  public:
74   explicit OutputRedirect(FILE* file);
75   ~OutputRedirect() FMT_NOEXCEPT;
76 
77   OutputRedirect(const OutputRedirect&) = delete;
78   OutputRedirect& operator=(const OutputRedirect&) = delete;
79 
80   // Restores the original file, reads output from the pipe into a string
81   // and returns it.
82   std::string restore_and_read();
83 };
84 
85 #  define FMT_TEST_WRITE_(statement, expected_output, file, fail)              \
86     GTEST_AMBIGUOUS_ELSE_BLOCKER_                                              \
87     if (::testing::AssertionResult gtest_ar = ::testing::AssertionSuccess()) { \
88       std::string gtest_expected_output = expected_output;                     \
89       OutputRedirect gtest_redir(file);                                        \
90       GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement);               \
91       std::string gtest_output = gtest_redir.restore_and_read();               \
92       if (gtest_output != gtest_expected_output) {                             \
93         gtest_ar << #statement " produces different output.\n"                 \
94                  << "Expected: " << gtest_expected_output << "\n"              \
95                  << "  Actual: " << gtest_output;                              \
96         goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__);            \
97       }                                                                        \
98     } else                                                                     \
99       GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__)                    \
100           : fail(gtest_ar.failure_message())
101 
102 // Tests that the statement writes the expected output to file.
103 #  define EXPECT_WRITE(file, statement, expected_output) \
104     FMT_TEST_WRITE_(statement, expected_output, file, GTEST_NONFATAL_FAILURE_)
105 
106 #  ifdef _MSC_VER
107 
108 // Suppresses Windows assertions on invalid file descriptors, making
109 // POSIX functions return proper error codes instead of crashing on Windows.
110 class SuppressAssert {
111  private:
112   _invalid_parameter_handler original_handler_;
113   int original_report_mode_;
114 
handle_invalid_parameter(const wchar_t *,const wchar_t *,const wchar_t *,unsigned,uintptr_t)115   static void handle_invalid_parameter(const wchar_t*, const wchar_t*,
116                                        const wchar_t*, unsigned, uintptr_t) {}
117 
118  public:
SuppressAssert()119   SuppressAssert()
120       : original_handler_(
121             _set_invalid_parameter_handler(handle_invalid_parameter)),
122         original_report_mode_(_CrtSetReportMode(_CRT_ASSERT, 0)) {}
~SuppressAssert()123   ~SuppressAssert() {
124     _set_invalid_parameter_handler(original_handler_);
125     _CrtSetReportMode(_CRT_ASSERT, original_report_mode_);
126   }
127 };
128 
129 #    define SUPPRESS_ASSERT(statement) \
130       {                                \
131         SuppressAssert sa;             \
132         statement;                     \
133       }
134 #  else
135 #    define SUPPRESS_ASSERT(statement) statement
136 #  endif  // _MSC_VER
137 
138 #  define EXPECT_SYSTEM_ERROR_NOASSERT(statement, error_code, message) \
139     EXPECT_SYSTEM_ERROR(SUPPRESS_ASSERT(statement), error_code, message)
140 
141 // Attempts to read count characters from a file.
142 std::string read(fmt::file& f, size_t count);
143 
144 #  define EXPECT_READ(file, expected_content) \
145     EXPECT_EQ(expected_content, \
146               read(file, fmt::string_view(expected_content).size()))
147 
148 #else
149 #  define EXPECT_WRITE(file, statement, expected_output) \
150     do {                                                 \
151       (void)(file);                                      \
152       (void)(statement);                                 \
153       (void)(expected_output);                           \
154       SUCCEED();                                         \
155     } while (false)
156 #endif  // FMT_USE_FCNTL
157 
158 template <typename Mock> struct ScopedMock : testing::StrictMock<Mock> {
ScopedMockScopedMock159   ScopedMock() { Mock::instance = this; }
~ScopedMockScopedMock160   ~ScopedMock() { Mock::instance = nullptr; }
161 };
162 
163 #endif  // FMT_GTEST_EXTRA_H_
164