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 }