/* * Copyright (c) 2024, Dan Klishch * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #ifndef AK_COROUTINE_STATEMENT_EXPRS_BROKEN # define CO_TRY_OR_FAIL(expression) \ ({ \ /* Ignore -Wshadow to allow nesting the macro. */ \ AK_IGNORE_DIAGNOSTIC("-Wshadow", \ auto _temporary_result = (expression)); \ static_assert(!::AK::Detail::IsLvalueReference, \ "Do not return a reference from a fallible expression"); \ if (_temporary_result.is_error()) [[unlikely]] { \ FAIL(_temporary_result.release_error()); \ co_return; \ } \ _temporary_result.release_value(); \ }) #elifndef AK_COROUTINE_DESTRUCTION_BROKEN namespace AK::Detail::Test { // FIXME: Straight-forward way to implement CO_TRY_OR_FAIL we use for Clang causes GCC to ICE. template struct TryOrFailAwaiter { TryOrFailAwaiter(T& expression) : m_expression(&expression) { } TryOrFailAwaiter(T&& expression) : m_expression(&expression) { } TryOrFailAwaiter(Coroutine&&) { static_assert(DependentFalse, "Despite the CO_ prefix in CO_TRY_OR_FAIL, you should co_await the argument of it."); } bool await_ready() { return false; } template requires IsSpecializationOf std::coroutine_handle<> await_suspend(std::coroutine_handle handle) { if (!m_expression->is_error()) { return handle; } else { auto awaiter = handle.promise().m_awaiter; auto* coroutine = handle.promise().m_coroutine; using ReturnType = RemoveReference::ReturnType; static_assert(SameAs, "CO_TRY_OR_FAIL can only be used inside ASYNC_TEST_CASE functions"); // Fail the current test. FAIL(m_expression->release_error()); // Forcefully stop coroutine. coroutine->m_handle = {}; // Run destructors for locals in the coroutine that failed. handle.destroy(); // Lastly, transfer control to the parent (or nothing, if parent is not yet suspended). if (awaiter) return awaiter; return std::noop_coroutine(); } } decltype(auto) await_resume() { return m_expression->release_value(); } T* m_expression { nullptr }; }; } # define CO_TRY_OR_FAIL(expression) (co_await ::AK::Detail::Test::TryOrFailAwaiter { (expression) }) #else # error Unable to work around compiler bugs in definiton of CO_TRY_OR_FAIL. #endif namespace Test { #define __ASYNC_TESTCASE_FUNC(x) __async_test_##x #define ASYNC_TEST_CASE(x) \ static Coroutine __ASYNC_TESTCASE_FUNC(x)(); \ \ static void __TESTCASE_FUNC(x)() \ { \ Core::run_async_in_new_event_loop(__ASYNC_TESTCASE_FUNC(x)); \ } \ \ struct __TESTCASE_TYPE(x) { \ __TESTCASE_TYPE(x) \ () \ { \ add_test_case_to_suite(adopt_ref(*new ::Test::TestCase(#x, __TESTCASE_FUNC(x), false))); \ } \ }; \ static struct __TESTCASE_TYPE(x) __TESTCASE_TYPE(x); \ static Coroutine __ASYNC_TESTCASE_FUNC(x)() }