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