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

%require-types
%enable-all-warnings
%new-style

%allow-injection
%no-child-restrictions

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

# for negative testing of loadApplyUserModule
%requires Util

%requires ./MyTestModule.qm

public namespace Test {
    class FileLineIteratorFake inherits AbstractIterator {
        private {
            bool valid = False;
        }

        constructor(string file_unused, *string h, *string g) {
        }

        bool valid() {
            return valid;
        }

        bool next() {
            return valid = !valid;
        }

        string getValue() {
            if (!valid)
                throw "ITERATOR-ERROR", "iterator not valid";
            return "test";
        }
    }

    sub exit(softint rc = 0) {
        printf("oops\n");
    }

    public class ReinjectedTagClass inherits MyTest::TagClass {
        constructor(string tag) : TagClass(tag) {
        }

        string getTag() {
            return TagClass::getTag() + "-injected";
        }
    }
}

const Code1 = "FileLineIterator li('test-file.csv');
bool sub next() {
    return li.next();
}
string sub get_value() {
    return li.getValue();
}
";

const Code2 = "%requires MyTestModule
TagClass t('tag');
string sub get_tag() {
    return t.getTag();
}
";


%exec-class InjectionTest

class InjectionTest inherits QUnit::DependencyInjectedTest {
    constructor() : QUnit::DependencyInjectedTest("Injection test", "1.0") {
        addTestCase("Test injection", \testInjection());
        addTestCase("Test errors", \testErrors());
        addTestCase("Test reinjection", \testReinjection());

        # Mandatory for injected tests
        setScriptPath(get_script_path());

        set_return_value(main());
    }

    nothing performModuleInjections() {
    }

    nothing performInjections(Program p) {
    }

    testInjection() {
        Program p(PO_NO_API|PO_NO_CHILD_PO_RESTRICTIONS|PO_ALLOW_INJECTION|PO_NEW_STYLE);
        p.importClass("Test::FileLineIteratorFake", "Qore::FileLineIterator", True);
        p.importSystemApi();
        p.lockOptions();

        p.parse(Code1, "test-1");
        p.run();
        assertTrue(p.callFunction("next"), "injection-1");
        assertEq("test", p.callFunction("get_value"), "injection-2");
        assertFalse(p.callFunction("next"), "injection-3");

        assertThrows('ITERATOR-ERROR', sub() {p.callFunction("get_value");});
    }

    testErrors() {
        Program p(PO_NO_API|PO_NO_CHILD_PO_RESTRICTIONS|PO_NEW_STYLE);

        assertThrows('CLASS-IMPORT-ERROR', "cannot import class", sub() {p.importClass("Test::FileLineIteratorFake", "Qore::FileLineIterator", True);});
        assertThrows('FUNCTION-IMPORT-ERROR', "cannot import function", sub() {p.importFunction("Test::exit", "Qore::exit", True);});
        assertThrows('LOAD-MODULE-ERROR', "cannot load module.*for injection", sub() {p.loadApplyToUserModule("Util");});
    }

    # test re-injection of a module
    testReinjection() {
        {
            # set up the Program container for reinjecting MyTestModule
            Program p(PO_NO_USER_API|PO_NO_CHILD_PO_RESTRICTIONS|PO_ALLOW_INJECTION|PO_NEW_STYLE);
            p.setScriptPath(get_script_path());
            p.importClass("Test::ReinjectedTagClass", "MyTest::TagClass", True);
            p.lockOptions();
            p.loadApplyToUserModule("./MyTestModule.qm", True);
        }

        {
            Program p(PO_NO_USER_API|PO_NEW_STYLE);
            p.parse(Code2, "test-2");
            p.run();
            assertEq("tag-injected", p.callFunction("get_tag"), "injection-5");
        }
    }
}

