<commit_before>#include "fly/coders/base64/base64_coder.hpp"

#include "test/util/path_util.hpp"

#include <gtest/gtest.h>

#include <filesystem>

//==================================================================================================
class Base64CoderTest : public ::testing::Test
{
protected:
    fly::Base64Coder m_coder;
};

//==================================================================================================
TEST_F(Base64CoderTest, Empty)
{
    const std::string raw;
    std::string enc, dec;

    ASSERT_TRUE(m_coder.encode_string(raw, enc));
    ASSERT_TRUE(m_coder.decode_string(enc, dec));

    EXPECT_TRUE(enc.empty());
    EXPECT_EQ(raw, dec);
}

//==================================================================================================
TEST_F(Base64CoderTest, ZeroPadding)
{
    const std::string raw = "Man";
    std::string enc, dec;

    ASSERT_TRUE(m_coder.encode_string(raw, enc));
    ASSERT_TRUE(m_coder.decode_string(enc, dec));

    EXPECT_EQ(enc, "TWFu");
    EXPECT_EQ(raw, dec);
}

//==================================================================================================
TEST_F(Base64CoderTest, SinglePadding)
{
    const std::string raw = "Ma";
    std::string enc, dec;

    ASSERT_TRUE(m_coder.encode_string(raw, enc));
    ASSERT_TRUE(m_coder.decode_string(enc, dec));

    EXPECT_EQ(enc, "TWE=");
    EXPECT_EQ(raw, dec);
}

//==================================================================================================
TEST_F(Base64CoderTest, DoublePadding)
{
    const std::string raw = "M";
    std::string enc, dec;

    ASSERT_TRUE(m_coder.encode_string(raw, enc));
    ASSERT_TRUE(m_coder.decode_string(enc, dec));

    EXPECT_EQ(enc, "TQ==");
    EXPECT_EQ(raw, dec);
}

//==================================================================================================
TEST_F(Base64CoderTest, InvalidSymbol)
{
    std::string dec;

    ASSERT_FALSE(m_coder.decode_string("^", dec));
    ASSERT_FALSE(m_coder.decode_string("ab^", dec));
    ASSERT_FALSE(m_coder.decode_string("^ab", dec));
    ASSERT_FALSE(m_coder.decode_string("ab^ab", dec));
}

//==================================================================================================
TEST_F(Base64CoderTest, InvalidChunkSize)
{
    std::string dec;

    ASSERT_FALSE(m_coder.decode_string("a", dec));
    ASSERT_FALSE(m_coder.decode_string("ab", dec));
    ASSERT_FALSE(m_coder.decode_string("abc", dec));
    ASSERT_FALSE(m_coder.decode_string("abcde", dec));
    ASSERT_FALSE(m_coder.decode_string("abcdef", dec));
    ASSERT_FALSE(m_coder.decode_string("abcdefg", dec));
}

//==================================================================================================
TEST_F(Base64CoderTest, WikiExample)
{
    // Example from: https://en.wikipedia.org/wiki/Base64#Examples
    const std::string raw =
        "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.";
    std::string enc, dec;

    ASSERT_TRUE(m_coder.encode_string(raw, enc));
    ASSERT_TRUE(m_coder.decode_string(enc, dec));

    const std::string expected =
        "<KEY>"
        "<KEY>"
        "<KEY>"
        "4gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4"
        "=";

    EXPECT_EQ(enc, expected);
    EXPECT_EQ(raw, dec);
}

//==================================================================================================
class Base64CoderFileTest : public Base64CoderTest
{
public:
    Base64CoderFileTest() noexcept :
        Base64CoderTest(),
        m_encoded_file(m_path.file()),
        m_decoded_file(m_path.file())
    {
    }

protected:
    fly::PathUtil::ScopedTempDirectory m_path;
    std::filesystem::path m_encoded_file;
    std::filesystem::path m_decoded_file;
};

//==================================================================================================
TEST_F(Base64CoderFileTest, AsciiFile)
{
    // Generated with:
    // tr -dc '[:graph:]' </dev/urandom | head -c 4194304 > test.txt
    const auto here = std::filesystem::path(__FILE__).parent_path();
    const auto raw = here / "data" / "test.txt";

    ASSERT_TRUE(m_coder.encode_file(raw, m_encoded_file));
    ASSERT_TRUE(m_coder.decode_file(m_encoded_file, m_decoded_file));

    // Generated with:
    // base64 -w0 test.txt > test.txt.base64
    const auto expected = here / "data" / "test.txt.base64";

    EXPECT_TRUE(fly::PathUtil::compare_files(m_encoded_file, expected));
    EXPECT_TRUE(fly::PathUtil::compare_files(raw, m_decoded_file));
}

//==================================================================================================
TEST_F(Base64CoderFileTest, PngFile)
{
    const auto here = std::filesystem::path(__FILE__).parent_path();
    const auto raw = here / "data" / "test.png";

    ASSERT_TRUE(m_coder.encode_file(raw, m_encoded_file));
    ASSERT_TRUE(m_coder.decode_file(m_encoded_file, m_decoded_file));

    // Generated with:
    // base64 -w0 test.png > test.png.base64
    const auto expected = here / "data" / "test.png.base64";

    EXPECT_TRUE(fly::PathUtil::compare_files(m_encoded_file, expected));
    EXPECT_TRUE(fly::PathUtil::compare_files(raw, m_decoded_file));
}

//==================================================================================================
TEST_F(Base64CoderFileTest, GifFile)
{
    const auto here = std::filesystem::path(__FILE__).parent_path();
    const auto raw = here / "data" / "test.gif";

    ASSERT_TRUE(m_coder.encode_file(raw, m_encoded_file));
    ASSERT_TRUE(m_coder.decode_file(m_encoded_file, m_decoded_file));

    // Generated with:
    // base64 -w0 test.gif > test.gif.base64
    const auto expected = here / "data" / "test.gif.base64";

    EXPECT_TRUE(fly::PathUtil::compare_files(m_encoded_file, expected));
    EXPECT_TRUE(fly::PathUtil::compare_files(raw, m_decoded_file));
}<commit_msg>Port Base64 coder tests to Catch2<commit_after>#include "fly/coders/base64/base64_coder.hpp"

#include "test/util/path_util.hpp"

#include <catch2/catch.hpp>

#include <cctype>
#include <filesystem>

TEST_CASE("Base64", "[coders]")
{
    fly::Base64Coder coder;

    SECTION("Encode and decode empty stream")
    {
        const std::string raw;
        std::string enc, dec;

        REQUIRE(coder.encode_string(raw, enc));
        REQUIRE(coder.decode_string(enc, dec));

        CHECK(enc.empty());
        CHECK(raw == dec);
    }

    SECTION("Encode and decode a stream without padding")
    {
        const std::string raw = "Man";
        std::string enc, dec;

        REQUIRE(coder.encode_string(raw, enc));
        REQUIRE(coder.decode_string(enc, dec));

        CHECK(enc == "TWFu");
        CHECK(raw == dec);
    }

    SECTION("Encode and decode a stream with one padding symbol")
    {
        const std::string raw = "Ma";
        std::string enc, dec;

        REQUIRE(coder.encode_string(raw, enc));
        REQUIRE(coder.decode_string(enc, dec));

        CHECK(enc == "TWE=");
        CHECK(raw == dec);
    }

    SECTION("Encode and decode a stream with two padding symbols")
    {
        const std::string raw = "M";
        std::string enc, dec;

        REQUIRE(coder.encode_string(raw, enc));
        REQUIRE(coder.decode_string(enc, dec));

        CHECK(enc == "TQ==");
        CHECK(raw == dec);
    }

    SECTION("Cannot decode streams with invalid symbols")
    {
        for (char ch = 0x00; ch >= 0x00; ++ch)
        {
            if ((ch == '+') || (ch == '/') || (ch == '=') || std::isalnum(ch))
            {
                continue;
            }

            const std::string test(1, ch);
            const std::string fill("a");
            std::string dec;

            CHECK_FALSE(coder.decode_string(test + test + test + test, dec));
            CHECK_FALSE(coder.decode_string(test + test + test + fill, dec));
            CHECK_FALSE(coder.decode_string(test + test + fill + test, dec));
            CHECK_FALSE(coder.decode_string(test + test + fill + fill, dec));
            CHECK_FALSE(coder.decode_string(test + fill + test + test, dec));
            CHECK_FALSE(coder.decode_string(test + fill + test + fill, dec));
            CHECK_FALSE(coder.decode_string(test + fill + fill + test, dec));
            CHECK_FALSE(coder.decode_string(test + fill + fill + fill, dec));
            CHECK_FALSE(coder.decode_string(fill + test + test + test, dec));
            CHECK_FALSE(coder.decode_string(fill + test + test + fill, dec));
            CHECK_FALSE(coder.decode_string(fill + test + fill + test, dec));
            CHECK_FALSE(coder.decode_string(fill + test + fill + fill, dec));
            CHECK_FALSE(coder.decode_string(fill + fill + test + test, dec));
            CHECK_FALSE(coder.decode_string(fill + fill + test + fill, dec));
            CHECK_FALSE(coder.decode_string(fill + fill + fill + test, dec));
        }
    }

    SECTION("Cannot decode streams with invalid chunk sizes")
    {
        std::string dec;

        CHECK_FALSE(coder.decode_string("a", dec));
        CHECK_FALSE(coder.decode_string("ab", dec));
        CHECK_FALSE(coder.decode_string("abc", dec));

        CHECK_FALSE(coder.decode_string("abcde", dec));
        CHECK_FALSE(coder.decode_string("abcdef", dec));
        CHECK_FALSE(coder.decode_string("abcdefg", dec));
    }

    SECTION("Example from Wikipedia: https://en.wikipedia.org/wiki/Base64#Examples")
    {
        const std::string raw =
            "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.";
        std::string enc, dec;

        REQUIRE(coder.encode_string(raw, enc));
        REQUIRE(coder.decode_string(enc, dec));

        const std::string expected =
            "<KEY>"
            "<KEY>"
            "<KEY>ZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCB<KEY>"
            "dlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5h"
            "bCBwbGVhc3VyZS4=";

        CHECK(enc == expected);
        CHECK(raw == dec);
    }

    SECTION("File tests")
    {
        fly::PathUtil::ScopedTempDirectory path;
        std::filesystem::path encoded_file = path.file();
        std::filesystem::path decoded_file = path.file();

        SECTION("Encode and decode a large file containing only ASCII symbols")
        {
            // Generated with:
            // tr -dc '[:graph:]' </dev/urandom | head -c 4194304 > test.txt
            const auto here = std::filesystem::path(__FILE__).parent_path();
            const auto raw = here / "data" / "test.txt";

            REQUIRE(coder.encode_file(raw, encoded_file));
            REQUIRE(coder.decode_file(encoded_file, decoded_file));

            // Generated with:
            // base64 -w0 test.txt > test.txt.base64
            const auto expected = here / "data" / "test.txt.base64";

            CHECK(fly::PathUtil::compare_files(encoded_file, expected));
            CHECK(fly::PathUtil::compare_files(raw, decoded_file));
        }

        SECTION("Encode and decode a PNG image file")
        {
            const auto here = std::filesystem::path(__FILE__).parent_path();
            const auto raw = here / "data" / "test.png";

            REQUIRE(coder.encode_file(raw, encoded_file));
            REQUIRE(coder.decode_file(encoded_file, decoded_file));

            // Generated with:
            // base64 -w0 test.png > test.png.base64
            const auto expected = here / "data" / "test.png.base64";

            CHECK(fly::PathUtil::compare_files(encoded_file, expected));
            CHECK(fly::PathUtil::compare_files(raw, decoded_file));
        }

        SECTION("Encode and decode a GIF image file")
        {
            const auto here = std::filesystem::path(__FILE__).parent_path();
            const auto raw = here / "data" / "test.gif";

            REQUIRE(coder.encode_file(raw, encoded_file));
            REQUIRE(coder.decode_file(encoded_file, decoded_file));

            // Generated with:
            // base64 -w0 test.gif > test.gif.base64
            const auto expected = here / "data" / "test.gif.base64";

            CHECK(fly::PathUtil::compare_files(encoded_file, expected));
            CHECK(fly::PathUtil::compare_files(raw, decoded_file));
        }
    }
}
{'fraction_non_alphanumeric': 0.21584376722576581, 'fraction_numerical': 0.011496968265217733, 'mean_word_length': 3.3153244988107375, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 24, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4200714', 'n_tokens_mistral': 4059, 'n_tokens_neox': 3839, 'n_words': 739}