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