#!/usr/bin/env qore
# -*- mode: qore; indent-tabs-mode: nil -*-

%new-style
%enable-all-warnings
%require-types
%strict-args
%no-child-restrictions

%requires ../../../../qlib/QUnit.qm

%exec-class Test

class BGTest {
    int add(int arg) {
        return arg;
    }
}

class Test inherits QUnit::Test {
    public {
        BGTest test();

        const Code = "
%exec-class T

class T {
    constructor() { background $.method(); }
    method() {}
}
";

        const ThreadName = "my-thread";
    }

    constructor() : QUnit::Test("background", "1.0", \ARGV) {
        addTestCase("background operator tests", \basicTests());
        addTestCase("issue 1467", \issue1467());
        addTestCase("issue 2637", \issue2637());
        addTestCase("issue 2679", \issue2679());
        addTestCase("issue 2653", \issue2653());
        addTestCase("issue 2701", \issue2701());
        addTestCase("issue 2444", \issue2444());
        set_return_value(main());
    }

    issue2653() {
        background test.add(1);
    }

    issue1467() {
        Program p();
        p.parse(Code, "issue 1467");
        assertEq(NOTHING, p.run());
    }

    issue2637() {
        assertEq(1, issue2637_1(1));
    }

    private int issue2637_1(int a1) {
        Counter c(1);
        background issue2637_2(c, ("a": a1));
        c.waitForZero();
        return a1;
    }

    private auto issue2637_2(Counter c, hash<auto> h) {
        c.dec();
    }

    issue2679() {
        # causes a crash with #2279
        Program p(PO_NEW_STYLE);
        p.parse("background (sprintf(\"A\"), sprintf(\"B\"));", "", WARN_RETURN_VALUE_IGNORED);
        assertTrue(True);
    }

    basicTests() {
        Counter cnt();
        int i = 0;

        code c = sub () {
            delete i;
            cnt.dec();
        };

        cnt.inc();
        testNullAssertion("delete local var in background", c);
        cnt.waitForZero();

        c = sub () {
            remove i;
            cnt.dec();
        };

        cnt.inc();
        testNullAssertion("remove local var in background", c);
        cnt.waitForZero();

        # do negative tests
        c = sub () {
            Program p(PO_NEW_STYLE);
            p.parse("int i;background (i *= 10); #1
            background (i /= 10); #2
            background (i -= 10); #3
            background (i += 10); #4
            background (i %= 10); #5
            background (i >>= 10); #6
            background (i <<= 10); #7
            background ++i; #8
            background i++; #9
            background --i; #10
            background i--; #11
            string str;
            background splice str, 0; #12
            background extract str, 0; #13", "bg");
        };

        *hash<auto> ex = testAssertion("background negative test", c, NOTHING, new TestResultExceptionType("PARSE-EXCEPTION"));

        # count exceptions
        while (ex) {
            ++i;
            ex = ex.next;
        }
        testAssertionValue("background negative count", i, 13);

        cnt.inc();
        testAssertion("create closure with bound local var in background exp", sub () {Test::f1(cnt);}, NOTHING, new TestResultValue());
        cnt.waitForZero();

        cnt.inc();
        testAssertion("create complex closure in background exp", sub () {Test::f2(cnt);}, NOTHING, new TestResultValue());
        cnt.waitForZero();
    }

    issue2701() {
        if (!HAVE_MANAGE_STACK) {
            testSkip("cannot manage the stack");
        }

        int size = get_default_thread_stack_size();
        #printf("size: %d\n", size);
        assertGt(0, size);
        int orig_size = size;
        size = set_default_thread_stack_size(size * 2);
        assertEq(orig_size * 2, size);
        if (HAVE_GET_STACK_SIZE) {
            Queue q();
            background checkStackSize(q);
            # issue #2701: the actual stack size assigned may be larger than requested
            assertGe(size, q.get());
        }
    }

    issue2444() {
        if (!HAVE_THREAD_NAME) {
            testSkip("no thread names available");
        }
        assertEq("qore/" + gettid(), get_thread_name());
        set_thread_name(ThreadName);
        assertEq(ThreadName, get_thread_name());
    }

    private static checkStackSize(Queue q) {
        q.push(get_stack_size());
    }

    static any f1(Counter cnt) {
        int a = 8;
        background sub () {++a; cnt.dec();}();
    }

    static any f2(Counter cnt) {
        int a = 8;
        background sub () {++a; sub() {++a; cnt.dec();}();}();
    }
}
