xref: /aosp_15_r20/external/pytorch/c10/test/util/logging_test.cpp (revision da0073e96a02ea20f0ac840b70461e3646d07c45)
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