#include "lib/test.h"

TEST_PREFIX("work: ", "[work]");

TEST("main") {
    AsyncTest test(10000, {"w", "aw"});
    WorkSP w = new Work(test.loop);
    auto main_id = std::this_thread::get_id();
    auto work_id = std::this_thread::get_id();
    w->work_cb = [&](Work*) {
        work_id = std::this_thread::get_id();
        test.happens("w");
    };
    w->after_work_cb = [&](auto, auto& err) {
        CHECK(!err);
        CHECK(std::this_thread::get_id() == main_id);
        test.happens("aw");
    };
    CHECK_FALSE(w->active());
    w->queue();
    CHECK(w->active());
    test.run();
    CHECK_FALSE(w->active());
    CHECK(work_id != main_id);
}

TEST("cancel") {
    AsyncTest test(10000);
    WorkSP w = new Work(test.loop);
    w->work_cb       = [&](Work*) { FAIL(); };
    w->after_work_cb = [&](auto, auto) { FAIL(); };
    w->cancel();
    test.run(); // noop
    // can't test active work because it starts executing immediately if there are free workers and thus
    // there is probability that it can't be canceled
}

TEST("factory") {
    AsyncTest test(10000, 2);
    auto r = Work::create(
        [&](Work*) { test.happens(); },
        [&](const WorkSP&, const std::error_code&) { test.happens(); },
        test.loop
    );
    CHECK(r);
    r = nullptr; // request must be alive while not completed
    test.run();
}

TEST("event listener") {
    AsyncTest test(10000);
    WorkSP w = new Work(test.loop);
    auto s = [&](auto lst) {
        w->event_listener(&lst);

        w->queue();
        test.run();

        CHECK(lst.cnt == 11);
    };
    SECTION("std") {
        struct Lst : IWorkListener {
            int cnt = 0;
            void on_work       (Work*)                                 override { ++cnt; }
            void on_after_work (const WorkSP&, const std::error_code&) override { cnt += 10; }
        };
        s(Lst());
    }
    SECTION("self") {
        struct Lst : IWorkSelfListener {
            int cnt = 0;
            void on_work       ()                       override { ++cnt; }
            void on_after_work (const std::error_code&) override { cnt += 10; }
        };
        s(Lst());
    }
}

TEST("double queue") {
    AsyncTest test(10000, {"w", "aw", "w", "aw"});
    WorkSP w = new Work(test.loop);
    w->work_cb = [&](Work*) {
        test.happens("w");
    };
    w->after_work_cb = [&](auto, auto&) {
        test.happens("aw");
    };
    w->queue();
    CHECK_THROWS(w->queue());
    test.run();

    // after completing, queue() works again
    w->queue();
    CHECK_THROWS(w->queue());
    test.run();
}