xref: /haiku/src/tests/kits/shared/JsonErrorHandlingTest.cpp (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
1 /*
2  * Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>
3  * Distributed under the terms of the MIT License.
4  */
5 #include "JsonErrorHandlingTest.h"
6 
7 #include <AutoDeleter.h>
8 
9 #include <Json.h>
10 #include <JsonEventListener.h>
11 
12 #include <cppunit/TestCaller.h>
13 #include <cppunit/TestSuite.h>
14 
15 #include "JsonSamples.h"
16 
17 
18 using namespace BPrivate;
19 
20 class ErrorCapturingListener : public BJsonEventListener {
21 public:
22 								ErrorCapturingListener();
23 		virtual					~ErrorCapturingListener();
24 
25 			bool				Handle(const BJsonEvent& event);
26 			void				HandleError(status_t status, int32 line,
27 									const char* message);
28 			void				Complete() {};
29 
30 			status_t			ErrorStatus();
31 			int32				GetErrorLine();
32 			BString				GetErrorMessage();
33 			bool				HasEventsAfterError();
34 			json_event_type		FirstEventTypeAfterError();
35 private:
36 			status_t			fErrorStatus;
37 			int32				fErrorLine;
38 			BString				fErrorMessage;
39 			json_event_type		fFirstEventTypeAfterError;
40 			int32				fEventCountAfterError;
41 
42 };
43 
44 
45 /*! This DataIO concrete implementation is designed to open and then to fail
46     in order to simulate what might happen if there were an IO problem when
47     parsing some JSON.
48 */
49 
50 class FailingDataIO : public BDataIO {
51 public:
52 									FailingDataIO();
53 		virtual						~FailingDataIO();
54 
55 		ssize_t						Read(void* buffer, size_t size);
56 		ssize_t						Write(const void* buffer, size_t size);
57 
58 		status_t					Flush();
59 };
60 
61 
62 // #pragma mark - FailingDataIO
63 
64 
65 FailingDataIO::FailingDataIO()
66 {
67 }
68 
69 
70 FailingDataIO::~FailingDataIO()
71 {
72 }
73 
74 
75 ssize_t
76 FailingDataIO::Read(void* buffer, size_t size)
77 {
78 	return B_IO_ERROR;
79 }
80 
81 
82 ssize_t
83 FailingDataIO::Write(const void* buffer, size_t size)
84 {
85 	fprintf(stdout, "attempt to write");
86 	return B_IO_ERROR;
87 }
88 
89 
90 status_t
91 FailingDataIO::Flush()
92 {
93 	return B_IO_ERROR;
94 }
95 
96 
97 // #pragma mark - ErrorCapturingListener
98 
99 
100 ErrorCapturingListener::ErrorCapturingListener()
101 {
102 	fErrorStatus = B_OK;
103 	fFirstEventTypeAfterError = B_JSON_NULL; // least likely
104 	fEventCountAfterError = 0;
105 }
106 
107 
108 ErrorCapturingListener::~ErrorCapturingListener()
109 {
110 }
111 
112 
113 bool
114 ErrorCapturingListener::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 
126 void
127 ErrorCapturingListener::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 
140 status_t
141 ErrorCapturingListener::ErrorStatus()
142 {
143 	return fErrorStatus;
144 }
145 
146 
147 int32
148 ErrorCapturingListener::GetErrorLine()
149 {
150 	return fErrorLine;
151 }
152 
153 
154 BString
155 ErrorCapturingListener::GetErrorMessage()
156 {
157 	return fErrorMessage;
158 }
159 
160 
161 json_event_type
162 ErrorCapturingListener::FirstEventTypeAfterError()
163 {
164 	return fFirstEventTypeAfterError;
165 }
166 
167 
168 bool
169 ErrorCapturingListener::HasEventsAfterError()
170 {
171 	return fEventCountAfterError > 0;
172 }
173 
174 
175 JsonErrorHandlingTest::JsonErrorHandlingTest()
176 {
177 }
178 
179 
180 JsonErrorHandlingTest::~JsonErrorHandlingTest()
181 {
182 }
183 
184 
185 void
186 JsonErrorHandlingTest::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 
198 void
199 JsonErrorHandlingTest::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 
210 void
211 JsonErrorHandlingTest::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 
224 void
225 JsonErrorHandlingTest::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 
235 void
236 JsonErrorHandlingTest::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 
269 void
270 JsonErrorHandlingTest::TestCompletelyUnknown()
271 {
272 	TestParseWithUnexpectedCharacter(
273 		JSON_SAMPLE_BROKEN_COMPLETELY_UNKNOWN, 1, B_BAD_DATA, 'z');
274 }
275 
276 
277 void
278 JsonErrorHandlingTest::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 
285 void
286 JsonErrorHandlingTest::TestStringUnterminated()
287 {
288 	TestParseWithErrorMessage(JSON_SAMPLE_BROKEN_UNTERMINATED_STRING, 1,
289 		B_BAD_DATA, "unexpected end of input");
290 }
291 
292 
293 void
294 JsonErrorHandlingTest::TestBadStringEscape()
295 {
296 	TestParseWithBadStringEscape(
297 		JSON_SAMPLE_BROKEN_BAD_STRING_ESCAPE, 1, B_BAD_DATA, 'v');
298 }
299 
300 
301 void
302 JsonErrorHandlingTest::TestBadNumber()
303 {
304 	TestParseWithErrorMessage(JSON_SAMPLE_BROKEN_NUMBER, 1, B_BAD_DATA,
305 		"malformed number");
306 }
307 
308 
309 void
310 JsonErrorHandlingTest::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
320 JsonErrorHandlingTest::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 }