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