root/src/tests/kits/shared/JsonEndToEndTest.cpp
/*
 * Copyright 2017-2023, Andrew Lindesay <apl@lindesay.co.nz>
 * Distributed under the terms of the MIT License.
 */
#include "JsonEndToEndTest.h"

#include <AutoDeleter.h>

#include <Json.h>
#include <JsonTextWriter.h>

#include <cppunit/TestCaller.h>
#include <cppunit/TestSuite.h>

#include "ChecksumJsonEventListener.h"
#include "FakeJsonDataGenerator.h"
#include "JsonSamples.h"


using namespace BPrivate;


static const size_t kHighVolumeItemCount = 10000;
static const uint32 kChecksumLimit = 100000;


JsonEndToEndTest::JsonEndToEndTest()
{
}


JsonEndToEndTest::~JsonEndToEndTest()
{
}


/*! Just here so it is possible to extract timings for the data generation cost.
*/

void
JsonEndToEndTest::TestHighVolumeStringSampleGenerationOnly()
{
        FakeJsonStreamDataIO* inputData = new FakeJsonStringStreamDataIO(kHighVolumeItemCount,
                kChecksumLimit);
        char c;

        while (inputData->Read(&c, 1) == 1) {
                // do nothing
        }
}


/*! Just here so it is possible to extract timings for the data generation cost.
*/

void
JsonEndToEndTest::TestHighVolumeNumberSampleGenerationOnly()
{
        FakeJsonStreamDataIO* inputData = new FakeJsonNumberStreamDataIO(kHighVolumeItemCount,
                kChecksumLimit);
        char c;

        while (inputData->Read(&c, 1) == 1) {
                // do nothing
        }
}


void
JsonEndToEndTest::TestHighVolumeStringParsing()
{
        FakeJsonStreamDataIO* inputData = new FakeJsonStringStreamDataIO(kHighVolumeItemCount,
                kChecksumLimit);
        ChecksumJsonEventListener* listener = new ChecksumJsonEventListener(kChecksumLimit);

        // ----------------------
    BPrivate::BJson::Parse(inputData, listener);
    // ----------------------

        CPPUNIT_ASSERT_EQUAL(B_OK, listener->Error());
        CPPUNIT_ASSERT_EQUAL(inputData->Checksum(), listener->Checksum());
}


void
JsonEndToEndTest::TestHighVolumeNumberParsing()
{
        FakeJsonStreamDataIO* inputData = new FakeJsonNumberStreamDataIO(kHighVolumeItemCount,
                kChecksumLimit);
        ChecksumJsonEventListener* listener = new ChecksumJsonEventListener(kChecksumLimit);

        // ----------------------
    BPrivate::BJson::Parse(inputData, listener);
    // ----------------------

        CPPUNIT_ASSERT_EQUAL(B_OK, listener->Error());
        CPPUNIT_ASSERT_EQUAL(inputData->Checksum(), listener->Checksum());
}


void
JsonEndToEndTest::TestParseAndWrite(const char* input, const char* expectedOutput)
{
        BDataIO* inputData = new BMemoryIO(input, strlen(input));
        ObjectDeleter<BDataIO> inputDataDeleter(inputData);
        BMallocIO* outputData = new BMallocIO();
        ObjectDeleter<BMallocIO> outputDataDeleter(outputData);
        BPrivate::BJsonTextWriter* listener
                = new BJsonTextWriter(outputData);
        ObjectDeleter<BPrivate::BJsonTextWriter> listenerDeleter(listener);

// ----------------------
        BPrivate::BJson::Parse(inputData, listener);
// ----------------------

        CPPUNIT_ASSERT_EQUAL(B_OK, listener->ErrorStatus());
        fprintf(stderr, "in           >%s<\n", input);
        fprintf(stderr, "expected out >%s<\n", expectedOutput);
        fprintf(stderr, "actual out   >%s<\n", (char*)outputData->Buffer());
        CPPUNIT_ASSERT_MESSAGE("expected did no equal actual output",
                0 == strncmp(expectedOutput, (char*)outputData->Buffer(),
                        strlen(expectedOutput)));
}


void
JsonEndToEndTest::TestNullA()
{
        TestParseAndWrite(JSON_SAMPLE_NULL_A_IN,
                JSON_SAMPLE_NULL_A_EXPECTED_OUT);
}


void
JsonEndToEndTest::TestTrueA()
{
        TestParseAndWrite(JSON_SAMPLE_TRUE_A_IN,
                JSON_SAMPLE_TRUE_A_EXPECTED_OUT);
}


void
JsonEndToEndTest::TestFalseA()
{
        TestParseAndWrite(JSON_SAMPLE_FALSE_A_IN,
                JSON_SAMPLE_FALSE_A_EXPECTED_OUT);
}


void
JsonEndToEndTest::TestNumberA()
{
        TestParseAndWrite(JSON_SAMPLE_NUMBER_A_IN,
                JSON_SAMPLE_NUMBER_A_EXPECTED_OUT);
}


void
JsonEndToEndTest::TestStringA()
{
        TestParseAndWrite(JSON_SAMPLE_STRING_A_IN,
                JSON_SAMPLE_STRING_A_EXPECTED_OUT);
}


void
JsonEndToEndTest::TestStringB()
{
        TestParseAndWrite(JSON_SAMPLE_STRING_B_IN,
                JSON_SAMPLE_STRING_B_EXPECTED_OUT);
}


/* In this test, there are some UTF-8 characters. */

void
JsonEndToEndTest::TestStringA2()
{
        TestParseAndWrite(JSON_SAMPLE_STRING_A2_IN,
                JSON_SAMPLE_STRING_A_EXPECTED_OUT);
}


void
JsonEndToEndTest::TestArrayA()
{
        TestParseAndWrite(JSON_SAMPLE_ARRAY_A_IN, JSON_SAMPLE_ARRAY_A_EXPECTED_OUT);
}


void
JsonEndToEndTest::TestArrayB()
{
        TestParseAndWrite(JSON_SAMPLE_ARRAY_B_IN, JSON_SAMPLE_ARRAY_B_EXPECTED_OUT);
}


void
JsonEndToEndTest::TestObjectA()
{
        TestParseAndWrite(JSON_SAMPLE_OBJECT_A_IN,
                JSON_SAMPLE_OBJECT_A_EXPECTED_OUT);
}


/*! This method will test an element being unterminated; such an object that
    is missing the terminating "}" symbol or a string that has no closing
    quote.  This is tested here because the writer
*/

void
JsonEndToEndTest::TestUnterminated(const char *input)
{
        BDataIO* inputData = new BMemoryIO(input, strlen(input));
        ObjectDeleter<BDataIO> inputDataDeleter(inputData);
        BMallocIO* outputData = new BMallocIO();
        ObjectDeleter<BMallocIO> outputDataDeleter(outputData);
        BPrivate::BJsonTextWriter* listener
                = new BJsonTextWriter(outputData);
        ObjectDeleter<BPrivate::BJsonTextWriter> listenerDeleter(listener);

// ----------------------
        BPrivate::BJson::Parse(inputData, listener);
// ----------------------

        CPPUNIT_ASSERT_EQUAL(B_BAD_DATA, listener->ErrorStatus());
}


void
JsonEndToEndTest::TestStringUnterminated()
{
        TestUnterminated(JSON_SAMPLE_BROKEN_UNTERMINATED_STRING);
}

void
JsonEndToEndTest::TestArrayUnterminated()
{
        TestUnterminated(JSON_SAMPLE_BROKEN_UNTERMINATED_ARRAY);
}

void
JsonEndToEndTest::TestObjectUnterminated()
{
        TestUnterminated(JSON_SAMPLE_BROKEN_UNTERMINATED_OBJECT);
}


/*static*/ void
JsonEndToEndTest::AddTests(BTestSuite& parent)
{
        CppUnit::TestSuite& suite = *new CppUnit::TestSuite("JsonEndToEndTest");

        suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
                "JsonEndToEndTest::TestNullA", &JsonEndToEndTest::TestNullA));
        suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
                "JsonEndToEndTest::TestTrueA", &JsonEndToEndTest::TestTrueA));
        suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
                "JsonEndToEndTest::TestFalseA", &JsonEndToEndTest::TestFalseA));
        suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
                "JsonEndToEndTest::TestNumberA", &JsonEndToEndTest::TestNumberA));
        suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
                "JsonEndToEndTest::TestStringA", &JsonEndToEndTest::TestStringA));
        suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
                "JsonEndToEndTest::TestStringA2", &JsonEndToEndTest::TestStringA2));
        suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
                "JsonEndToEndTest::TestStringB", &JsonEndToEndTest::TestStringB));
        suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
                "JsonEndToEndTest::TestArrayA", &JsonEndToEndTest::TestArrayA));
        suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
                "JsonEndToEndTest::TestArrayB", &JsonEndToEndTest::TestArrayB));
        suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
                "JsonEndToEndTest::TestObjectA", &JsonEndToEndTest::TestObjectA));
        suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
                "JsonEndToEndTest::TestStringUnterminated",
                &JsonEndToEndTest::TestStringUnterminated));
        suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
                "JsonEndToEndTest::TestArrayUnterminated",
                &JsonEndToEndTest::TestArrayUnterminated));
        suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
                "JsonEndToEndTest::TestHighVolumeStringParsing",
                &JsonEndToEndTest::TestHighVolumeStringParsing));
        suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
                "JsonEndToEndTest::TestHighVolumeNumberParsing",
                &JsonEndToEndTest::TestHighVolumeNumberParsing));
        suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
                "JsonEndToEndTest::TestHighVolumeStringSampleGenerationOnly",
                &JsonEndToEndTest::TestHighVolumeStringSampleGenerationOnly));
        suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
                "JsonEndToEndTest::TestHighVolumeNumberSampleGenerationOnly",
                &JsonEndToEndTest::TestHighVolumeNumberSampleGenerationOnly));

        parent.addTest("JsonEndToEndTest", &suite);
}