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

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

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

%exec-class DataLineIteratorTest

class DataLineIteratorTest inherits QUnit::Test {
    private {
        const DataList = (
            "a2ps-4.13-1332.1.x86_64",
            "a2ps-devel-4.13-1332.1.x86_64",
            "aaa_base-11.3-7.2.x86_64",
            "příliš žluťoučký kůň úpěl ďábelské ódy",
        );

        const BasicData = (
            "abcdef",
            "xyz42",
            "123456"
        );
    }

    constructor() : QUnit::Test("DataLineIterator test", "1.0") {
        addTestCase("Basic tests", \basicTests());
        addTestCase("Reset test", \resetTest());

        addTestCase("LF Explicit tests", sub() {doTestsExplicit("\n");});
        addTestCase("LF Auto tests", sub() {doTestsAuto("\n");});
        addTestCase("LF UTF-16 Explicit tests", sub() {doTestsExplicitEnc("UTF16", "\n");});
        addTestCase("LF UTF-16 Auto tests", sub() {doTestsAutoEnc("UTF16", "\n");});
        addTestCase("LF UTF-16LE Explicit tests", sub() {doTestsExplicitEnc("UTF16LE", "\n");});
        addTestCase("LF UTF-16LE Auto tests", sub() {doTestsAutoEnc("UTF16LE", "\n");});
        addTestCase("LF UTF-16BE Explicit tests", sub() {doTestsExplicitEnc("UTF16BE", "\n");});
        addTestCase("LF UTF-16BE Auto tests", sub() {doTestsAutoEnc("UTF16BE", "\n");});

        addTestCase("CR Explicit tests", sub() {doTestsExplicit("\r");});
        addTestCase("CR Auto tests", sub() {doTestsAuto("\r");});
        addTestCase("CR UTF-16 Explicit tests", sub() {doTestsExplicitEnc("UTF16", "\r");});
        addTestCase("CR UTF-16 Auto tests", sub() {doTestsAutoEnc("UTF16", "\r");});
        addTestCase("CR UTF-16LE Explicit tests", sub() {doTestsExplicitEnc("UTF16LE", "\r");});
        addTestCase("CR UTF-16LE Auto tests", sub() {doTestsAutoEnc("UTF16LE", "\r");});
        addTestCase("CR UTF-16BE Explicit tests", sub() {doTestsExplicitEnc("UTF16BE", "\r");});
        addTestCase("CR UTF-16BE Auto tests", sub() {doTestsAutoEnc("UTF16BE", "\r");});

        addTestCase("CRLF Explicit tests", sub() {doTestsExplicit("\r\n");});
        addTestCase("CRLF Auto tests", sub() {doTestsAuto("\r\n");});
        addTestCase("CRLF UTF-16 Explicit tests", sub() {doTestsExplicitEnc("UTF16", "\r\n");});
        addTestCase("CRLF UTF-16 Auto tests", sub() {doTestsAutoEnc("UTF16", "\r\n");});
        addTestCase("CRLF UTF-16LE Explicit tests", sub() {doTestsExplicitEnc("UTF16LE", "\r\n");});
        addTestCase("CRLF UTF-16LE Auto tests", sub() {doTestsAutoEnc("UTF16LE", "\r\n");});
        addTestCase("CRLF UTF-16BE Explicit tests", sub() {doTestsExplicitEnc("UTF16BE", "\r\n");});
        addTestCase("CRLF UTF-16BE Auto tests", sub() {doTestsAutoEnc("UTF16BE", "\r\n");});

        set_return_value(main());
    }

    basicTests() {
        string WithNLines = foldl $1 + "\n" + $2, BasicData;
        string WithRNLines = foldl $1 + "\r\n" + $2, BasicData;
        string WithRLines = foldl $1 + "\r" + $2, BasicData;
        string weirdEOL = "XY";
        string WithWeirdEOL = foldl $1 + weirdEOL + $2, BasicData;

        AbstractLineIterator a = new DataLineIterator("");
        assertFalse(a.valid());
        assertEq(0, a.index());
        assertThrows('ITERATOR-ERROR', \a.getValue());
        assertFalse(a.next());
        assertFalse(a.valid());
        assertEq(0, a.index());
        assertThrows('ITERATOR-ERROR', \a.getValue());

        a = new DataLineIterator(BasicData[0]);
        assertFalse(a.valid());
        assertEq(0, a.index());
        assertThrows('ITERATOR-ERROR', \a.getValue());
        assertTrue(a.next());
        assertTrue(a.valid());
        assertEq(1, a.index());
        assertEq(BasicData[0], a.getLine());
        assertEq(BasicData[0], a.getValue());
        assertFalse(a.next());
        assertFalse(a.valid());
        assertEq(0, a.index());
        assertThrows('ITERATOR-ERROR', \a.getValue());

        a = new DataLineIterator(WithNLines);
        assertFalse(a.valid());
        assertEq(0, a.index());
        assertThrows('ITERATOR-ERROR', \a.getValue());
        assertTrue(a.next());
        assertTrue(a.valid());
        assertEq(1, a.index());
        assertEq(BasicData[0], a.getLine());
        assertEq(BasicData[0], a.getValue());
        assertTrue(a.next());
        assertTrue(a.valid());
        assertEq(2, a.index());
        assertEq(BasicData[1], a.getLine());
        assertEq(BasicData[1], a.getValue());
        assertTrue(a.next());
        assertTrue(a.valid());
        assertEq(3, a.index());
        assertEq(BasicData[2], a.getLine());
        assertEq(BasicData[2], a.getValue());
        assertFalse(a.next());
        assertFalse(a.valid());
        assertEq(0, a.index());
        assertThrows('ITERATOR-ERROR', \a.getValue());

        a = new DataLineIterator(WithRNLines);
        assertFalse(a.valid());
        assertEq(0, a.index());
        assertThrows('ITERATOR-ERROR', \a.getValue());
        assertTrue(a.next());
        assertTrue(a.valid());
        assertEq(1, a.index());
        assertEq(BasicData[0], a.getLine());
        assertEq(BasicData[0], a.getValue());
        assertTrue(a.next());
        assertTrue(a.valid());
        assertEq(2, a.index());
        assertEq(BasicData[1], a.getLine());
        assertEq(BasicData[1], a.getValue());
        assertTrue(a.next());
        assertTrue(a.valid());
        assertEq(3, a.index());
        assertEq(BasicData[2], a.getLine());
        assertEq(BasicData[2], a.getValue());
        assertFalse(a.next());
        assertFalse(a.valid());
        assertEq(0, a.index());
        assertThrows('ITERATOR-ERROR', \a.getValue());

        a = new DataLineIterator(WithRLines);
        assertFalse(a.valid());
        assertEq(0, a.index());
        assertThrows('ITERATOR-ERROR', \a.getValue());
        assertTrue(a.next());
        assertTrue(a.valid());
        assertEq(1, a.index());
        assertEq(BasicData[0], a.getLine());
        assertEq(BasicData[0], a.getValue());
        assertTrue(a.next());
        assertTrue(a.valid());
        assertEq(2, a.index());
        assertEq(BasicData[1], a.getLine());
        assertEq(BasicData[1], a.getValue());
        assertTrue(a.next());
        assertTrue(a.valid());
        assertEq(3, a.index());
        assertEq(BasicData[2], a.getLine());
        assertEq(BasicData[2], a.getValue());
        assertFalse(a.next());
        assertFalse(a.valid());
        assertEq(0, a.index());
        assertThrows('ITERATOR-ERROR', \a.getValue());

        a = new DataLineIterator(WithWeirdEOL, weirdEOL);
        assertFalse(a.valid());
        assertEq(0, a.index());
        assertThrows('ITERATOR-ERROR', \a.getValue());
        assertTrue(a.next());
        assertTrue(a.valid());
        assertEq(1, a.index());
        assertEq(BasicData[0], a.getLine());
        assertEq(BasicData[0], a.getValue());
        assertTrue(a.next());
        assertTrue(a.valid());
        assertEq(2, a.index());
        assertEq(BasicData[1], a.getLine());
        assertEq(BasicData[1], a.getValue());
        assertTrue(a.next());
        assertTrue(a.valid());
        assertEq(3, a.index());
        assertEq(BasicData[2], a.getLine());
        assertEq(BasicData[2], a.getValue());
        assertFalse(a.next());
        assertFalse(a.valid());
        assertEq(0, a.index());
        assertThrows('ITERATOR-ERROR', \a.getValue());
    }

    resetTest() {
        DataLineIterator a = new DataLineIterator("abc");
        assertTrue(a.next());
        assertFalse(a.next());
        assertTrue(a.next());
        assertFalse(a.next());

        a = new DataLineIterator("abc\ndef\nghi");
        assertTrue(a.next());
        assertEq("abc", a.getLine());
        assertTrue(a.next());
        assertEq("def", a.getLine());
        a.reset();
        assertFalse(a.valid());
        assertEq(0, a.index());
        assertTrue(a.next());
        assertEq("abc", a.getLine());
        assertTrue(a.next());
        assertEq("def", a.getLine());
        assertTrue(a.next());
        assertEq("ghi", a.getLine());
        assertFalse(a.next());
    }

    doTestsIntern(DataLineIterator di) {
        for (int i = 0; i < DataList.size(); i++) {
            assertTrue(di.next(), sprintf("next() #%d", i));
            assertEq(DataList[i].strlen(), di.getValue().strlen(), sprintf("getValue() #%d", i));
        }
        assertFalse(di.next());
    }

    doTestsExplicit(string eol) {
        string data = foldl $1 + eol + $2, DataList;
        DataLineIterator di(data, eol);
        doTestsIntern(di);
    }

    doTestsAuto(string eol) {
        string data = foldl $1 + eol + $2, DataList;
        DataLineIterator di(data);
        doTestsIntern(di);
    }

    doTestsExplicitEnc(string enc, string eol) {
        string data = foldl $1 + eol + $2, DataList;
        string dataEncoded = convert_encoding(data, enc);
        DataLineIterator di(dataEncoded, eol);
        doTestsIntern(di);
    }

    doTestsAutoEnc(string enc, string eol) {
        string data = foldl $1 + eol + $2, DataList;
        string dataEncoded = convert_encoding(data, enc);
        DataLineIterator di(dataEncoded);
        doTestsIntern(di);
    }
}
