1 // Copyright 2018 The Dawn 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 //     http://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 <gtest/gtest.h>
16 
17 #include "dawn_native/Error.h"
18 #include "dawn_native/ErrorData.h"
19 
20 using namespace dawn_native;
21 
22 namespace {
23 
24     int dummySuccess = 0xbeef;
25     const char* dummyErrorMessage = "I am an error message :3";
26 
27     // Check returning a success MaybeError with {};
TEST(ErrorTests, Error_Success)28     TEST(ErrorTests, Error_Success) {
29         auto ReturnSuccess = []() -> MaybeError { return {}; };
30 
31         MaybeError result = ReturnSuccess();
32         ASSERT_TRUE(result.IsSuccess());
33     }
34 
35     // Check returning an error MaybeError with "return DAWN_VALIDATION_ERROR"
TEST(ErrorTests, Error_Error)36     TEST(ErrorTests, Error_Error) {
37         auto ReturnError = []() -> MaybeError { return DAWN_VALIDATION_ERROR(dummyErrorMessage); };
38 
39         MaybeError result = ReturnError();
40         ASSERT_TRUE(result.IsError());
41 
42         std::unique_ptr<ErrorData> errorData = result.AcquireError();
43         ASSERT_EQ(errorData->GetMessage(), dummyErrorMessage);
44     }
45 
46     // Check returning a success ResultOrError with an implicit conversion
TEST(ErrorTests, ResultOrError_Success)47     TEST(ErrorTests, ResultOrError_Success) {
48         auto ReturnSuccess = []() -> ResultOrError<int*> { return &dummySuccess; };
49 
50         ResultOrError<int*> result = ReturnSuccess();
51         ASSERT_TRUE(result.IsSuccess());
52         ASSERT_EQ(result.AcquireSuccess(), &dummySuccess);
53     }
54 
55     // Check returning an error ResultOrError with "return DAWN_VALIDATION_ERROR"
TEST(ErrorTests, ResultOrError_Error)56     TEST(ErrorTests, ResultOrError_Error) {
57         auto ReturnError = []() -> ResultOrError<int*> {
58             return DAWN_VALIDATION_ERROR(dummyErrorMessage);
59         };
60 
61         ResultOrError<int*> result = ReturnError();
62         ASSERT_TRUE(result.IsError());
63 
64         std::unique_ptr<ErrorData> errorData = result.AcquireError();
65         ASSERT_EQ(errorData->GetMessage(), dummyErrorMessage);
66     }
67 
68     // Check DAWN_TRY handles successes correctly.
TEST(ErrorTests, TRY_Success)69     TEST(ErrorTests, TRY_Success) {
70         auto ReturnSuccess = []() -> MaybeError { return {}; };
71 
72         // We need to check that DAWN_TRY doesn't return on successes
73         bool tryReturned = true;
74 
75         auto Try = [ReturnSuccess, &tryReturned]() -> MaybeError {
76             DAWN_TRY(ReturnSuccess());
77             tryReturned = false;
78             return {};
79         };
80 
81         MaybeError result = Try();
82         ASSERT_TRUE(result.IsSuccess());
83         ASSERT_FALSE(tryReturned);
84     }
85 
86     // Check DAWN_TRY handles errors correctly.
TEST(ErrorTests, TRY_Error)87     TEST(ErrorTests, TRY_Error) {
88         auto ReturnError = []() -> MaybeError { return DAWN_VALIDATION_ERROR(dummyErrorMessage); };
89 
90         auto Try = [ReturnError]() -> MaybeError {
91             DAWN_TRY(ReturnError());
92             // DAWN_TRY should return before this point
93             EXPECT_FALSE(true);
94             return {};
95         };
96 
97         MaybeError result = Try();
98         ASSERT_TRUE(result.IsError());
99 
100         std::unique_ptr<ErrorData> errorData = result.AcquireError();
101         ASSERT_EQ(errorData->GetMessage(), dummyErrorMessage);
102     }
103 
104     // Check DAWN_TRY adds to the backtrace.
TEST(ErrorTests, TRY_AddsToBacktrace)105     TEST(ErrorTests, TRY_AddsToBacktrace) {
106         auto ReturnError = []() -> MaybeError { return DAWN_VALIDATION_ERROR(dummyErrorMessage); };
107 
108         auto SingleTry = [ReturnError]() -> MaybeError {
109             DAWN_TRY(ReturnError());
110             return {};
111         };
112 
113         auto DoubleTry = [SingleTry]() -> MaybeError {
114             DAWN_TRY(SingleTry());
115             return {};
116         };
117 
118         MaybeError singleResult = SingleTry();
119         ASSERT_TRUE(singleResult.IsError());
120 
121         MaybeError doubleResult = DoubleTry();
122         ASSERT_TRUE(doubleResult.IsError());
123 
124         std::unique_ptr<ErrorData> singleData = singleResult.AcquireError();
125         std::unique_ptr<ErrorData> doubleData = doubleResult.AcquireError();
126 
127         ASSERT_EQ(singleData->GetBacktrace().size() + 1, doubleData->GetBacktrace().size());
128     }
129 
130     // Check DAWN_TRY_ASSIGN handles successes correctly.
TEST(ErrorTests, TRY_RESULT_Success)131     TEST(ErrorTests, TRY_RESULT_Success) {
132         auto ReturnSuccess = []() -> ResultOrError<int*> { return &dummySuccess; };
133 
134         // We need to check that DAWN_TRY doesn't return on successes
135         bool tryReturned = true;
136 
137         auto Try = [ReturnSuccess, &tryReturned]() -> ResultOrError<int*> {
138             int* result = nullptr;
139             DAWN_TRY_ASSIGN(result, ReturnSuccess());
140             tryReturned = false;
141 
142             EXPECT_EQ(result, &dummySuccess);
143             return result;
144         };
145 
146         ResultOrError<int*> result = Try();
147         ASSERT_TRUE(result.IsSuccess());
148         ASSERT_FALSE(tryReturned);
149         ASSERT_EQ(result.AcquireSuccess(), &dummySuccess);
150     }
151 
152     // Check DAWN_TRY_ASSIGN handles errors correctly.
TEST(ErrorTests, TRY_RESULT_Error)153     TEST(ErrorTests, TRY_RESULT_Error) {
154         auto ReturnError = []() -> ResultOrError<int*> {
155             return DAWN_VALIDATION_ERROR(dummyErrorMessage);
156         };
157 
158         auto Try = [ReturnError]() -> ResultOrError<int*> {
159             int* result = nullptr;
160             DAWN_TRY_ASSIGN(result, ReturnError());
161             DAWN_UNUSED(result);
162 
163             // DAWN_TRY should return before this point
164             EXPECT_FALSE(true);
165             return &dummySuccess;
166         };
167 
168         ResultOrError<int*> result = Try();
169         ASSERT_TRUE(result.IsError());
170 
171         std::unique_ptr<ErrorData> errorData = result.AcquireError();
172         ASSERT_EQ(errorData->GetMessage(), dummyErrorMessage);
173     }
174 
175     // Check DAWN_TRY_ASSIGN adds to the backtrace.
TEST(ErrorTests, TRY_RESULT_AddsToBacktrace)176     TEST(ErrorTests, TRY_RESULT_AddsToBacktrace) {
177         auto ReturnError = []() -> ResultOrError<int*> {
178             return DAWN_VALIDATION_ERROR(dummyErrorMessage);
179         };
180 
181         auto SingleTry = [ReturnError]() -> ResultOrError<int*> {
182             DAWN_TRY(ReturnError());
183             return &dummySuccess;
184         };
185 
186         auto DoubleTry = [SingleTry]() -> ResultOrError<int*> {
187             DAWN_TRY(SingleTry());
188             return &dummySuccess;
189         };
190 
191         ResultOrError<int*> singleResult = SingleTry();
192         ASSERT_TRUE(singleResult.IsError());
193 
194         ResultOrError<int*> doubleResult = DoubleTry();
195         ASSERT_TRUE(doubleResult.IsError());
196 
197         std::unique_ptr<ErrorData> singleData = singleResult.AcquireError();
198         std::unique_ptr<ErrorData> doubleData = doubleResult.AcquireError();
199 
200         ASSERT_EQ(singleData->GetBacktrace().size() + 1, doubleData->GetBacktrace().size());
201     }
202 
203     // Check a ResultOrError can be DAWN_TRY_ASSIGNED in a function that returns an Error
TEST(ErrorTests, TRY_RESULT_ConversionToError)204     TEST(ErrorTests, TRY_RESULT_ConversionToError) {
205         auto ReturnError = []() -> ResultOrError<int*> {
206             return DAWN_VALIDATION_ERROR(dummyErrorMessage);
207         };
208 
209         auto Try = [ReturnError]() -> MaybeError {
210             int* result = nullptr;
211             DAWN_TRY_ASSIGN(result, ReturnError());
212             DAWN_UNUSED(result);
213 
214             return {};
215         };
216 
217         MaybeError result = Try();
218         ASSERT_TRUE(result.IsError());
219 
220         std::unique_ptr<ErrorData> errorData = result.AcquireError();
221         ASSERT_EQ(errorData->GetMessage(), dummyErrorMessage);
222     }
223 
224     // Check a ResultOrError can be DAWN_TRY_ASSIGNED in a function that returns an Error
225     // Version without Result<E*, T*>
TEST(ErrorTests, TRY_RESULT_ConversionToErrorNonPointer)226     TEST(ErrorTests, TRY_RESULT_ConversionToErrorNonPointer) {
227         auto ReturnError = []() -> ResultOrError<int> {
228             return DAWN_VALIDATION_ERROR(dummyErrorMessage);
229         };
230 
231         auto Try = [ReturnError]() -> MaybeError {
232             int result = 0;
233             DAWN_TRY_ASSIGN(result, ReturnError());
234             DAWN_UNUSED(result);
235 
236             return {};
237         };
238 
239         MaybeError result = Try();
240         ASSERT_TRUE(result.IsError());
241 
242         std::unique_ptr<ErrorData> errorData = result.AcquireError();
243         ASSERT_EQ(errorData->GetMessage(), dummyErrorMessage);
244     }
245 
246     // Check DAWN_TRY_ASSIGN handles successes correctly.
TEST(ErrorTests, TRY_RESULT_CLEANUP_Success)247     TEST(ErrorTests, TRY_RESULT_CLEANUP_Success) {
248         auto ReturnSuccess = []() -> ResultOrError<int*> { return &dummySuccess; };
249 
250         // We need to check that DAWN_TRY_ASSIGN_WITH_CLEANUP doesn't return on successes and the
251         // cleanup is not called.
252         bool tryReturned = true;
253         bool tryCleanup = false;
254 
255         auto Try = [ReturnSuccess, &tryReturned, &tryCleanup]() -> ResultOrError<int*> {
256             int* result = nullptr;
257             DAWN_TRY_ASSIGN_WITH_CLEANUP(result, ReturnSuccess(), { tryCleanup = true; });
258             tryReturned = false;
259 
260             EXPECT_EQ(result, &dummySuccess);
261             return result;
262         };
263 
264         ResultOrError<int*> result = Try();
265         ASSERT_TRUE(result.IsSuccess());
266         ASSERT_FALSE(tryReturned);
267         ASSERT_FALSE(tryCleanup);
268         ASSERT_EQ(result.AcquireSuccess(), &dummySuccess);
269     }
270 
271     // Check DAWN_TRY_ASSIGN handles cleanups.
TEST(ErrorTests, TRY_RESULT_CLEANUP_Cleanup)272     TEST(ErrorTests, TRY_RESULT_CLEANUP_Cleanup) {
273         auto ReturnError = []() -> ResultOrError<int*> {
274             return DAWN_VALIDATION_ERROR(dummyErrorMessage);
275         };
276 
277         // We need to check that DAWN_TRY_ASSIGN_WITH_CLEANUP calls cleanup when error.
278         bool tryCleanup = false;
279 
280         auto Try = [ReturnError, &tryCleanup]() -> ResultOrError<int*> {
281             int* result = nullptr;
282             DAWN_TRY_ASSIGN_WITH_CLEANUP(result, ReturnError(), { tryCleanup = true; });
283             DAWN_UNUSED(result);
284 
285             // DAWN_TRY_ASSIGN_WITH_CLEANUP should return before this point
286             EXPECT_FALSE(true);
287             return &dummySuccess;
288         };
289 
290         ResultOrError<int*> result = Try();
291         ASSERT_TRUE(result.IsError());
292 
293         std::unique_ptr<ErrorData> errorData = result.AcquireError();
294         ASSERT_EQ(errorData->GetMessage(), dummyErrorMessage);
295         ASSERT_TRUE(tryCleanup);
296     }
297 
298     // Check DAWN_TRY_ASSIGN can override return value when needed.
TEST(ErrorTests, TRY_RESULT_CLEANUP_OverrideReturn)299     TEST(ErrorTests, TRY_RESULT_CLEANUP_OverrideReturn) {
300         auto ReturnError = []() -> ResultOrError<int*> {
301             return DAWN_VALIDATION_ERROR(dummyErrorMessage);
302         };
303 
304         auto Try = [ReturnError]() -> bool {
305             int* result = nullptr;
306             DAWN_TRY_ASSIGN_WITH_CLEANUP(result, ReturnError(), {}, true);
307             DAWN_UNUSED(result);
308 
309             // DAWN_TRY_ASSIGN_WITH_CLEANUP should return before this point
310             EXPECT_FALSE(true);
311             return false;
312         };
313 
314         bool result = Try();
315         ASSERT_TRUE(result);
316     }
317 
318     // Check a MaybeError can be DAWN_TRIED in a function that returns an ResultOrError
319     // Check DAWN_TRY handles errors correctly.
TEST(ErrorTests, TRY_ConversionToErrorOrResult)320     TEST(ErrorTests, TRY_ConversionToErrorOrResult) {
321         auto ReturnError = []() -> MaybeError { return DAWN_VALIDATION_ERROR(dummyErrorMessage); };
322 
323         auto Try = [ReturnError]() -> ResultOrError<int*> {
324             DAWN_TRY(ReturnError());
325             return &dummySuccess;
326         };
327 
328         ResultOrError<int*> result = Try();
329         ASSERT_TRUE(result.IsError());
330 
331         std::unique_ptr<ErrorData> errorData = result.AcquireError();
332         ASSERT_EQ(errorData->GetMessage(), dummyErrorMessage);
333     }
334 
335     // Check a MaybeError can be DAWN_TRIED in a function that returns an ResultOrError
336     // Check DAWN_TRY handles errors correctly. Version without Result<E*, T*>
TEST(ErrorTests, TRY_ConversionToErrorOrResultNonPointer)337     TEST(ErrorTests, TRY_ConversionToErrorOrResultNonPointer) {
338         auto ReturnError = []() -> MaybeError { return DAWN_VALIDATION_ERROR(dummyErrorMessage); };
339 
340         auto Try = [ReturnError]() -> ResultOrError<int> {
341             DAWN_TRY(ReturnError());
342             return 42;
343         };
344 
345         ResultOrError<int> result = Try();
346         ASSERT_TRUE(result.IsError());
347 
348         std::unique_ptr<ErrorData> errorData = result.AcquireError();
349         ASSERT_EQ(errorData->GetMessage(), dummyErrorMessage);
350     }
351 
352 }  // anonymous namespace
353