1228512Sjilles/*
2228512Sjilles * Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>
3228512Sjilles * Distributed under the terms of the MIT License.
4228512Sjilles */
5228512Sjilles#include "JsonErrorHandlingTest.h"
6228512Sjilles
7228512Sjilles#include <AutoDeleter.h>
8228512Sjilles
9228512Sjilles#include <Json.h>
10228512Sjilles#include <JsonEventListener.h>
11228512Sjilles
12228512Sjilles#include <cppunit/TestCaller.h>
13228512Sjilles#include <cppunit/TestSuite.h>
14228512Sjilles
15228512Sjilles#include "JsonSamples.h"
16228512Sjilles
17228512Sjilles
18228512Sjillesusing namespace BPrivate;
19228512Sjilles
20228512Sjillesclass ErrorCapturingListener : public BJsonEventListener {
21228512Sjillespublic:
22228512Sjilles								ErrorCapturingListener();
23228512Sjilles		virtual					~ErrorCapturingListener();
24228512Sjilles
25228512Sjilles			bool				Handle(const BJsonEvent& event);
26228512Sjilles			void				HandleError(status_t status, int32 line,
27228512Sjilles									const char* message);
28228512Sjilles			void				Complete() {};
29290914Sngie
30228512Sjilles			status_t			ErrorStatus();
31290914Sngie			int32				GetErrorLine();
32290914Sngie			BString				GetErrorMessage();
33290914Sngie			bool				HasEventsAfterError();
34228512Sjilles			json_event_type		FirstEventTypeAfterError();
35228512Sjillesprivate:
36228512Sjilles			status_t			fErrorStatus;
37228512Sjilles			int32				fErrorLine;
38228512Sjilles			BString				fErrorMessage;
39290914Sngie			json_event_type		fFirstEventTypeAfterError;
40228512Sjilles			int32				fEventCountAfterError;
41228512Sjilles
42228512Sjilles};
43228512Sjilles
44228512Sjilles
45228512Sjilles/*! This DataIO concrete implementation is designed to open and then to fail
46228512Sjilles    in order to simulate what might happen if there were an IO problem when
47228512Sjilles    parsing some JSON.
48228512Sjilles*/
49228512Sjilles
50228512Sjillesclass FailingDataIO : public BDataIO {
51228512Sjillespublic:
52228512Sjilles									FailingDataIO();
53228512Sjilles		virtual						~FailingDataIO();
54228512Sjilles
55228512Sjilles		ssize_t						Read(void* buffer, size_t size);
56228512Sjilles		ssize_t						Write(const void* buffer, size_t size);
57228512Sjilles
58228512Sjilles		status_t					Flush();
59228512Sjilles};
60228512Sjilles
61228512Sjilles
62228512Sjilles// #pragma mark - FailingDataIO
63228512Sjilles
64228512Sjilles
65228512SjillesFailingDataIO::FailingDataIO()
66228512Sjilles{
67228512Sjilles}
68
69
70FailingDataIO::~FailingDataIO()
71{
72}
73
74
75ssize_t
76FailingDataIO::Read(void* buffer, size_t size)
77{
78	return B_IO_ERROR;
79}
80
81
82ssize_t
83FailingDataIO::Write(const void* buffer, size_t size)
84{
85	fprintf(stdout, "attempt to write");
86	return B_IO_ERROR;
87}
88
89
90status_t
91FailingDataIO::Flush()
92{
93	return B_IO_ERROR;
94}
95
96
97// #pragma mark - ErrorCapturingListener
98
99
100ErrorCapturingListener::ErrorCapturingListener()
101{
102	fErrorStatus = B_OK;
103	fFirstEventTypeAfterError = B_JSON_NULL; // least likely
104	fEventCountAfterError = 0;
105}
106
107
108ErrorCapturingListener::~ErrorCapturingListener()
109{
110}
111
112
113bool
114ErrorCapturingListener::Handle(const BJsonEvent& event)
115{
116	if (fErrorStatus != B_OK) {
117		if (fEventCountAfterError == 0)
118			fFirstEventTypeAfterError = event.EventType();
119
120		fEventCountAfterError++;
121	}
122	return true; // keep going.
123}
124
125
126void
127ErrorCapturingListener::HandleError(status_t status, int32 line,
128	const char *message)
129{
130	fErrorStatus = status;
131	fErrorLine = line;
132
133	if (message != NULL)
134		fErrorMessage = BString(message);
135	else
136		fErrorMessage = BString();
137}
138
139
140status_t
141ErrorCapturingListener::ErrorStatus()
142{
143	return fErrorStatus;
144}
145
146
147int32
148ErrorCapturingListener::GetErrorLine()
149{
150	return fErrorLine;
151}
152
153
154BString
155ErrorCapturingListener::GetErrorMessage()
156{
157	return fErrorMessage;
158}
159
160
161json_event_type
162ErrorCapturingListener::FirstEventTypeAfterError()
163{
164	return fFirstEventTypeAfterError;
165}
166
167
168bool
169ErrorCapturingListener::HasEventsAfterError()
170{
171	return fEventCountAfterError > 0;
172}
173
174
175JsonErrorHandlingTest::JsonErrorHandlingTest()
176{
177}
178
179
180JsonErrorHandlingTest::~JsonErrorHandlingTest()
181{
182}
183
184
185void
186JsonErrorHandlingTest::TestParseWithBadStringEscape(const char* input,
187	int32 line, status_t expectedStatus, char expectedBadEscapeChar)
188{
189	BString expectedMessage;
190	expectedMessage.SetToFormat("unexpected escaped character [%c] "
191		"in string parsing", expectedBadEscapeChar);
192
193	TestParseWithErrorMessage(input, line, expectedStatus,
194		expectedMessage.String());
195}
196
197
198void
199JsonErrorHandlingTest::TestParseWithUnterminatedElement(const char* input,
200	int32 line, status_t expectedStatus)
201{
202	BString expectedMessage;
203	expectedMessage.SetToFormat("unterminated element");
204
205	TestParseWithErrorMessage(input, line, expectedStatus,
206		expectedMessage.String());
207}
208
209
210void
211JsonErrorHandlingTest::TestParseWithUnexpectedCharacter(const char* input,
212	int32 line, status_t expectedStatus, char expectedChar)
213{
214	BString expectedMessage;
215	expectedMessage.SetToFormat("unexpected character [%" B_PRIu8
216		"] (%c) when parsing element", static_cast<uint8>(expectedChar),
217		expectedChar);
218
219	TestParseWithErrorMessage(input, line, expectedStatus,
220		expectedMessage.String());
221}
222
223
224void
225JsonErrorHandlingTest::TestParseWithErrorMessage(const char* input, int32 line,
226	status_t expectedStatus, const char* expectedMessage)
227{
228	fprintf(stderr, "in >%s<\n", input);
229	BDataIO *inputData = new BMemoryIO(input, strlen(input));
230	ObjectDeleter<BDataIO> inputDataDeleter(inputData);
231	TestParseWithErrorMessage(inputData, line, expectedStatus, expectedMessage);
232}
233
234
235void
236JsonErrorHandlingTest::TestParseWithErrorMessage(BDataIO* inputData, int32 line,
237	status_t expectedStatus, const char* expectedMessage)
238{
239	ErrorCapturingListener* listener = new ErrorCapturingListener();
240	ObjectDeleter<ErrorCapturingListener> listenerDeleter(listener);
241
242// ----------------------
243	BPrivate::BJson::Parse(inputData, listener);
244// ----------------------
245
246	fprintf(stderr, "expected error at line %" B_PRIi32 " - %s : %s\n",
247		line,
248		strerror(expectedStatus),
249		expectedMessage);
250
251	fprintf(stderr, "actual error at line %" B_PRIi32 " - %s : %s\n",
252		listener->GetErrorLine(),
253		strerror(listener->ErrorStatus()),
254		listener->GetErrorMessage().String());
255
256	if (listener->HasEventsAfterError()) {
257		fprintf(stderr, "first event after error [%d]\n",
258			listener->FirstEventTypeAfterError());
259	}
260
261	CPPUNIT_ASSERT(!listener->HasEventsAfterError());
262	CPPUNIT_ASSERT_EQUAL(expectedStatus, listener->ErrorStatus());
263	CPPUNIT_ASSERT_EQUAL(line, listener->GetErrorLine());
264	CPPUNIT_ASSERT(0 == strcmp(expectedMessage,
265		listener->GetErrorMessage().String()));
266}
267
268
269void
270JsonErrorHandlingTest::TestCompletelyUnknown()
271{
272	TestParseWithUnexpectedCharacter(
273		JSON_SAMPLE_BROKEN_COMPLETELY_UNKNOWN, 1, B_BAD_DATA, 'z');
274}
275
276
277void
278JsonErrorHandlingTest::TestObjectWithPrematureSeparator()
279{
280	TestParseWithErrorMessage(JSON_SAMPLE_BROKEN_OBJECT_PREMATURE_SEPARATOR, 1,
281		B_BAD_DATA, "unexpected item separator when parsing start of object");
282}
283
284
285void
286JsonErrorHandlingTest::TestStringUnterminated()
287{
288	TestParseWithErrorMessage(JSON_SAMPLE_BROKEN_UNTERMINATED_STRING, 1,
289		B_BAD_DATA, "unexpected end of input");
290}
291
292
293void
294JsonErrorHandlingTest::TestBadStringEscape()
295{
296	TestParseWithBadStringEscape(
297		JSON_SAMPLE_BROKEN_BAD_STRING_ESCAPE, 1, B_BAD_DATA, 'v');
298}
299
300
301void
302JsonErrorHandlingTest::TestBadNumber()
303{
304	TestParseWithErrorMessage(JSON_SAMPLE_BROKEN_NUMBER, 1, B_BAD_DATA,
305		"malformed number");
306}
307
308
309void
310JsonErrorHandlingTest::TestIOIssue()
311{
312	BDataIO *inputData = new FailingDataIO();
313	ObjectDeleter<BDataIO> inputDataDeleter(inputData);
314	TestParseWithErrorMessage(inputData, -1, B_IO_ERROR,
315		"io related read error");
316}
317
318
319/*static*/ void
320JsonErrorHandlingTest::AddTests(BTestSuite& parent)
321{
322	CppUnit::TestSuite& suite = *new CppUnit::TestSuite(
323		"JsonErrorHandlingTest");
324
325	suite.addTest(new CppUnit::TestCaller<JsonErrorHandlingTest>(
326		"JsonErrorHandlingTest::TestCompletelyUnknown",
327		&JsonErrorHandlingTest::TestCompletelyUnknown));
328
329	suite.addTest(new CppUnit::TestCaller<JsonErrorHandlingTest>(
330		"JsonErrorHandlingTest::TestObjectWithPrematureSeparator",
331		&JsonErrorHandlingTest::TestObjectWithPrematureSeparator));
332
333	suite.addTest(new CppUnit::TestCaller<JsonErrorHandlingTest>(
334		"JsonErrorHandlingTest::TestStringUnterminated",
335		&JsonErrorHandlingTest::TestStringUnterminated));
336
337	suite.addTest(new CppUnit::TestCaller<JsonErrorHandlingTest>(
338		"JsonErrorHandlingTest::TestBadStringEscape",
339		&JsonErrorHandlingTest::TestBadStringEscape));
340
341	suite.addTest(new CppUnit::TestCaller<JsonErrorHandlingTest>(
342		"JsonErrorHandlingTest::TestBadNumber",
343		&JsonErrorHandlingTest::TestBadNumber));
344
345	suite.addTest(new CppUnit::TestCaller<JsonErrorHandlingTest>(
346		"JsonErrorHandlingTest::TestIOIssue",
347		&JsonErrorHandlingTest::TestIOIssue));
348
349	parent.addTest("JsonErrorHandlingTest", &suite);
350}