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

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

%allow-injection
%no-child-restrictions

# for negative testing of loadApplyUserModule
%requires ../../../../../qlib/Util.qm
%requires ../../../../../qlib/QUnit.qm

%requires ./MyTestModule.qm
#/;

%exec-class InjectionTest

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();
}
";

class InjectionTest inherits QUnit::Test {
    constructor() : Test("InjectionTest", "1.0") {
        addTestCase("injection test", \injectionTest());
        set_return_value(main());
    }

    injectionTest() {
        {
            # set up the Program container
            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", \p.callFunction(), "get_value");
        }

        {
            # set up the Program container
            Program p(PO_NO_API|PO_NO_CHILD_PO_RESTRICTIONS|PO_NEW_STYLE);

            assertThrows("CLASS-IMPORT-ERROR", \p.importClass(), ("Test::FileLineIteratorFake", "Qore::FileLineIterator", True));
            assertThrows("FUNCTION-IMPORT-ERROR", \p.importFunction(), ("Test::exit", "Qore::exit", True));
            assertThrows("LOAD-MODULE-ERROR", \p.loadApplyToUserModule(), "Util");
        }

        # test re-injection of a module
        {
            # 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"));
        }
    }
}
