/* * Copyright 2017, Andrew Lindesay * Distributed under the terms of the MIT License. */ #include "JsonErrorHandlingTest.h" #include #include #include #include #include #include "JsonSamples.h" using namespace BPrivate; class ErrorCapturingListener : public BJsonEventListener { public: ErrorCapturingListener(); virtual ~ErrorCapturingListener(); bool Handle(const BJsonEvent& event); void HandleError(status_t status, int32 line, const char* message); void Complete() {}; status_t ErrorStatus(); int32 GetErrorLine(); BString GetErrorMessage(); bool HasEventsAfterError(); json_event_type FirstEventTypeAfterError(); private: status_t fErrorStatus; int32 fErrorLine; BString fErrorMessage; json_event_type fFirstEventTypeAfterError; int32 fEventCountAfterError; }; /*! This DataIO concrete implementation is designed to open and then to fail in order to simulate what might happen if there were an IO problem when parsing some JSON. */ class FailingDataIO : public BDataIO { public: FailingDataIO(); virtual ~FailingDataIO(); ssize_t Read(void* buffer, size_t size); ssize_t Write(const void* buffer, size_t size); status_t Flush(); }; // #pragma mark - FailingDataIO FailingDataIO::FailingDataIO() { } FailingDataIO::~FailingDataIO() { } ssize_t FailingDataIO::Read(void* buffer, size_t size) { return B_IO_ERROR; } ssize_t FailingDataIO::Write(const void* buffer, size_t size) { fprintf(stdout, "attempt to write"); return B_IO_ERROR; } status_t FailingDataIO::Flush() { return B_IO_ERROR; } // #pragma mark - ErrorCapturingListener ErrorCapturingListener::ErrorCapturingListener() { fErrorStatus = B_OK; fFirstEventTypeAfterError = B_JSON_NULL; // least likely fEventCountAfterError = 0; } ErrorCapturingListener::~ErrorCapturingListener() { } bool ErrorCapturingListener::Handle(const BJsonEvent& event) { if (fErrorStatus != B_OK) { if (fEventCountAfterError == 0) fFirstEventTypeAfterError = event.EventType(); fEventCountAfterError++; } return true; // keep going. } void ErrorCapturingListener::HandleError(status_t status, int32 line, const char *message) { fErrorStatus = status; fErrorLine = line; if (message != NULL) fErrorMessage = BString(message); else fErrorMessage = BString(); } status_t ErrorCapturingListener::ErrorStatus() { return fErrorStatus; } int32 ErrorCapturingListener::GetErrorLine() { return fErrorLine; } BString ErrorCapturingListener::GetErrorMessage() { return fErrorMessage; } json_event_type ErrorCapturingListener::FirstEventTypeAfterError() { return fFirstEventTypeAfterError; } bool ErrorCapturingListener::HasEventsAfterError() { return fEventCountAfterError > 0; } JsonErrorHandlingTest::JsonErrorHandlingTest() { } JsonErrorHandlingTest::~JsonErrorHandlingTest() { } void JsonErrorHandlingTest::TestParseWithBadStringEscape(const char* input, int32 line, status_t expectedStatus, char expectedBadEscapeChar) { BString expectedMessage; expectedMessage.SetToFormat("unexpected escaped character [%c] " "in string parsing", expectedBadEscapeChar); TestParseWithErrorMessage(input, line, expectedStatus, expectedMessage.String()); } void JsonErrorHandlingTest::TestParseWithUnterminatedElement(const char* input, int32 line, status_t expectedStatus) { BString expectedMessage; expectedMessage.SetToFormat("unterminated element"); TestParseWithErrorMessage(input, line, expectedStatus, expectedMessage.String()); } void JsonErrorHandlingTest::TestParseWithUnexpectedCharacter(const char* input, int32 line, status_t expectedStatus, char expectedChar) { BString expectedMessage; expectedMessage.SetToFormat("unexpected character [%" B_PRIu8 "] (%c) when parsing element", static_cast(expectedChar), expectedChar); TestParseWithErrorMessage(input, line, expectedStatus, expectedMessage.String()); } void JsonErrorHandlingTest::TestParseWithErrorMessage(const char* input, int32 line, status_t expectedStatus, const char* expectedMessage) { fprintf(stderr, "in >%s<\n", input); BDataIO *inputData = new BMemoryIO(input, strlen(input)); ObjectDeleter inputDataDeleter(inputData); TestParseWithErrorMessage(inputData, line, expectedStatus, expectedMessage); } void JsonErrorHandlingTest::TestParseWithErrorMessage(BDataIO* inputData, int32 line, status_t expectedStatus, const char* expectedMessage) { ErrorCapturingListener* listener = new ErrorCapturingListener(); ObjectDeleter listenerDeleter(listener); // ---------------------- BPrivate::BJson::Parse(inputData, listener); // ---------------------- fprintf(stderr, "expected error at line %" B_PRIi32 " - %s : %s\n", line, strerror(expectedStatus), expectedMessage); fprintf(stderr, "actual error at line %" B_PRIi32 " - %s : %s\n", listener->GetErrorLine(), strerror(listener->ErrorStatus()), listener->GetErrorMessage().String()); if (listener->HasEventsAfterError()) { fprintf(stderr, "first event after error [%d]\n", listener->FirstEventTypeAfterError()); } CPPUNIT_ASSERT(!listener->HasEventsAfterError()); CPPUNIT_ASSERT_EQUAL(expectedStatus, listener->ErrorStatus()); CPPUNIT_ASSERT_EQUAL(line, listener->GetErrorLine()); CPPUNIT_ASSERT(0 == strcmp(expectedMessage, listener->GetErrorMessage().String())); } void JsonErrorHandlingTest::TestCompletelyUnknown() { TestParseWithUnexpectedCharacter( JSON_SAMPLE_BROKEN_COMPLETELY_UNKNOWN, 1, B_BAD_DATA, 'z'); } void JsonErrorHandlingTest::TestObjectWithPrematureSeparator() { TestParseWithErrorMessage(JSON_SAMPLE_BROKEN_OBJECT_PREMATURE_SEPARATOR, 1, B_BAD_DATA, "unexpected item separator when parsing start of object"); } void JsonErrorHandlingTest::TestStringUnterminated() { TestParseWithErrorMessage(JSON_SAMPLE_BROKEN_UNTERMINATED_STRING, 1, B_BAD_DATA, "unexpected end of input"); } void JsonErrorHandlingTest::TestBadStringEscape() { TestParseWithBadStringEscape( JSON_SAMPLE_BROKEN_BAD_STRING_ESCAPE, 1, B_BAD_DATA, 'v'); } void JsonErrorHandlingTest::TestBadNumber() { TestParseWithErrorMessage(JSON_SAMPLE_BROKEN_NUMBER, 1, B_BAD_DATA, "malformed number"); } void JsonErrorHandlingTest::TestIOIssue() { BDataIO *inputData = new FailingDataIO(); ObjectDeleter inputDataDeleter(inputData); TestParseWithErrorMessage(inputData, -1, B_IO_ERROR, "io related read error"); } /*static*/ void JsonErrorHandlingTest::AddTests(BTestSuite& parent) { CppUnit::TestSuite& suite = *new CppUnit::TestSuite( "JsonErrorHandlingTest"); suite.addTest(new CppUnit::TestCaller( "JsonErrorHandlingTest::TestCompletelyUnknown", &JsonErrorHandlingTest::TestCompletelyUnknown)); suite.addTest(new CppUnit::TestCaller( "JsonErrorHandlingTest::TestObjectWithPrematureSeparator", &JsonErrorHandlingTest::TestObjectWithPrematureSeparator)); suite.addTest(new CppUnit::TestCaller( "JsonErrorHandlingTest::TestStringUnterminated", &JsonErrorHandlingTest::TestStringUnterminated)); suite.addTest(new CppUnit::TestCaller( "JsonErrorHandlingTest::TestBadStringEscape", &JsonErrorHandlingTest::TestBadStringEscape)); suite.addTest(new CppUnit::TestCaller( "JsonErrorHandlingTest::TestBadNumber", &JsonErrorHandlingTest::TestBadNumber)); suite.addTest(new CppUnit::TestCaller( "JsonErrorHandlingTest::TestIOIssue", &JsonErrorHandlingTest::TestIOIssue)); parent.addTest("JsonErrorHandlingTest", &suite); }