#!/usr/bin/env qore

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

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

%exec-class JoinPathsTest

public class JoinPathsTest inherits QUnit::Test {

    private {
        list results_generic;
        list results_posix;
        list results_win;
    }

    globalSetUp() {
        # tests that should work on both platforms
        results_generic = (
            ('', '', ''),
            ('/path/to', '', '/path/to'),
            ('path/to', '', 'path/to'),
            ('/path/to/', '', '/path/to/'),
            ('path/to/', '', 'path/to/'),
            ('/path/to/', '..', '/path/to/..'),
            ('/path/to/', '.', '/path/to/.'),
            ('', 'file.txt', 'file.txt'),
            ('/', 'file.txt', '/file.txt'),
            ('/', '', '/'),
            ('', '/', '/'),
            ('', '/dir', '/dir'),
            ('/path/to/file.txt', '/dir', '/dir'),
            ('path/to/file.txt', '/dir', '/dir'),
            ('file.txt', '/dir', '/dir'),
            ('/', '/dir', '/dir'),
            ('/path/to', '/dir', '/dir'),
            ('/path/to/', '/dir', '/dir'),
            ('/dir', '/dir', '/dir'),
            ('some/path', (), 'some/path'),
        );

        # POSIX tests
        results_posix = results_generic + (
            ('/path/to', 'file.txt', '/path/to/file.txt'),
            ('/path/to', '..', '/path/to/..'),
            ('/path/to', '.', '/path/to/.'),
            ('dir', ('a', 'b'), 'dir/a/b'),
            ('/path/to', ('dir'), '/path/to/dir'),
            ('/path/to/', ('dir'), '/path/to/dir'),
            ('/path/to/', ('dir/'), '/path/to/dir/'),
            ('/path/to', ('/dir'), '/dir'),
            ('/path/to', ('a', 'b', 'c'), '/path/to/a/b/c'),
            ('/path/to', ('a', 'b', '/c'), '/c'),
            ('/path/to', ('a', 'b', '/c', 'd'), '/c/d'),
            ('path/to', ('a', 'b', '/c', 'd'), '/c/d'),
            ('/some/path', (), '/some/path'),
        );

        # Windows tests
        results_win = results_generic + (
            ('C:\', 'file.txt', 'C:\file.txt'),
            ('C:\', 'C:file.txt', 'C:\file.txt'),
            ('C:\', 'c:file.txt', 'C:\file.txt'),
            ('C:', 'file.txt', 'C:file.txt'),
            ('C:', 'C:file.txt', 'C:file.txt'),
            ('C:', 'c:file.txt', 'C:file.txt'),
            ('C:\', 'D:\file.txt', 'D:\file.txt'),
            ('C:', 'D:\file.txt', 'D:\file.txt'),
            ('C:\', 'D:\file.txt', 'D:\file.txt'),
            ('C:\', 'D:\file.txt', 'D:\file.txt'),
            ('d:/path/dir/', 'd:efd', 'd:/path/dir/efd'),
            ('c:/path/dir/', 'd:efd', 'd:efd'),
            ('path/to', 'file.txt', 'path/to\file.txt'),
            ('path/to', '..', 'path/to\..'),
            ('path/to', '.', 'path/to\.'),
            ('A:\path/to', 'file.txt', 'A:\path/to\file.txt'),
            ('B:\path/to', '..', 'B:\path/to\..'),
            ('C:\path/to', '.', 'C:\path/to\.'),
            ('dir', ('a', 'b'), 'dir\a\b'),
            ('C:/path/to', ('dir'), 'C:/path/to\dir'),
            ('d:/path/to/', ('dir'), 'd:/path/to/dir'),
            ('E:/path/to/', ('dir/'), 'E:/path/to/dir/'),
            ('e:/path/to', ('f:/dir'), 'f:/dir'),
            ('g:/path/to', ('a', 'b', 'c'), 'g:/path/to\a\b\c'),
            ('h:/path/to', ('a', 'b', 'k:/c'), 'k:/c'),
            ('i:/path/to', ('a', 'b', 'd:/c', 'd'), 'd:/c\d'),
            ('j:/some/path', (), 'j:/some/path'),
            ('path/to', ('a', 'b', 'Z:/c', 'd'), 'Z:/c\d'),
            ('another/path', (), 'another/path'),
            ('C:\path\to', ('dir'), 'C:\path\to\dir'),
            ('d:\path\to\', ('dir'), 'd:\path\to\dir'),
            ('E:\path\to\', ('dir\'), 'E:\path\to\dir\'),
            ('e:\path\to', ('f:\dir'), 'f:\dir'),
            ('g:\path\to', ('a', 'b', 'c'), 'g:\path\to\a\b\c'),
            ('h:\path\to', ('a', 'b', 'k:\c'), 'k:\c'),
            ('i:\path\to', ('a', 'b', 'd:\c', 'd'), 'd:\c\d'),
            ('j:\some\path', (), 'j:\some\path'),
            ('path\to', ('a', 'b', 'Z:\c', 'd'), 'Z:\c\d'),
            ('another\path', (), 'another\path'),
        );
    }


    #! Returns input parameters as a list (in a way the list variant of join_paths expects)
    list<string> res_to_list_input_params(list<auto> res) {
        if (res[1].type() == "list") {
            if (res[1].size() > 0) {
                # insert res[0] at the beginning of the list
                return res[0] + res[1];
            } else {
                # res[1] is an empty list -> return just res[0]
                return (res[0],);
            }
        } else {
            # res[0] and res[1] are both strings
            return (res[0], res[1]);
        }
    }

    constructor() : Test ("JoinPathsTest", "1.0") {
        addTestCase("Join paths test", \test_join_paths());
        addTestCase("Join paths test Posix", \test_join_paths_posix());
        addTestCase("Join paths test Windows", \test_join_paths_win());
        addTestCase("Join paths list test", \test_join_paths_list());
        addTestCase("Join paths list test Posix", \test_join_paths_posix_list());
        addTestCase("Join paths list test Windows", \test_join_paths_win_list());
        addTestCase("Join paths exception", \test_join_paths_exception());
        set_return_value(main());
    }

    #! Tests the string+softlist variant of join_paths with the local path handler
    test_join_paths() {
        list results;
        if (Platform.Windows) {
            results = results_win;
        } else {
            results = results_posix;
        }

        foreach list<auto> res in (results) {
            on_error printf("res: %y\n", res);
            assertEq(res[2], join_paths(res[0], res[1]), sprintf("join_paths('%s', '%s')", res[0], res[1]));
        }

        if (Platform.Windows) {
            assertEq("a\\b\\c", join_paths("a", "b", "c"));
        } else {
            assertEq("a/b/c", join_paths("a", "b", "c"));
        }
    }

    #! Tests the string+softlist variant of join_paths with posix path handler
    test_join_paths_posix() {
        PosixPathHandler ph = new PosixPathHandler();
        foreach list<auto> res in (results_posix) {
            on_error printf("res: %y\n", res);
            assertEq(res[2], ph.joinPaths(cast<list<string>>(() + res[0] + res[1])),
                sprintf("POSIX join_paths('%s', '%s')", res[0], res[1]));
        }

        assertEq("a/b/c", ph.joinPaths(("a", "b", "c")));
    }

    #! Tests the string+softlist variant of join_paths with windows path handler
    test_join_paths_win() {
        WinPathHandler ph = new WinPathHandler();
        foreach list<auto> res in (results_win) {
            on_error printf("res: %y\n", res);
            assertEq(res[2], ph.joinPaths(cast<list<string>>(() + res[0] + res[1])),
                sprintf("WIN join_paths('%s', '%s')", res[0], res[1]));
        }

        assertEq("a\\b\\c", ph.joinPaths(("a", "b", "c")));
    }

    #! Tests the list variant of join_paths with the local path handler
    test_join_paths_list() {
        list results;
        if (Platform.Windows)
            results = results_win;
        else
            results = results_posix;

        foreach list res in (results) {
            list<string> paths = res_to_list_input_params(res);
            assertEq(res[2], join_paths(paths), sprintf("join_paths: %n", paths));
        }
    }

    #! Tests the list variant of join_paths with posix path handler
    test_join_paths_posix_list() {
        PosixPathHandler ph = new PosixPathHandler();
        foreach list res in (results_posix) {
            list<string> paths = res_to_list_input_params(res);
            assertEq(res[2], ph.joinPaths(paths), sprintf("POSIX join_paths: %n", paths));
        }
    }

    #! Tests the list variant of join_paths with windows path handler
    test_join_paths_win_list() {
        WinPathHandler ph = new WinPathHandler();
        foreach list res in (results_win) {
            list<string> paths = res_to_list_input_params(res);
            assertEq(res[2], ph.joinPaths(paths), sprintf("WIN join_paths: %n", paths));
        }
    }

    #! Tests cases when an exception is expected
    test_join_paths_exception() {
        list<string> empty_list = new list<string>();
        assertThrows("MISSING-PARAMETER-ERROR", \join_paths(), (empty_list,));
        assertThrows("RUNTIME-TYPE-ERROR", \join_paths(), (('some/path', (NOTHING,))));
        assertThrows("RUNTIME-TYPE-ERROR", \join_paths(), (('some', ('path', NOTHING,))));
        assertThrows("RUNTIME-TYPE-ERROR", \join_paths(), (('some', ('path', NOTHING, 'dir'))));
        assertThrows("RUNTIME-TYPE-ERROR", \join_paths(), (('some/path', (42,))));
        assertThrows("RUNTIME-TYPE-ERROR", \join_paths(), (('some', ('path', 3.14159,))));
        assertThrows("RUNTIME-TYPE-ERROR", \join_paths(), (('some', ('path', {"unexpected": "hash"}, 'dir'))));
    }
}
