#include "Test/Test.h"
#include "Async/Async.h"

namespace clice::testing {

namespace {

suite<"Async"> suite = [] {
    test("Lock") = [] {
        async::Lock lock;

        int x = 0;

        auto task1 = [&]() -> async::Task<> {
            auto guard = co_await lock.try_lock();
            co_await async::sleep(5);
            expect(that % x == 0);
            co_await async::sleep(10);
            expect(that % x == 0);
            co_await async::sleep(5);
            x = 1;
        };

        auto task2 = [&]() -> async::Task<> {
            auto guard = co_await lock.try_lock();
            co_await async::sleep(5);
            expect(that % x == 1);
            co_await async::sleep(5);
            expect(that % x == 1);
            co_await async::sleep(10);
            x = 2;
        };

        auto task3 = [&]() -> async::Task<> {
            auto guard = co_await lock.try_lock();
            co_await async::sleep(10);
            expect(that % x == 2);
            co_await async::sleep(5);
            expect(that % x == 2);
            co_await async::sleep(5);
        };

        async::run(task1(), task2(), task3());
    };

    test("LockCancel") = [] {
        async::Lock lock;

        int x = 0;
        int y = 0;

        auto task = [&]() -> async::Task<> {
            x += 1;
            auto guard = co_await lock.try_lock();
            co_await async::sleep(100);
            y += 1;
        };

        auto task1 = task();
        auto task2 = task();
        auto task3 = task();

        auto cancel = [&task2]() -> async::Task<> {
            co_await async::sleep(10);
            task2.cancel();
            task2.dispose();
        };

        task1.schedule();
        task2.schedule();
        task3.schedule();

        async::run(cancel());

        expect(that % x == 3);
        expect(that % y == 2);
    };
};

}  // namespace

}  // namespace clice::testing
