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