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);
Complete()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
FailingDataIO()65 FailingDataIO::FailingDataIO()
66 {
67 }
68
69
~FailingDataIO()70 FailingDataIO::~FailingDataIO()
71 {
72 }
73
74
75 ssize_t
Read(void * buffer,size_t size)76 FailingDataIO::Read(void* buffer, size_t size)
77 {
78 return B_IO_ERROR;
79 }
80
81
82 ssize_t
Write(const void * buffer,size_t size)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
Flush()91 FailingDataIO::Flush()
92 {
93 return B_IO_ERROR;
94 }
95
96
97 // #pragma mark - ErrorCapturingListener
98
99
ErrorCapturingListener()100 ErrorCapturingListener::ErrorCapturingListener()
101 {
102 fErrorStatus = B_OK;
103 fFirstEventTypeAfterError = B_JSON_NULL; // least likely
104 fEventCountAfterError = 0;
105 }
106
107
~ErrorCapturingListener()108 ErrorCapturingListener::~ErrorCapturingListener()
109 {
110 }
111
112
113 bool
Handle(const BJsonEvent & event)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
HandleError(status_t status,int32 line,const char * message)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
ErrorStatus()141 ErrorCapturingListener::ErrorStatus()
142 {
143 return fErrorStatus;
144 }
145
146
147 int32
GetErrorLine()148 ErrorCapturingListener::GetErrorLine()
149 {
150 return fErrorLine;
151 }
152
153
154 BString
GetErrorMessage()155 ErrorCapturingListener::GetErrorMessage()
156 {
157 return fErrorMessage;
158 }
159
160
161 json_event_type
FirstEventTypeAfterError()162 ErrorCapturingListener::FirstEventTypeAfterError()
163 {
164 return fFirstEventTypeAfterError;
165 }
166
167
168 bool
HasEventsAfterError()169 ErrorCapturingListener::HasEventsAfterError()
170 {
171 return fEventCountAfterError > 0;
172 }
173
174
JsonErrorHandlingTest()175 JsonErrorHandlingTest::JsonErrorHandlingTest()
176 {
177 }
178
179
~JsonErrorHandlingTest()180 JsonErrorHandlingTest::~JsonErrorHandlingTest()
181 {
182 }
183
184
185 void
TestParseWithBadStringEscape(const char * input,int32 line,status_t expectedStatus,char expectedBadEscapeChar)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
TestParseWithUnterminatedElement(const char * input,int32 line,status_t expectedStatus)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
TestParseWithUnexpectedCharacter(const char * input,int32 line,status_t expectedStatus,char expectedChar)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
TestParseWithErrorMessage(const char * input,int32 line,status_t expectedStatus,const char * expectedMessage)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
TestParseWithErrorMessage(BDataIO * inputData,int32 line,status_t expectedStatus,const char * expectedMessage)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
TestCompletelyUnknown()270 JsonErrorHandlingTest::TestCompletelyUnknown()
271 {
272 TestParseWithUnexpectedCharacter(
273 JSON_SAMPLE_BROKEN_COMPLETELY_UNKNOWN, 1, B_BAD_DATA, 'z');
274 }
275
276
277 void
TestObjectWithPrematureSeparator()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
TestStringUnterminated()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
TestBadStringEscape()294 JsonErrorHandlingTest::TestBadStringEscape()
295 {
296 TestParseWithBadStringEscape(
297 JSON_SAMPLE_BROKEN_BAD_STRING_ESCAPE, 1, B_BAD_DATA, 'v');
298 }
299
300
301 void
TestBadNumber()302 JsonErrorHandlingTest::TestBadNumber()
303 {
304 TestParseWithErrorMessage(JSON_SAMPLE_BROKEN_NUMBER, 1, B_BAD_DATA,
305 "malformed number");
306 }
307
308
309 void
TestIOIssue()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
AddTests(BTestSuite & parent)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 }