    // Copyright (C) 2006  Davis E. King (davis@dlib.net)
    // License: Boost Software License   See LICENSE.txt for the full license.

    #include <sstream>
    #include <string>
    #include <cstdlib>
    #include <ctime>
    #include <dlib/base64.h>

    #include "tester.h"

    namespace
    {
        using namespace test;
        using namespace dlib;
        using namespace std;

        logger dlog("test.base64");

        template <
            typename base64
            >
        void base64_kernel_test (
        )
        /*!
            requires
                - base64 is an implementation of base64/base64_kernel_abstract.h
            ensures
                - runs tests on base64 for compliance with the specs
        !*/
        {

            const unsigned int seed = static_cast<unsigned int>(time(0));
            try
            {

                srand(seed);

                base64 test;

                const string wiki_normal = "\
    Man is distinguished, not only by his reason, but by this singular passion from other \
    animals, which is a lust of the mind, that by a perseverance of delight in the continued \
    and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure.";

                const string wiki_encoded = "\
    TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0\n\
    aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1\n\
    c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0\n\
    aGUgY29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdl\n\
    LCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=";

                string str;

                istringstream sin;
                ostringstream sout;

                sin.str(wiki_encoded);
                test.decode(sin,sout);
                DLIB_TEST_MSG(sout.str() == wiki_normal,
                       "sout.str(): " << sout.str() <<
                       "\nwiki_normal: " << wiki_normal);

                sout.str("");
                sin.str(wiki_normal);
                sin.clear();
                test.encode(sin,sout);

                string a(sout.str()), b(wiki_encoded);
                // we want to strip all the whitespace from a and b now
                sin.str(a);
                a.clear();
                sin >> str;
                while (sin)
                {
                    a += str;
                    sin >> str;
                }

                sin.clear();
                sin.str(b);
                b.clear();
                sin >> str;
                while (sin)
                {
                    b += str;
                    sin >> str;
                }
                sin.clear();

                DLIB_TEST_MSG(a == b,
                       "a: \n" << a <<
                       "\n\nb: \n" << b);

                sin.clear();
                sin.str("");
                sout.str("");
                test.encode(sin,sout);
                sin.str(sout.str());
                sout.str("");
                test.decode(sin,sout);
                DLIB_TEST(sout.str() == "");

                sin.clear();
                sin.str("a");
                sout.str("");
                test.encode(sin,sout);
                sin.str(sout.str());
                sout.str("");
                test.decode(sin,sout);
                DLIB_TEST(sout.str() == "a");

                sin.clear();
                sin.str("da");
                sout.str("");
                test.encode(sin,sout);
                sin.str(sout.str());
                sout.str("");
                test.decode(sin,sout);
                DLIB_TEST(sout.str() == "da");

                sin.clear();
                sin.str("dav");
                sout.str("");
                test.encode(sin,sout);
                sin.str(sout.str());
                sout.str("");
                test.decode(sin,sout);
                DLIB_TEST(sout.str() == "dav");

                sin.clear();
                sin.str("davi");
                sout.str("");
                test.encode(sin,sout);
                sin.str(sout.str());
                sout.str("");
                test.decode(sin,sout);
                DLIB_TEST(sout.str() == "davi");

                for (int i = 0; i < 1000; ++i)
                {
                    str.clear();
                    sin.clear();
                    sout.str("");
                    sin.str("");

                    // fill str with random garbage
                    const int size = rand()%2000;
                    for (int j = 0; j < size; ++j)
                    {
                        unsigned char ch = rand()&0xFF;
                        str += ch;
                    }

                    sin.str(str);
                    test.encode(sin,sout);
                    sin.clear();
                    sin.str(sout.str());
                    sout.str("");
                    test.decode(sin,sout);

                    DLIB_TEST(str == sout.str());

                }

            }
            catch (typename base64::decode_error& e)
            {
                DLIB_TEST_MSG(false,
                    "decode_error thrown when it shouldn't have been (" << seed << "):\n "
                     << e.info);
            }
        }

        class base64_tester : public tester
        {
        public:
            base64_tester (
            ) :
                tester ("test_base64",
                        "Runs tests on the base64 component.")
            {}

            void perform_test (
            )
            {
                print_spinner();
                base64_kernel_test<base64>();
            }
        } a;

    }