1 #include <algorithm>
2 #include <optional>
3
4 #include <c10/util/ArrayRef.h>
5 #include <c10/util/Logging.h>
6 #include <gtest/gtest.h>
7
8 namespace c10_test {
9
10 using std::set;
11 using std::string;
12 using std::vector;
13
TEST(LoggingTest,TestEnforceTrue)14 TEST(LoggingTest, TestEnforceTrue) {
15 // This should just work.
16 CAFFE_ENFORCE(true, "Isn't it?");
17 }
18
TEST(LoggingTest,TestEnforceFalse)19 TEST(LoggingTest, TestEnforceFalse) {
20 bool kFalse = false;
21 std::swap(FLAGS_caffe2_use_fatal_for_enforce, kFalse);
22 try {
23 CAFFE_ENFORCE(false, "This throws.");
24 // This should never be triggered.
25 ADD_FAILURE();
26 } catch (const ::c10::Error&) {
27 }
28 std::swap(FLAGS_caffe2_use_fatal_for_enforce, kFalse);
29 }
30
TEST(LoggingTest,TestEnforceEquals)31 TEST(LoggingTest, TestEnforceEquals) {
32 int x = 4;
33 int y = 5;
34 int z = 0;
35 try {
36 CAFFE_ENFORCE_THAT(std::equal_to<void>(), ==, ++x, ++y, "Message: ", z++);
37 // This should never be triggered.
38 ADD_FAILURE();
39 } catch (const ::c10::Error& err) {
40 auto errStr = std::string(err.what());
41 EXPECT_NE(errStr.find("5 vs 6"), string::npos);
42 EXPECT_NE(errStr.find("Message: 0"), string::npos);
43 }
44
45 // arguments are expanded only once
46 CAFFE_ENFORCE_THAT(std::equal_to<void>(), ==, ++x, y);
47 EXPECT_EQ(x, 6);
48 EXPECT_EQ(y, 6);
49 EXPECT_EQ(z, 1);
50 }
51
52 namespace {
53 struct EnforceEqWithCaller {
testc10_test::__anon5a7771a50111::EnforceEqWithCaller54 void test(const char* x) {
55 CAFFE_ENFORCE_EQ_WITH_CALLER(1, 1, "variable: ", x, " is a variable");
56 }
57 };
58 } // namespace
59
TEST(LoggingTest,TestEnforceMessageVariables)60 TEST(LoggingTest, TestEnforceMessageVariables) {
61 const char* const x = "hello";
62 CAFFE_ENFORCE_EQ(1, 1, "variable: ", x, " is a variable");
63
64 EnforceEqWithCaller e;
65 e.test(x);
66 }
67
TEST(LoggingTest,EnforceEqualsObjectWithReferenceToTemporaryWithoutUseOutOfScope)68 TEST(
69 LoggingTest,
70 EnforceEqualsObjectWithReferenceToTemporaryWithoutUseOutOfScope) {
71 std::vector<int> x = {1, 2, 3, 4};
72 // This case is a little tricky. We have a temporary
73 // std::initializer_list to which our temporary ArrayRef
74 // refers. Temporary lifetime extension by binding a const reference
75 // to the ArrayRef doesn't extend the lifetime of the
76 // std::initializer_list, just the ArrayRef, so we end up with a
77 // dangling ArrayRef. This test forces the implementation to get it
78 // right.
79 CAFFE_ENFORCE_EQ(x, (at::ArrayRef<int>{1, 2, 3, 4}));
80 }
81
82 namespace {
83 struct Noncopyable {
84 int x;
85
Noncopyablec10_test::__anon5a7771a50211::Noncopyable86 explicit Noncopyable(int a) : x(a) {}
87
88 Noncopyable(const Noncopyable&) = delete;
89 Noncopyable(Noncopyable&&) = delete;
90 Noncopyable& operator=(const Noncopyable&) = delete;
91 Noncopyable& operator=(Noncopyable&&) = delete;
92
operator ==c10_test::__anon5a7771a50211::Noncopyable93 bool operator==(const Noncopyable& rhs) const {
94 return x == rhs.x;
95 }
96 };
97
operator <<(std::ostream & out,const Noncopyable & nc)98 std::ostream& operator<<(std::ostream& out, const Noncopyable& nc) {
99 out << "Noncopyable(" << nc.x << ")";
100 return out;
101 }
102 } // namespace
103
TEST(LoggingTest,DoesntCopyComparedObjects)104 TEST(LoggingTest, DoesntCopyComparedObjects) {
105 CAFFE_ENFORCE_EQ(Noncopyable(123), Noncopyable(123));
106 }
107
TEST(LoggingTest,EnforceShowcase)108 TEST(LoggingTest, EnforceShowcase) {
109 // It's not really a test but rather a convenient thing that you can run and
110 // see all messages
111 int one = 1;
112 int two = 2;
113 int three = 3;
114 #define WRAP_AND_PRINT(exp) \
115 try { \
116 exp; \
117 } catch (const ::c10::Error&) { \
118 /* ::c10::Error already does LOG(ERROR) */ \
119 }
120 WRAP_AND_PRINT(CAFFE_ENFORCE_EQ(one, two));
121 WRAP_AND_PRINT(CAFFE_ENFORCE_NE(one * 2, two));
122 WRAP_AND_PRINT(CAFFE_ENFORCE_GT(one, two));
123 WRAP_AND_PRINT(CAFFE_ENFORCE_GE(one, two));
124 WRAP_AND_PRINT(CAFFE_ENFORCE_LT(three, two));
125 WRAP_AND_PRINT(CAFFE_ENFORCE_LE(three, two));
126
127 WRAP_AND_PRINT(CAFFE_ENFORCE_EQ(
128 one * two + three, three * two, "It's a pretty complicated expression"));
129
130 WRAP_AND_PRINT(CAFFE_ENFORCE_THAT(
131 std::equal_to<void>(), ==, one * two + three, three * two));
132 }
133
TEST(LoggingTest,Join)134 TEST(LoggingTest, Join) {
135 auto s = c10::Join(", ", vector<int>({1, 2, 3}));
136 EXPECT_EQ(s, "1, 2, 3");
137 s = c10::Join(":", vector<string>());
138 EXPECT_EQ(s, "");
139 s = c10::Join(", ", set<int>({3, 1, 2}));
140 EXPECT_EQ(s, "1, 2, 3");
141 }
142
TEST(LoggingTest,TestDanglingElse)143 TEST(LoggingTest, TestDanglingElse) {
144 if (true)
145 TORCH_DCHECK_EQ(1, 1);
146 else
147 GTEST_FAIL();
148 }
149
150 #if GTEST_HAS_DEATH_TEST
TEST(LoggingDeathTest,TestEnforceUsingFatal)151 TEST(LoggingDeathTest, TestEnforceUsingFatal) {
152 bool kTrue = true;
153 std::swap(FLAGS_caffe2_use_fatal_for_enforce, kTrue);
154 // NOLINTNEXTLINE(cppcoreguidelines-avoid-goto,hicpp-avoid-goto)
155 EXPECT_DEATH(CAFFE_ENFORCE(false, "This goes fatal."), "");
156 std::swap(FLAGS_caffe2_use_fatal_for_enforce, kTrue);
157 }
158 #endif
159
f1()160 C10_NOINLINE void f1() {
161 CAFFE_THROW("message");
162 }
163
f2()164 C10_NOINLINE void f2() {
165 f1();
166 }
167
f3()168 C10_NOINLINE void f3() {
169 f2();
170 }
171
172 #ifdef FBCODE_CAFFE2
TEST(LoggingTest,ExceptionWhat)173 TEST(LoggingTest, ExceptionWhat) {
174 std::optional<::c10::Error> error;
175 try {
176 f3();
177 } catch (const ::c10::Error& e) {
178 error = e;
179 }
180
181 ASSERT_TRUE(error);
182 std::string what = error->what();
183
184 EXPECT_TRUE(what.find("c10_test::f1()") != std::string::npos) << what;
185 EXPECT_TRUE(what.find("c10_test::f2()") != std::string::npos) << what;
186 EXPECT_TRUE(what.find("c10_test::f3()") != std::string::npos) << what;
187
188 // what() should be recomputed.
189 error->add_context("NewContext");
190 what = error->what();
191 EXPECT_TRUE(what.find("c10_test::f1()") != std::string::npos) << what;
192 EXPECT_TRUE(what.find("c10_test::f2()") != std::string::npos) << what;
193 EXPECT_TRUE(what.find("c10_test::f3()") != std::string::npos) << what;
194 EXPECT_TRUE(what.find("NewContext") != std::string::npos) << what;
195 }
196 #endif
197
TEST(LoggingTest,LazyBacktrace)198 TEST(LoggingTest, LazyBacktrace) {
199 struct CountingLazyString : ::c10::OptimisticLazyValue<std::string> {
200 mutable size_t invocations{0};
201
202 std::string compute() const override {
203 ++invocations;
204 return "A string";
205 }
206 };
207
208 auto backtrace = std::make_shared<CountingLazyString>();
209 ::c10::Error ex("", backtrace);
210 // The backtrace is not computed on construction, and then it is not computed
211 // more than once.
212 EXPECT_EQ(backtrace->invocations, 0);
213 const char* w1 = ex.what();
214 EXPECT_EQ(backtrace->invocations, 1);
215 const char* w2 = ex.what();
216 EXPECT_EQ(backtrace->invocations, 1);
217 // what() should not be recomputed.
218 EXPECT_EQ(w1, w2);
219
220 ex.add_context("");
221 ex.what();
222 EXPECT_EQ(backtrace->invocations, 1);
223 }
224
225 } // namespace c10_test
226