1 // Copyright 2019 The Abseil Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "absl/status/status.h"
16 
17 #include "gmock/gmock.h"
18 #include "gtest/gtest.h"
19 #include "absl/strings/str_cat.h"
20 
21 namespace {
22 
23 using ::testing::Eq;
24 using ::testing::HasSubstr;
25 using ::testing::Optional;
26 using ::testing::UnorderedElementsAreArray;
27 
TEST(StatusCode,InsertionOperator)28 TEST(StatusCode, InsertionOperator) {
29   const absl::StatusCode code = absl::StatusCode::kUnknown;
30   std::ostringstream oss;
31   oss << code;
32   EXPECT_EQ(oss.str(), absl::StatusCodeToString(code));
33 }
34 
35 // This structure holds the details for testing a single error code,
36 // its creator, and its classifier.
37 struct ErrorTest {
38   absl::StatusCode code;
39   using Creator = absl::Status (*)(absl::string_view);
40   using Classifier = bool (*)(const absl::Status&);
41   Creator creator;
42   Classifier classifier;
43 };
44 
45 constexpr ErrorTest kErrorTests[]{
46     {absl::StatusCode::kCancelled, absl::CancelledError, absl::IsCancelled},
47     {absl::StatusCode::kUnknown, absl::UnknownError, absl::IsUnknown},
48     {absl::StatusCode::kInvalidArgument, absl::InvalidArgumentError,
49      absl::IsInvalidArgument},
50     {absl::StatusCode::kDeadlineExceeded, absl::DeadlineExceededError,
51      absl::IsDeadlineExceeded},
52     {absl::StatusCode::kNotFound, absl::NotFoundError, absl::IsNotFound},
53     {absl::StatusCode::kAlreadyExists, absl::AlreadyExistsError,
54      absl::IsAlreadyExists},
55     {absl::StatusCode::kPermissionDenied, absl::PermissionDeniedError,
56      absl::IsPermissionDenied},
57     {absl::StatusCode::kResourceExhausted, absl::ResourceExhaustedError,
58      absl::IsResourceExhausted},
59     {absl::StatusCode::kFailedPrecondition, absl::FailedPreconditionError,
60      absl::IsFailedPrecondition},
61     {absl::StatusCode::kAborted, absl::AbortedError, absl::IsAborted},
62     {absl::StatusCode::kOutOfRange, absl::OutOfRangeError, absl::IsOutOfRange},
63     {absl::StatusCode::kUnimplemented, absl::UnimplementedError,
64      absl::IsUnimplemented},
65     {absl::StatusCode::kInternal, absl::InternalError, absl::IsInternal},
66     {absl::StatusCode::kUnavailable, absl::UnavailableError,
67      absl::IsUnavailable},
68     {absl::StatusCode::kDataLoss, absl::DataLossError, absl::IsDataLoss},
69     {absl::StatusCode::kUnauthenticated, absl::UnauthenticatedError,
70      absl::IsUnauthenticated},
71 };
72 
TEST(Status,CreateAndClassify)73 TEST(Status, CreateAndClassify) {
74   for (const auto& test : kErrorTests) {
75     SCOPED_TRACE(absl::StatusCodeToString(test.code));
76 
77     // Ensure that the creator does, in fact, create status objects with the
78     // expected error code and message.
79     std::string message =
80         absl::StrCat("error code ", test.code, " test message");
81     absl::Status status = test.creator(message);
82     EXPECT_EQ(test.code, status.code());
83     EXPECT_EQ(message, status.message());
84 
85     // Ensure that the classifier returns true for a status produced by the
86     // creator.
87     EXPECT_TRUE(test.classifier(status));
88 
89     // Ensure that the classifier returns false for status with a different
90     // code.
91     for (const auto& other : kErrorTests) {
92       if (other.code != test.code) {
93         EXPECT_FALSE(test.classifier(absl::Status(other.code, "")))
94             << " other.code = " << other.code;
95       }
96     }
97   }
98 }
99 
TEST(Status,DefaultConstructor)100 TEST(Status, DefaultConstructor) {
101   absl::Status status;
102   EXPECT_TRUE(status.ok());
103   EXPECT_EQ(absl::StatusCode::kOk, status.code());
104   EXPECT_EQ("", status.message());
105 }
106 
TEST(Status,OkStatus)107 TEST(Status, OkStatus) {
108   absl::Status status = absl::OkStatus();
109   EXPECT_TRUE(status.ok());
110   EXPECT_EQ(absl::StatusCode::kOk, status.code());
111   EXPECT_EQ("", status.message());
112 }
113 
TEST(Status,ConstructorWithCodeMessage)114 TEST(Status, ConstructorWithCodeMessage) {
115   {
116     absl::Status status(absl::StatusCode::kCancelled, "");
117     EXPECT_FALSE(status.ok());
118     EXPECT_EQ(absl::StatusCode::kCancelled, status.code());
119     EXPECT_EQ("", status.message());
120   }
121   {
122     absl::Status status(absl::StatusCode::kInternal, "message");
123     EXPECT_FALSE(status.ok());
124     EXPECT_EQ(absl::StatusCode::kInternal, status.code());
125     EXPECT_EQ("message", status.message());
126   }
127 }
128 
TEST(Status,ConstructOutOfRangeCode)129 TEST(Status, ConstructOutOfRangeCode) {
130   const int kRawCode = 9999;
131   absl::Status status(static_cast<absl::StatusCode>(kRawCode), "");
132   EXPECT_EQ(absl::StatusCode::kUnknown, status.code());
133   EXPECT_EQ(kRawCode, status.raw_code());
134 }
135 
136 constexpr char kUrl1[] = "url.payload.1";
137 constexpr char kUrl2[] = "url.payload.2";
138 constexpr char kUrl3[] = "url.payload.3";
139 constexpr char kUrl4[] = "url.payload.xx";
140 
141 constexpr char kPayload1[] = "aaaaa";
142 constexpr char kPayload2[] = "bbbbb";
143 constexpr char kPayload3[] = "ccccc";
144 
145 using PayloadsVec = std::vector<std::pair<std::string, absl::Cord>>;
146 
TEST(Status,TestGetSetPayload)147 TEST(Status, TestGetSetPayload) {
148   absl::Status ok_status = absl::OkStatus();
149   ok_status.SetPayload(kUrl1, absl::Cord(kPayload1));
150   ok_status.SetPayload(kUrl2, absl::Cord(kPayload2));
151 
152   EXPECT_FALSE(ok_status.GetPayload(kUrl1));
153   EXPECT_FALSE(ok_status.GetPayload(kUrl2));
154 
155   absl::Status bad_status(absl::StatusCode::kInternal, "fail");
156   bad_status.SetPayload(kUrl1, absl::Cord(kPayload1));
157   bad_status.SetPayload(kUrl2, absl::Cord(kPayload2));
158 
159   EXPECT_THAT(bad_status.GetPayload(kUrl1), Optional(Eq(kPayload1)));
160   EXPECT_THAT(bad_status.GetPayload(kUrl2), Optional(Eq(kPayload2)));
161 
162   EXPECT_FALSE(bad_status.GetPayload(kUrl3));
163 
164   bad_status.SetPayload(kUrl1, absl::Cord(kPayload3));
165   EXPECT_THAT(bad_status.GetPayload(kUrl1), Optional(Eq(kPayload3)));
166 
167   // Testing dynamically generated type_url
168   bad_status.SetPayload(absl::StrCat(kUrl1, ".1"), absl::Cord(kPayload1));
169   EXPECT_THAT(bad_status.GetPayload(absl::StrCat(kUrl1, ".1")),
170               Optional(Eq(kPayload1)));
171 }
172 
TEST(Status,TestErasePayload)173 TEST(Status, TestErasePayload) {
174   absl::Status bad_status(absl::StatusCode::kInternal, "fail");
175   bad_status.SetPayload(kUrl1, absl::Cord(kPayload1));
176   bad_status.SetPayload(kUrl2, absl::Cord(kPayload2));
177   bad_status.SetPayload(kUrl3, absl::Cord(kPayload3));
178 
179   EXPECT_FALSE(bad_status.ErasePayload(kUrl4));
180 
181   EXPECT_TRUE(bad_status.GetPayload(kUrl2));
182   EXPECT_TRUE(bad_status.ErasePayload(kUrl2));
183   EXPECT_FALSE(bad_status.GetPayload(kUrl2));
184   EXPECT_FALSE(bad_status.ErasePayload(kUrl2));
185 
186   EXPECT_TRUE(bad_status.ErasePayload(kUrl1));
187   EXPECT_TRUE(bad_status.ErasePayload(kUrl3));
188 
189   bad_status.SetPayload(kUrl1, absl::Cord(kPayload1));
190   EXPECT_TRUE(bad_status.ErasePayload(kUrl1));
191 }
192 
TEST(Status,TestComparePayloads)193 TEST(Status, TestComparePayloads) {
194   absl::Status bad_status1(absl::StatusCode::kInternal, "fail");
195   bad_status1.SetPayload(kUrl1, absl::Cord(kPayload1));
196   bad_status1.SetPayload(kUrl2, absl::Cord(kPayload2));
197   bad_status1.SetPayload(kUrl3, absl::Cord(kPayload3));
198 
199   absl::Status bad_status2(absl::StatusCode::kInternal, "fail");
200   bad_status2.SetPayload(kUrl2, absl::Cord(kPayload2));
201   bad_status2.SetPayload(kUrl3, absl::Cord(kPayload3));
202   bad_status2.SetPayload(kUrl1, absl::Cord(kPayload1));
203 
204   EXPECT_EQ(bad_status1, bad_status2);
205 }
206 
TEST(Status,TestComparePayloadsAfterErase)207 TEST(Status, TestComparePayloadsAfterErase) {
208   absl::Status payload_status(absl::StatusCode::kInternal, "");
209   payload_status.SetPayload(kUrl1, absl::Cord(kPayload1));
210   payload_status.SetPayload(kUrl2, absl::Cord(kPayload2));
211 
212   absl::Status empty_status(absl::StatusCode::kInternal, "");
213 
214   // Different payloads, not equal
215   EXPECT_NE(payload_status, empty_status);
216   EXPECT_TRUE(payload_status.ErasePayload(kUrl1));
217 
218   // Still Different payloads, still not equal.
219   EXPECT_NE(payload_status, empty_status);
220   EXPECT_TRUE(payload_status.ErasePayload(kUrl2));
221 
222   // Both empty payloads, should be equal
223   EXPECT_EQ(payload_status, empty_status);
224 }
225 
AllVisitedPayloads(const absl::Status & s)226 PayloadsVec AllVisitedPayloads(const absl::Status& s) {
227   PayloadsVec result;
228 
229   s.ForEachPayload([&](absl::string_view type_url, const absl::Cord& payload) {
230     result.push_back(std::make_pair(std::string(type_url), payload));
231   });
232 
233   return result;
234 }
235 
TEST(Status,TestForEachPayload)236 TEST(Status, TestForEachPayload) {
237   absl::Status bad_status(absl::StatusCode::kInternal, "fail");
238   bad_status.SetPayload(kUrl1, absl::Cord(kPayload1));
239   bad_status.SetPayload(kUrl2, absl::Cord(kPayload2));
240   bad_status.SetPayload(kUrl3, absl::Cord(kPayload3));
241 
242   int count = 0;
243 
244   bad_status.ForEachPayload(
245       [&count](absl::string_view, const absl::Cord&) { ++count; });
246 
247   EXPECT_EQ(count, 3);
248 
249   PayloadsVec expected_payloads = {{kUrl1, absl::Cord(kPayload1)},
250                                    {kUrl2, absl::Cord(kPayload2)},
251                                    {kUrl3, absl::Cord(kPayload3)}};
252 
253   // Test that we visit all the payloads in the status.
254   PayloadsVec visited_payloads = AllVisitedPayloads(bad_status);
255   EXPECT_THAT(visited_payloads, UnorderedElementsAreArray(expected_payloads));
256 
257   // Test that visitation order is not consistent between run.
258   std::vector<absl::Status> scratch;
259   while (true) {
260     scratch.emplace_back(absl::StatusCode::kInternal, "fail");
261 
262     scratch.back().SetPayload(kUrl1, absl::Cord(kPayload1));
263     scratch.back().SetPayload(kUrl2, absl::Cord(kPayload2));
264     scratch.back().SetPayload(kUrl3, absl::Cord(kPayload3));
265 
266     if (AllVisitedPayloads(scratch.back()) != visited_payloads) {
267       break;
268     }
269   }
270 }
271 
TEST(Status,ToString)272 TEST(Status, ToString) {
273   absl::Status s(absl::StatusCode::kInternal, "fail");
274   EXPECT_EQ("INTERNAL: fail", s.ToString());
275   s.SetPayload("foo", absl::Cord("bar"));
276   EXPECT_EQ("INTERNAL: fail [foo='bar']", s.ToString());
277   s.SetPayload("bar", absl::Cord("\377"));
278   EXPECT_THAT(s.ToString(),
279               AllOf(HasSubstr("INTERNAL: fail"), HasSubstr("[foo='bar']"),
280                     HasSubstr("[bar='\\xff']")));
281 }
282 
EraseAndReturn(const absl::Status & base)283 absl::Status EraseAndReturn(const absl::Status& base) {
284   absl::Status copy = base;
285   EXPECT_TRUE(copy.ErasePayload(kUrl1));
286   return copy;
287 }
288 
TEST(Status,CopyOnWriteForErasePayload)289 TEST(Status, CopyOnWriteForErasePayload) {
290   {
291     absl::Status base(absl::StatusCode::kInvalidArgument, "fail");
292     base.SetPayload(kUrl1, absl::Cord(kPayload1));
293     EXPECT_TRUE(base.GetPayload(kUrl1).has_value());
294     absl::Status copy = EraseAndReturn(base);
295     EXPECT_TRUE(base.GetPayload(kUrl1).has_value());
296     EXPECT_FALSE(copy.GetPayload(kUrl1).has_value());
297   }
298   {
299     absl::Status base(absl::StatusCode::kInvalidArgument, "fail");
300     base.SetPayload(kUrl1, absl::Cord(kPayload1));
301     absl::Status copy = base;
302 
303     EXPECT_TRUE(base.GetPayload(kUrl1).has_value());
304     EXPECT_TRUE(copy.GetPayload(kUrl1).has_value());
305 
306     EXPECT_TRUE(base.ErasePayload(kUrl1));
307 
308     EXPECT_FALSE(base.GetPayload(kUrl1).has_value());
309     EXPECT_TRUE(copy.GetPayload(kUrl1).has_value());
310   }
311 }
312 
TEST(Status,CopyConstructor)313 TEST(Status, CopyConstructor) {
314   {
315     absl::Status status;
316     absl::Status copy(status);
317     EXPECT_EQ(copy, status);
318   }
319   {
320     absl::Status status(absl::StatusCode::kInvalidArgument, "message");
321     absl::Status copy(status);
322     EXPECT_EQ(copy, status);
323   }
324   {
325     absl::Status status(absl::StatusCode::kInvalidArgument, "message");
326     status.SetPayload(kUrl1, absl::Cord(kPayload1));
327     absl::Status copy(status);
328     EXPECT_EQ(copy, status);
329   }
330 }
331 
TEST(Status,CopyAssignment)332 TEST(Status, CopyAssignment) {
333   absl::Status assignee;
334   {
335     absl::Status status;
336     assignee = status;
337     EXPECT_EQ(assignee, status);
338   }
339   {
340     absl::Status status(absl::StatusCode::kInvalidArgument, "message");
341     assignee = status;
342     EXPECT_EQ(assignee, status);
343   }
344   {
345     absl::Status status(absl::StatusCode::kInvalidArgument, "message");
346     status.SetPayload(kUrl1, absl::Cord(kPayload1));
347     assignee = status;
348     EXPECT_EQ(assignee, status);
349   }
350 }
351 
TEST(Status,CopyAssignmentIsNotRef)352 TEST(Status, CopyAssignmentIsNotRef) {
353   const absl::Status status_orig(absl::StatusCode::kInvalidArgument, "message");
354   absl::Status status_copy = status_orig;
355   EXPECT_EQ(status_orig, status_copy);
356   status_copy.SetPayload(kUrl1, absl::Cord(kPayload1));
357   EXPECT_NE(status_orig, status_copy);
358 }
359 
TEST(Status,MoveConstructor)360 TEST(Status, MoveConstructor) {
361   {
362     absl::Status status;
363     absl::Status copy(absl::Status{});
364     EXPECT_EQ(copy, status);
365   }
366   {
367     absl::Status status(absl::StatusCode::kInvalidArgument, "message");
368     absl::Status copy(
369         absl::Status(absl::StatusCode::kInvalidArgument, "message"));
370     EXPECT_EQ(copy, status);
371   }
372   {
373     absl::Status status(absl::StatusCode::kInvalidArgument, "message");
374     status.SetPayload(kUrl1, absl::Cord(kPayload1));
375     absl::Status copy1(status);
376     absl::Status copy2(std::move(status));
377     EXPECT_EQ(copy1, copy2);
378   }
379 }
380 
TEST(Status,MoveAssignment)381 TEST(Status, MoveAssignment) {
382   absl::Status assignee;
383   {
384     absl::Status status;
385     assignee = absl::Status();
386     EXPECT_EQ(assignee, status);
387   }
388   {
389     absl::Status status(absl::StatusCode::kInvalidArgument, "message");
390     assignee = absl::Status(absl::StatusCode::kInvalidArgument, "message");
391     EXPECT_EQ(assignee, status);
392   }
393   {
394     absl::Status status(absl::StatusCode::kInvalidArgument, "message");
395     status.SetPayload(kUrl1, absl::Cord(kPayload1));
396     absl::Status copy(status);
397     assignee = std::move(status);
398     EXPECT_EQ(assignee, copy);
399   }
400 }
401 
TEST(Status,Update)402 TEST(Status, Update) {
403   absl::Status s;
404   s.Update(absl::OkStatus());
405   EXPECT_TRUE(s.ok());
406   const absl::Status a(absl::StatusCode::kCancelled, "message");
407   s.Update(a);
408   EXPECT_EQ(s, a);
409   const absl::Status b(absl::StatusCode::kInternal, "other message");
410   s.Update(b);
411   EXPECT_EQ(s, a);
412   s.Update(absl::OkStatus());
413   EXPECT_EQ(s, a);
414   EXPECT_FALSE(s.ok());
415 }
416 
TEST(Status,Equality)417 TEST(Status, Equality) {
418   absl::Status ok;
419   absl::Status no_payload = absl::CancelledError("no payload");
420   absl::Status one_payload = absl::InvalidArgumentError("one payload");
421   one_payload.SetPayload(kUrl1, absl::Cord(kPayload1));
422   absl::Status two_payloads = one_payload;
423   two_payloads.SetPayload(kUrl2, absl::Cord(kPayload2));
424   const std::array<absl::Status, 4> status_arr = {ok, no_payload, one_payload,
425                                                   two_payloads};
426   for (int i = 0; i < status_arr.size(); i++) {
427     for (int j = 0; j < status_arr.size(); j++) {
428       if (i == j) {
429         EXPECT_TRUE(status_arr[i] == status_arr[j]);
430         EXPECT_FALSE(status_arr[i] != status_arr[j]);
431       } else {
432         EXPECT_TRUE(status_arr[i] != status_arr[j]);
433         EXPECT_FALSE(status_arr[i] == status_arr[j]);
434       }
435     }
436   }
437 }
438 
TEST(Status,Swap)439 TEST(Status, Swap) {
440   auto test_swap = [](const absl::Status& s1, const absl::Status& s2) {
441     absl::Status copy1 = s1, copy2 = s2;
442     swap(copy1, copy2);
443     EXPECT_EQ(copy1, s2);
444     EXPECT_EQ(copy2, s1);
445   };
446   const absl::Status ok;
447   const absl::Status no_payload(absl::StatusCode::kAlreadyExists, "no payload");
448   absl::Status with_payload(absl::StatusCode::kInternal, "with payload");
449   with_payload.SetPayload(kUrl1, absl::Cord(kPayload1));
450   test_swap(ok, no_payload);
451   test_swap(no_payload, ok);
452   test_swap(ok, with_payload);
453   test_swap(with_payload, ok);
454   test_swap(no_payload, with_payload);
455   test_swap(with_payload, no_payload);
456 }
457 
458 }  // namespace
459