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

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

%requires ../../../../qlib/Util.qm
%requires ../../../../qlib/QUnit.qm
%requires ../../../../qlib/FsUtil.qm
%requires ../../../../qlib/DataProvider
%requires ../../../../qlib/FilePoller.qm

%exec-class FilePollerTest

class MyFilePoller inherits FilePoller {
    public {
        list fl = ();
    }

    constructor(string dir) : FilePoller(dir, ".*") {
    }

    singleFileEvent(hash h) {
        fl += h.name;
    }
}

public class FilePollerTest inherits QUnit::Test {
    public {
    }

    private {
        const Files = (
            "file1",
            "file2",
            "file3",
        );
    }

    constructor() : Test("FilePoller", "1.0") {
        addTestCase("FilePoller", \filePollerTest());
        addTestCase("FilePollerDataProvider", \filePollerDataProviderTest());

        # Return for compatibility with test harness that checks return value
        set_return_value(main());
    }

    private filePollerTest() {
        string dir = tmp_location() + DirSep + get_random_string();
        mkdir(dir);
        chdir(dir);
        on_exit
            rmdir(dir);

        File f();
        map f.open2($1, O_CREAT|O_TRUNC|O_WRONLY), Files;
        on_exit map unlink($1), Files;

        MyFilePoller fp(dir);
        fp.runOnce();
        fp.stop();
        assertEq(Files, fp.fl);
    }

    private filePollerDataProviderTest() {
        Logger logger();
        {
            LoggerLevel level;
            if (m_options.verbose > 3) {
                level = LoggerLevel::getLevelDebug();
            } else if (m_options.verbose > 2) {
                level = LoggerLevel::getLevelInfo();
            } else {
                level = LoggerLevel::getLevelError();
            }
            logger = new Logger("test", level);
        }
        logger.addAppender(new StdoutAppender());

        TmpDir dir("FilePollerDataProvider-");

        MyObserver observer();
        FileDelayedPollerDataProvider fp({
            "path": dir.path,
            "poll_interval": 1,
        });
        fp.setLogger(logger);
        fp.registerObserver(observer);

        File f();
        map f.open2(dir.path + "/" + $1, O_CREAT|O_TRUNC|O_WRONLY), Files;
        on_exit map unlink($1), dir.path + "/" + Files;

        fp.observersReady();

        observer.wait();
        assertEq("file1", observer.h.file1.name);
        assertEq("file2", observer.h.file2.name);
        assertEq("file3", observer.h.file3.name);
        assertTrue(True);
    }
}

class MyObserver inherits Observer {
    public {
        hash<string, hash<auto>> h();
    }

    private {
        Mutex m();
        Condition cond();
    }

    update(string event_id, hash<auto> data_) {
        h{data_.name} = data_;
        if (h.size() == 3) {
            m.lock();
            on_exit m.unlock();

            cond.broadcast();
        }
    }

    wait() {
        date start = now_us();

        m.lock();
        on_exit m.unlock();

        while (h.size() != 3) {
            cond.wait(m, 1s);
            if ((now_us() - start) > 5s) {
                throw "ERROR", "no files in timeout";
            }
        }
    }
}
