1/*
2 * Copyright 2017-2023, Andrew Lindesay <apl@lindesay.co.nz>
3 * Distributed under the terms of the MIT License.
4 */
5#include "JsonEndToEndTest.h"
6
7#include <AutoDeleter.h>
8
9#include <Json.h>
10#include <JsonTextWriter.h>
11
12#include <cppunit/TestCaller.h>
13#include <cppunit/TestSuite.h>
14
15#include "ChecksumJsonEventListener.h"
16#include "FakeJsonDataGenerator.h"
17#include "JsonSamples.h"
18
19
20using namespace BPrivate;
21
22
23static const size_t kHighVolumeItemCount = 10000;
24static const uint32 kChecksumLimit = 100000;
25
26
27JsonEndToEndTest::JsonEndToEndTest()
28{
29}
30
31
32JsonEndToEndTest::~JsonEndToEndTest()
33{
34}
35
36
37/*! Just here so it is possible to extract timings for the data generation cost.
38*/
39
40void
41JsonEndToEndTest::TestHighVolumeStringSampleGenerationOnly()
42{
43	FakeJsonStreamDataIO* inputData = new FakeJsonStringStreamDataIO(kHighVolumeItemCount,
44		kChecksumLimit);
45	char c;
46
47	while (inputData->Read(&c, 1) == 1) {
48		// do nothing
49	}
50}
51
52
53/*! Just here so it is possible to extract timings for the data generation cost.
54*/
55
56void
57JsonEndToEndTest::TestHighVolumeNumberSampleGenerationOnly()
58{
59	FakeJsonStreamDataIO* inputData = new FakeJsonNumberStreamDataIO(kHighVolumeItemCount,
60		kChecksumLimit);
61	char c;
62
63	while (inputData->Read(&c, 1) == 1) {
64		// do nothing
65	}
66}
67
68
69void
70JsonEndToEndTest::TestHighVolumeStringParsing()
71{
72	FakeJsonStreamDataIO* inputData = new FakeJsonStringStreamDataIO(kHighVolumeItemCount,
73		kChecksumLimit);
74	ChecksumJsonEventListener* listener = new ChecksumJsonEventListener(kChecksumLimit);
75
76	// ----------------------
77    BPrivate::BJson::Parse(inputData, listener);
78    // ----------------------
79
80	CPPUNIT_ASSERT_EQUAL(B_OK, listener->Error());
81	CPPUNIT_ASSERT_EQUAL(inputData->Checksum(), listener->Checksum());
82}
83
84
85void
86JsonEndToEndTest::TestHighVolumeNumberParsing()
87{
88	FakeJsonStreamDataIO* inputData = new FakeJsonNumberStreamDataIO(kHighVolumeItemCount,
89		kChecksumLimit);
90	ChecksumJsonEventListener* listener = new ChecksumJsonEventListener(kChecksumLimit);
91
92	// ----------------------
93    BPrivate::BJson::Parse(inputData, listener);
94    // ----------------------
95
96	CPPUNIT_ASSERT_EQUAL(B_OK, listener->Error());
97	CPPUNIT_ASSERT_EQUAL(inputData->Checksum(), listener->Checksum());
98}
99
100
101void
102JsonEndToEndTest::TestParseAndWrite(const char* input, const char* expectedOutput)
103{
104	BDataIO* inputData = new BMemoryIO(input, strlen(input));
105	ObjectDeleter<BDataIO> inputDataDeleter(inputData);
106	BMallocIO* outputData = new BMallocIO();
107	ObjectDeleter<BMallocIO> outputDataDeleter(outputData);
108	BPrivate::BJsonTextWriter* listener
109		= new BJsonTextWriter(outputData);
110	ObjectDeleter<BPrivate::BJsonTextWriter> listenerDeleter(listener);
111
112// ----------------------
113	BPrivate::BJson::Parse(inputData, listener);
114// ----------------------
115
116	CPPUNIT_ASSERT_EQUAL(B_OK, listener->ErrorStatus());
117	fprintf(stderr, "in           >%s<\n", input);
118	fprintf(stderr, "expected out >%s<\n", expectedOutput);
119	fprintf(stderr, "actual out   >%s<\n", (char*)outputData->Buffer());
120	CPPUNIT_ASSERT_MESSAGE("expected did no equal actual output",
121		0 == strncmp(expectedOutput, (char*)outputData->Buffer(),
122			strlen(expectedOutput)));
123}
124
125
126void
127JsonEndToEndTest::TestNullA()
128{
129	TestParseAndWrite(JSON_SAMPLE_NULL_A_IN,
130		JSON_SAMPLE_NULL_A_EXPECTED_OUT);
131}
132
133
134void
135JsonEndToEndTest::TestTrueA()
136{
137	TestParseAndWrite(JSON_SAMPLE_TRUE_A_IN,
138		JSON_SAMPLE_TRUE_A_EXPECTED_OUT);
139}
140
141
142void
143JsonEndToEndTest::TestFalseA()
144{
145	TestParseAndWrite(JSON_SAMPLE_FALSE_A_IN,
146		JSON_SAMPLE_FALSE_A_EXPECTED_OUT);
147}
148
149
150void
151JsonEndToEndTest::TestNumberA()
152{
153	TestParseAndWrite(JSON_SAMPLE_NUMBER_A_IN,
154		JSON_SAMPLE_NUMBER_A_EXPECTED_OUT);
155}
156
157
158void
159JsonEndToEndTest::TestStringA()
160{
161	TestParseAndWrite(JSON_SAMPLE_STRING_A_IN,
162		JSON_SAMPLE_STRING_A_EXPECTED_OUT);
163}
164
165
166void
167JsonEndToEndTest::TestStringB()
168{
169	TestParseAndWrite(JSON_SAMPLE_STRING_B_IN,
170		JSON_SAMPLE_STRING_B_EXPECTED_OUT);
171}
172
173
174/* In this test, there are some UTF-8 characters. */
175
176void
177JsonEndToEndTest::TestStringA2()
178{
179	TestParseAndWrite(JSON_SAMPLE_STRING_A2_IN,
180		JSON_SAMPLE_STRING_A_EXPECTED_OUT);
181}
182
183
184void
185JsonEndToEndTest::TestArrayA()
186{
187	TestParseAndWrite(JSON_SAMPLE_ARRAY_A_IN, JSON_SAMPLE_ARRAY_A_EXPECTED_OUT);
188}
189
190
191void
192JsonEndToEndTest::TestArrayB()
193{
194	TestParseAndWrite(JSON_SAMPLE_ARRAY_B_IN, JSON_SAMPLE_ARRAY_B_EXPECTED_OUT);
195}
196
197
198void
199JsonEndToEndTest::TestObjectA()
200{
201	TestParseAndWrite(JSON_SAMPLE_OBJECT_A_IN,
202		JSON_SAMPLE_OBJECT_A_EXPECTED_OUT);
203}
204
205
206/*! This method will test an element being unterminated; such an object that
207    is missing the terminating "}" symbol or a string that has no closing
208    quote.  This is tested here because the writer
209*/
210
211void
212JsonEndToEndTest::TestUnterminated(const char *input)
213{
214	BDataIO* inputData = new BMemoryIO(input, strlen(input));
215	ObjectDeleter<BDataIO> inputDataDeleter(inputData);
216	BMallocIO* outputData = new BMallocIO();
217	ObjectDeleter<BMallocIO> outputDataDeleter(outputData);
218	BPrivate::BJsonTextWriter* listener
219		= new BJsonTextWriter(outputData);
220	ObjectDeleter<BPrivate::BJsonTextWriter> listenerDeleter(listener);
221
222// ----------------------
223	BPrivate::BJson::Parse(inputData, listener);
224// ----------------------
225
226	CPPUNIT_ASSERT_EQUAL(B_BAD_DATA, listener->ErrorStatus());
227}
228
229
230void
231JsonEndToEndTest::TestStringUnterminated()
232{
233	TestUnterminated(JSON_SAMPLE_BROKEN_UNTERMINATED_STRING);
234}
235
236void
237JsonEndToEndTest::TestArrayUnterminated()
238{
239	TestUnterminated(JSON_SAMPLE_BROKEN_UNTERMINATED_ARRAY);
240}
241
242void
243JsonEndToEndTest::TestObjectUnterminated()
244{
245	TestUnterminated(JSON_SAMPLE_BROKEN_UNTERMINATED_OBJECT);
246}
247
248
249/*static*/ void
250JsonEndToEndTest::AddTests(BTestSuite& parent)
251{
252	CppUnit::TestSuite& suite = *new CppUnit::TestSuite("JsonEndToEndTest");
253
254	suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
255		"JsonEndToEndTest::TestNullA", &JsonEndToEndTest::TestNullA));
256	suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
257		"JsonEndToEndTest::TestTrueA", &JsonEndToEndTest::TestTrueA));
258	suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
259		"JsonEndToEndTest::TestFalseA", &JsonEndToEndTest::TestFalseA));
260	suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
261		"JsonEndToEndTest::TestNumberA", &JsonEndToEndTest::TestNumberA));
262	suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
263		"JsonEndToEndTest::TestStringA", &JsonEndToEndTest::TestStringA));
264	suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
265		"JsonEndToEndTest::TestStringA2", &JsonEndToEndTest::TestStringA2));
266	suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
267		"JsonEndToEndTest::TestStringB", &JsonEndToEndTest::TestStringB));
268	suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
269		"JsonEndToEndTest::TestArrayA", &JsonEndToEndTest::TestArrayA));
270	suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
271		"JsonEndToEndTest::TestArrayB", &JsonEndToEndTest::TestArrayB));
272	suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
273		"JsonEndToEndTest::TestObjectA", &JsonEndToEndTest::TestObjectA));
274	suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
275		"JsonEndToEndTest::TestStringUnterminated",
276		&JsonEndToEndTest::TestStringUnterminated));
277	suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
278		"JsonEndToEndTest::TestArrayUnterminated",
279		&JsonEndToEndTest::TestArrayUnterminated));
280	suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
281		"JsonEndToEndTest::TestHighVolumeStringParsing",
282		&JsonEndToEndTest::TestHighVolumeStringParsing));
283	suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
284		"JsonEndToEndTest::TestHighVolumeNumberParsing",
285		&JsonEndToEndTest::TestHighVolumeNumberParsing));
286	suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
287		"JsonEndToEndTest::TestHighVolumeStringSampleGenerationOnly",
288		&JsonEndToEndTest::TestHighVolumeStringSampleGenerationOnly));
289	suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
290		"JsonEndToEndTest::TestHighVolumeNumberSampleGenerationOnly",
291		&JsonEndToEndTest::TestHighVolumeNumberSampleGenerationOnly));
292
293	parent.addTest("JsonEndToEndTest", &suite);
294}
295