/*
 * steghide 0.5.1 - a steganography program
 * Copyright (C) 1999-2003 Stefan Hetzl <shetzl@chello.at>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 */

#include "common.h"

#include "BitStringTest.h"
#include "TestCategoryCaller.h"

BitStringTest::BitStringTest(TestSuite *s) : UnitTest("BitString", s) {
    ADDTESTCATEGORY(BitStringTest, testBitInputOutput);
    ADDTESTCATEGORY(BitStringTest, testLength);
    ADDTESTCATEGORY(BitStringTest, testDatatypeInput);
    ADDTESTCATEGORY(BitStringTest, testDatatypeOutput);
    ADDTESTCATEGORY(BitStringTest, testEquality);
    ADDTESTCATEGORY(BitStringTest, testCutting);
    ADDTESTCATEGORY(BitStringTest, testCompression);
    ADDTESTCATEGORY(BitStringTest, testArity);
}

void BitStringTest::setup() {
    bs_0 = new BitString();
    bs_0->append(false);

    bs_1 = new BitString();
    bs_1->append(true);

    bs_10 = new BitString();
    bs_10->append(true);
    bs_10->append(false);

    bs_001 = new BitString();
    bs_001->append(false);
    bs_001->append(false);
    bs_001->append(true);

    bs_100 = new BitString();
    bs_100->append(true);
    bs_100->append(false);
    bs_100->append(false);

    bs_1010 = new BitString();
    bs_1010->append(true);
    bs_1010->append(false);
    bs_1010->append(true);
    bs_1010->append(false);

    bs_1110 = new BitString();
    bs_1110->append(true);
    bs_1110->append(true);
    bs_1110->append(true);
    bs_1110->append(false);

    bs_01011 = new BitString();
    bs_01011->append(false);
    bs_01011->append(true);
    bs_01011->append(false);
    bs_01011->append(true);
    bs_01011->append(true);

    bs_10010 = new BitString();
    bs_10010->append(true);
    bs_10010->append(false);
    bs_10010->append(false);
    bs_10010->append(true);
    bs_10010->append(false);

    bs_10101110 = new BitString();
    bs_10101110->append(true);
    bs_10101110->append(false);
    bs_10101110->append(true);
    bs_10101110->append(false);
    bs_10101110->append(true);
    bs_10101110->append(true);
    bs_10101110->append(true);
    bs_10101110->append(false);

    bs_101011101 = new BitString();
    bs_101011101->append(true);
    bs_101011101->append(false);
    bs_101011101->append(true);
    bs_101011101->append(false);
    bs_101011101->append(true);
    bs_101011101->append(true);
    bs_101011101->append(true);
    bs_101011101->append(false);
    bs_101011101->append(true);
}

void BitStringTest::cleanup() {
    delete bs_0;
    delete bs_1;
    delete bs_10;
    delete bs_001;
    delete bs_100;
    delete bs_1010;
    delete bs_1110;
    delete bs_01011;
    delete bs_10010;
    delete bs_10101110;
    delete bs_101011101;
}

void BitStringTest::testBitInputOutput() {
    addTestResult((*bs_0)[0] == false);
    addTestResult((*bs_1)[0] == true);
    addTestResult((*bs_10010)[0] == true && (*bs_10010)[1] == false && (*bs_10010)[2] == false &&
                  (*bs_10010)[3] == true && (*bs_10010)[4] == false);
    addTestResult((*bs_10101110)[0] == true && (*bs_10101110)[1] == false &&
                  (*bs_10101110)[2] == true && (*bs_10101110)[3] == false &&
                  (*bs_10101110)[4] == true && (*bs_10101110)[5] == true &&
                  (*bs_10101110)[6] == true && (*bs_10101110)[7] == false);
    addTestResult(
        (*bs_101011101)[0] == true && (*bs_101011101)[1] == false && (*bs_101011101)[2] == true &&
        (*bs_101011101)[3] == false && (*bs_101011101)[4] == true && (*bs_101011101)[5] == true &&
        (*bs_101011101)[6] == true && (*bs_101011101)[7] == false && (*bs_101011101)[8] == true);
    {
        BitString bs1 = *bs_10010;
        BitString bs2 = *bs_01011;
        bs1.setBit(0, 0);
        bs1.setBit(1, 1);
        bs2.setBit(4, 0);
        addTestResult(bs1 == bs2);
    }
}

void BitStringTest::testLength() {
    addTestResult(bs_0->getLength() == 1);
    addTestResult(bs_1->getLength() == 1);
    addTestResult(bs_10010->getLength() == 5);
    addTestResult(bs_10101110->getLength() == 8);
    addTestResult(bs_101011101->getLength() == 9);
}

void BitStringTest::testDatatypeInput() {
    {
        UWORD16 v = 0xFF81;
        BitString bs;
        bs.append(v, 10);
        addTestResult(bs.getLength() == 10 && bs.Data[0] == 0x81 && bs.Data[1] == 0x03);
    }

    {
        std::string str("test");
        BitString bs(str);
        addTestResult(bs.getLength() == 32 && bs.Data[0] == 't' && bs.Data[1] == 'e' &&
                      bs.Data[2] == 's' && bs.Data[3] == 't');
    }

    {
        std::vector<BYTE> vec;
        vec.push_back(123);
        vec.push_back(45);
        vec.push_back(6);
        BitString bs(vec);
        addTestResult(bs.getLength() == 24 && bs.Data[0] == 123 && bs.Data[1] == 45 &&
                      bs.Data[2] == 6);
    }

    {
        BitString bs1;
        bs1.append(true).append(false).append(true).append(false);
        BitString bs2;
        bs2.append(false).append(true).append(true).append(false).append(bs1);
        addTestResult(bs2.getLength() == 8 && bs2.Data[0] == 0x56);
    }

    {
        BitString bs1;
        bs1.append(false).append(true).append(true).append(true).append(true).append(true);
        bs1.cutBits(1, 5);
        BitString bs2;
        bs2.append(true)
            .append(false)
            .append(true)
            .append(true)
            .append(false)
            .append(true)
            .append(false)
            .append(true);
        bs2.append(true)
            .append(false)
            .append(false)
            .append(false)
            .append(false)
            .append(false)
            .append(false)
            .append(false);
        bs2.append(false)
            .append(false)
            .append(false)
            .append(false)
            .append(false)
            .append(false)
            .append(false)
            .append(false);
        bs2.append(false)
            .append(false)
            .append(false)
            .append(false)
            .append(false)
            .append(false)
            .append(false)
            .append(false);
        bs1.append(bs2);

        BitString bsref;
        bsref.append(false)
            .append(true)
            .append(false)
            .append(true)
            .append(true)
            .append(false)
            .append(true)
            .append(false);
        bsref.append(true)
            .append(true)
            .append(false)
            .append(false)
            .append(false)
            .append(false)
            .append(false)
            .append(false);
        bsref.append(false)
            .append(false)
            .append(false)
            .append(false)
            .append(false)
            .append(false)
            .append(false)
            .append(false);
        bsref.append(false)
            .append(false)
            .append(false)
            .append(false)
            .append(false)
            .append(false)
            .append(false)
            .append(false);
        bsref.append(false);

        if (bs1 != bsref) {
            std::cerr << "bs1:" << std::endl;
            bs1.print(1);
            std::cerr << "bsref:" << std::endl;
            bsref.print(1);
        }
        addTestResult(bs1 == bsref);
    }
}

void BitStringTest::testDatatypeOutput() {
    addTestResult(bs_10010->getValue(1, 2) == 0);

    {
        std::vector<BYTE> vec;
        vec.push_back(0x88);
        vec.push_back(0x07);
        BitString bs(vec);
        addTestResult(bs.getValue(4, 8) == 0x78);
    }

    {
        BitString bs = bs_10101110->getBits(2, 4);
        addTestResult(bs.getLength() == 4 && bs[0] == true && bs[1] == false && bs[2] == true &&
                      bs[3] == true);
    }

    {
        std::vector<BYTE> vec = bs_10101110->getBytes();
        addTestResult(vec.size() == 1 && vec[0] == 0x75);
    }
}

void BitStringTest::testEquality() {
    addTestResult(*bs_0 == *bs_0);
    addTestResult(!(*bs_0 == *bs_1));

    {
        BitString bs(std::string("a test"));
        addTestResult(bs == bs);
    }

    {
        BitString bs1;
        bs1.append((BYTE)0xAB);
        BitString bs2;
        bs2.append(true).append(true).append(false).append(true);
        bs2.append(false).append(true).append(false).append(true);
        addTestResult(bs1 == bs2);
    }
}

void BitStringTest::testCutting() {
    {
        BitString bs1 = *bs_10010;
        BitString bs2 = *bs_100;
        addTestResult(bs1.truncate(0, 3) == bs2);
    }

    {
        BitString bs1 = *bs_10101110;
        BitString bs2;
        bs2.append(true).append(false).append(true).append(true);
        addTestResult(bs1.truncate(2, 6) == bs2);
    }

    {
        BitString bs1 = *bs_10010;
        BitString bs2 = *bs_001;
        BitString bs3 = *bs_10;

        BitString middle = bs1.cutBits(1, 3);
        addTestResult(middle == bs2 && bs1 == bs3);
    }

    {
        BitString bs1 = *bs_10101110;
        BitString bs2 = *bs_1110;
        BitString bs3 = *bs_1010;

        BitString cutted = bs1.cutBits(4, 4);
        addTestResult(cutted == bs2 && bs1 == bs3);
    }

    {
        BitString bs1;
        bs1.append(false);
        BitString bs2;
        bs2.append(true)
            .append(false)
            .append(true)
            .append(true)
            .append(false)
            .append(true)
            .append(false)
            .append(true);
        bs2.append(true)
            .append(false)
            .append(false)
            .append(false)
            .append(false)
            .append(false)
            .append(false)
            .append(false);
        bs2.append(false)
            .append(false)
            .append(false)
            .append(false)
            .append(false)
            .append(false)
            .append(false)
            .append(false);
        bs2.append(false)
            .append(false)
            .append(false)
            .append(false)
            .append(false)
            .append(false)
            .append(false)
            .append(false);
        bs1.append(bs2);
        BitString bs3 = bs1.cutBits(0, 32);

        BitString bsref;
        bsref.append(false)
            .append(true)
            .append(false)
            .append(true)
            .append(true)
            .append(false)
            .append(true)
            .append(false);
        bsref.append(true)
            .append(true)
            .append(false)
            .append(false)
            .append(false)
            .append(false)
            .append(false)
            .append(false);
        bsref.append(false)
            .append(false)
            .append(false)
            .append(false)
            .append(false)
            .append(false)
            .append(false)
            .append(false);
        bsref.append(false)
            .append(false)
            .append(false)
            .append(false)
            .append(false)
            .append(false)
            .append(false)
            .append(false);

        addTestResult(bs3 == bsref && bs1 == *bs_0);
    }
}

void BitStringTest::testCompression() {
    {
        BitString bs1(std::string("a test of tests of some recurrence"));
        BitString bs2 = bs1;
        bs1.compress(9);
        bs1.uncompress(bs2.getLength());
        addTestResult(bs1 == bs2);
    }

    {
        BitString bs1(std::string("afafafafafafafaf"));
        BitString bs2 = bs1;
        bs1.compress(9);
        addTestResult(bs1 != bs2);
    }
}

void BitStringTest::testArity() {
    bs_0->setArity(2);
    addTestResult(bs_0->getNAry(0) == 0);
    bs_1->setArity(2);
    addTestResult(bs_1->getNAry(0) == 1);
    bs_10010->setArity(4);
    addTestResult(bs_10010->getNAry(1) != 3);
    bs_101011101->setArity(8);
    addTestResult(bs_101011101->getNAry(2) == 5);

    {
        BitString bs1;
        bs1.setArity(2);
        bs1.appendNAry(0);
        bs1.setArity(8);
        bs1.appendNAry(6);
        bs1.appendNAry(1);
        bs1.setArity(4);
        bs1.appendNAry(3);

        BitString bs2;
        bs2.append(false);
        bs2.append(false).append(true).append(true);
        bs2.append(true).append(false).append(false);
        bs2.append(true).append(true);

        addTestResult(bs1 == bs2);
    }
}
