HaikuDepot : Streaming Icon Meta-Data Parser

More background work for later performance improvements.
This change generalizes the parsing of meta-data from
JSON streams as similarly structured meta-data is
anticipated to be carried in other payloads.  Unit tests
have also been implemented to provide coverage on this
new functionality.
This commit is contained in:
Andrew Lindesay 2017-08-01 23:19:27 +02:00
parent 96251548d1
commit a1c3daa638
14 changed files with 788 additions and 124 deletions

View File

@ -1,6 +1,6 @@
SubDir HAIKU_TOP src apps haikudepot ;
UsePrivateHeaders interface shared package support net ;
UsePrivateHeaders interface shared storage package support net ;
# source directories
local sourceDirs =
@ -79,7 +79,8 @@ Application HaikuDepot :
ServerSettings.cpp
WebAppInterface.cpp
ServerIconExportUpdateProcess.cpp
IconMetaData.cpp
StandardMetaDataJsonEventListener.cpp
StandardMetaData.cpp
# tar
TarArchiveHeader.cpp
@ -123,4 +124,4 @@ Application TextDocumentTest :
$(textDocumentSources)
: be translation shared [ TargetLibsupc++ ]
;
;

View File

@ -1,58 +0,0 @@
/*
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "IconMetaData.h"
BDateTime
IconMetaData::_CreateDateTime(uint64_t millisSinceEpoc)
{
time_t secondsSinceEpoc = (millisSinceEpoc / 1000);
BDateTime result;
result.SetTime_t(secondsSinceEpoc);
return result;
}
uint64_t
IconMetaData::GetCreateTimestamp()
{
return fCreateTimestamp;
}
BDateTime
IconMetaData::GetCreateTimestampAsDateTime()
{
return _CreateDateTime(GetCreateTimestamp());
}
void
IconMetaData::SetCreateTimestamp(uint64_t value)
{
fCreateTimestamp = value;
}
uint64_t
IconMetaData::GetDataModifiedTimestamp()
{
return fDataModifiedTimestamp;
}
void
IconMetaData::SetDataModifiedTimestamp(uint64_t value)
{
fDataModifiedTimestamp = value;
}
BDateTime
IconMetaData::GetDataModifiedTimestampAsDateTime()
{
return _CreateDateTime(GetDataModifiedTimestamp());
}

View File

@ -11,6 +11,7 @@
#include <time.h>
#include <AutoDeleter.h>
#include <FileIO.h>
#include <HttpRequest.h>
#include <HttpTime.h>
#include <Json.h>
@ -19,6 +20,7 @@
#include <support/ZlibCompressionAlgorithm.h>
#include "ServerSettings.h"
#include "StandardMetaDataJsonEventListener.h"
#include "StorageUtils.h"
#include "TarArchiveService.h"
#include "ToFileUrlProtocolListener.h"
@ -80,7 +82,7 @@ ServerIconExportUpdateProcess::Run()
if (result == B_OK) {
if (0 != remove(tarGzFilePath.Path())) {
fprintf(stdout, "unable to delete the temporary tgz path; "
"%s", tarGzFilePath.Path());
"%s\n", tarGzFilePath.Path());
}
}
}
@ -108,7 +110,7 @@ ServerIconExportUpdateProcess::_IfModifiedSinceHeaderValue(BString& headerValue,
return B_FILE_NOT_FOUND;
}
IconMetaData iconMetaData;
StandardMetaData iconMetaData;
status_t result = _PopulateIconMetaData(iconMetaData, iconMetaDataPath);
if (result == B_OK) {
@ -120,6 +122,9 @@ ServerIconExportUpdateProcess::_IfModifiedSinceHeaderValue(BString& headerValue,
BPrivate::BHttpTime modifiedHttpTime(modifiedDateTime);
headerValue.SetTo(modifiedHttpTime
.ToString(BPrivate::B_HTTP_TIME_FORMAT_COOKIE));
} else {
fprintf(stderr, "unable to parse the icon meta-data date and time -"
" cannot set the 'If-Modified-Since' header\n");
}
return result;
@ -226,53 +231,33 @@ ServerIconExportUpdateProcess::_Download(BPath& tarGzFilePath, const BUrl& url,
status_t
ServerIconExportUpdateProcess::_PopulateIconMetaData(IconMetaData& iconMetaData,
BMessage& message) const
ServerIconExportUpdateProcess::_PopulateIconMetaData(
StandardMetaData& iconMetaData, BPath& path) const
{
status_t result = B_OK;
double value; // numeric resolution issue?
FILE *file = fopen(path.Path(), "rb");
if (result == B_OK)
result = message.FindDouble("createTimestamp", &value);
if (file == NULL) {
fprintf(stderr, "unable to find the icon meta data file at [%s]\n",
path.Path());
return B_FILE_NOT_FOUND;
}
if (result == B_OK)
iconMetaData.SetCreateTimestamp((uint64) value);
BFileIO iconMetaDataFile(file, true); // takes ownership
// the "$" here indicates that the data is at the top level.
StandardMetaDataJsonEventListener listener("$", iconMetaData);
BPrivate::BJson::Parse(&iconMetaDataFile, &listener);
if (result == B_OK)
result = message.FindDouble("dataModifiedTimestamp", &value);
status_t result = listener.ErrorStatus();
if (result == B_OK)
iconMetaData.SetDataModifiedTimestamp((uint64) value);
if (result != B_OK)
return result;
return result;
}
status_t
ServerIconExportUpdateProcess::_PopulateIconMetaData(IconMetaData& iconMetaData,
BString& jsonString) const
{
BMessage infoMetaDataMessage;
status_t result = BJson::Parse(jsonString, infoMetaDataMessage);
if (result == B_OK)
return _PopulateIconMetaData(iconMetaData, infoMetaDataMessage);
return result;
}
status_t
ServerIconExportUpdateProcess::_PopulateIconMetaData(IconMetaData& iconMetaData,
BPath& path) const
{
BString infoMetaDataStr;
status_t result = StorageUtils::AppendToString(path, infoMetaDataStr);
if (result == B_OK)
return _PopulateIconMetaData(iconMetaData, infoMetaDataStr);
return result;
if (!iconMetaData.IsPopulated()) {
fprintf(stderr, "the icon meta data was read from [%s], but no values "
"were extracted\n", path.Path());
return B_BAD_DATA;
}
return B_OK;
}

View File

@ -14,7 +14,7 @@
#include <String.h>
#include <Url.h>
#include "IconMetaData.h"
#include "StandardMetaData.h"
class ServerIconExportUpdateProcess : public AbstractServerProcess {
@ -37,14 +37,8 @@ private:
BPath& iconMetaDataPath) const;
status_t _PopulateIconMetaData(
IconMetaData& iconMetaData, BPath& path)
StandardMetaData& iconMetaData, BPath& path)
const;
status_t _PopulateIconMetaData(
IconMetaData& iconMetaData,
BString& jsonString) const;
status_t _PopulateIconMetaData(
IconMetaData& iconMetaData,
BMessage& message) const;
BString fBaseUrl;
BPath fLocalStorageDirectoryPath;

View File

@ -0,0 +1,71 @@
/*
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "StandardMetaData.h"
StandardMetaData::StandardMetaData()
{
fCreateTimestamp = 0;
fDataModifiedTimestamp = 0;
}
BDateTime
StandardMetaData::_CreateDateTime(uint64_t millisSinceEpoc)
{
time_t secondsSinceEpoc = (millisSinceEpoc / 1000);
BDateTime result;
result.SetTime_t(secondsSinceEpoc);
return result;
}
uint64_t
StandardMetaData::GetCreateTimestamp()
{
return fCreateTimestamp;
}
BDateTime
StandardMetaData::GetCreateTimestampAsDateTime()
{
return _CreateDateTime(GetCreateTimestamp());
}
void
StandardMetaData::SetCreateTimestamp(uint64_t value)
{
fCreateTimestamp = value;
}
uint64_t
StandardMetaData::GetDataModifiedTimestamp()
{
return fDataModifiedTimestamp;
}
void
StandardMetaData::SetDataModifiedTimestamp(uint64_t value)
{
fDataModifiedTimestamp = value;
}
BDateTime
StandardMetaData::GetDataModifiedTimestampAsDateTime()
{
return _CreateDateTime(GetDataModifiedTimestamp());
}
bool
StandardMetaData::IsPopulated()
{
return fCreateTimestamp != 0 && fDataModifiedTimestamp != 0;
}

View File

@ -2,10 +2,8 @@
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef ICON_META_DATA_H
#define ICON_META_DATA_H
#ifndef STANDARD_META_DATA_H
#define STANDARD_META_DATA_H
#include <DateTime.h>
#include <File.h>
@ -14,14 +12,17 @@
#include <String.h>
/* This class models (some of) the meta-data that is bundled into the tar file
* that is downloaded to the HaikuDepot client from the HaikuDepotServer
* application server system. The file is included in the tar-ball data.
/* This class models (some of) the meta-data that is bundled into data that is
* relayed from the HaikuDepotServer application server down to the client.
* This includes the tar-ball of icons as well as "bulk data" such as streams of
* information about repositories and packages.
*/
class IconMetaData {
class StandardMetaData {
public:
StandardMetaData();
uint64_t GetCreateTimestamp();
BDateTime GetCreateTimestampAsDateTime();
void SetCreateTimestamp(uint64_t value);
@ -31,6 +32,7 @@ public:
void SetDataModifiedTimestamp(
uint64_t value);
bool IsPopulated();
private:
BDateTime _CreateDateTime(
uint64_t millisSinceEpoc);
@ -39,4 +41,4 @@ private:
};
#endif // ICON_META_DATA_H
#endif // STANDARD_META_DATA_H

View File

@ -0,0 +1,408 @@
/*
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "StandardMetaDataJsonEventListener.h"
#include "stdio.h"
#define KEY_CREATE_TIMESTAMP "createTimestamp"
#define KEY_DATA_MODIFIED_TIMESTAMP "dataModifiedTimestamp"
class SmdStackedEventListener : public BJsonEventListener {
public:
SmdStackedEventListener(
StandardMetaDataJsonEventListener*
mainListener,
SmdStackedEventListener* parent);
~SmdStackedEventListener();
void HandleError(status_t status, int32 line,
const char* message);
void Complete();
status_t ErrorStatus();
SmdStackedEventListener*
Parent();
StandardMetaData*
MetaData();
protected:
void SetStackedListenerMainListener(
SmdStackedEventListener* stackedListener);
StandardMetaDataJsonEventListener*
fMainListener;
SmdStackedEventListener*
fParent;
};
class SmdStackedArrayEventListener : public SmdStackedEventListener {
public:
SmdStackedArrayEventListener(
StandardMetaDataJsonEventListener*
mainListener,
SmdStackedEventListener* parent);
~SmdStackedArrayEventListener();
bool Handle(const BJsonEvent& event);
};
class SmdStackedObjectMessageEventListener : public SmdStackedEventListener {
public:
SmdStackedObjectMessageEventListener(
BStringList* jsonPathObjectNames,
StandardMetaDataJsonEventListener*
mainListener,
SmdStackedEventListener* parent);
~SmdStackedObjectMessageEventListener();
bool Handle(const BJsonEvent& event);
private:
BStringList* fJsonPathObjectNames;
BString fNextItemName;
};
// #pragma mark - SmdStackedEventListener
SmdStackedEventListener::SmdStackedEventListener(
StandardMetaDataJsonEventListener* mainListener,
SmdStackedEventListener* parent)
{
fMainListener = mainListener;
fParent = parent;
}
SmdStackedEventListener::~SmdStackedEventListener()
{
}
void
SmdStackedEventListener::HandleError(status_t status, int32 line,
const char* message)
{
fMainListener->HandleError(status, line, message);
}
void
SmdStackedEventListener::Complete()
{
fMainListener->Complete();
}
status_t
SmdStackedEventListener::ErrorStatus()
{
return fMainListener->ErrorStatus();
}
StandardMetaData*
SmdStackedEventListener::MetaData()
{
return fMainListener->MetaData();
}
SmdStackedEventListener*
SmdStackedEventListener::Parent()
{
return fParent;
}
void
SmdStackedEventListener::SetStackedListenerMainListener(
SmdStackedEventListener* stackedListener)
{
fMainListener->SetStackedListener(stackedListener);
}
// #pragma mark - SmdStackedArrayEventListener
SmdStackedArrayEventListener::SmdStackedArrayEventListener(
StandardMetaDataJsonEventListener* mainListener,
SmdStackedEventListener* parent)
:
SmdStackedEventListener(mainListener, parent)
{
}
SmdStackedArrayEventListener::~SmdStackedArrayEventListener()
{
}
bool
SmdStackedArrayEventListener::Handle(const BJsonEvent& event)
{
if (ErrorStatus() != B_OK)
return false;
switch (event.EventType()) {
case B_JSON_NUMBER:
case B_JSON_STRING:
case B_JSON_TRUE:
case B_JSON_FALSE:
case B_JSON_NULL:
// ignore these atomic types in an array.
break;
case B_JSON_OBJECT_START:
SetStackedListenerMainListener(
new SmdStackedObjectMessageEventListener(NULL, fMainListener,
this));
break;
case B_JSON_ARRAY_START:
SetStackedListenerMainListener(new SmdStackedArrayEventListener(
fMainListener, this));
break;
case B_JSON_ARRAY_END:
SetStackedListenerMainListener(fParent);
delete this;
break;
case B_JSON_OBJECT_END:
case B_JSON_OBJECT_NAME:
HandleError(B_NOT_ALLOWED, JSON_EVENT_LISTENER_ANY_LINE,
"illegal state when processing events in an array");
return false;
}
return true;
}
// #pragma mark - SmdStackedObjectMessageEventListener
SmdStackedObjectMessageEventListener::SmdStackedObjectMessageEventListener(
BStringList* jsonPathObjectNames,
StandardMetaDataJsonEventListener* mainListener,
SmdStackedEventListener* parent)
:
SmdStackedEventListener(mainListener, parent)
{
fJsonPathObjectNames = jsonPathObjectNames;
}
SmdStackedObjectMessageEventListener::~SmdStackedObjectMessageEventListener()
{
if (fJsonPathObjectNames != NULL)
delete fJsonPathObjectNames;
}
bool
SmdStackedObjectMessageEventListener::Handle(const BJsonEvent& event)
{
if (ErrorStatus() != B_OK)
return false;
switch (event.EventType()) {
case B_JSON_OBJECT_NAME:
fNextItemName = event.Content();
break;
case B_JSON_NUMBER:
if (fJsonPathObjectNames != NULL
&& fJsonPathObjectNames->IsEmpty()) {
if (fNextItemName == KEY_CREATE_TIMESTAMP) {
MetaData()->SetCreateTimestamp(
event.ContentInteger());
}
if (fNextItemName == KEY_DATA_MODIFIED_TIMESTAMP) {
MetaData()->SetDataModifiedTimestamp(
event.ContentInteger());
}
}
break;
case B_JSON_STRING:
case B_JSON_TRUE:
case B_JSON_FALSE:
case B_JSON_NULL:
// ignore these atomic types as they are not required to fill the
// data structure.
break;
case B_JSON_OBJECT_START:
{
BStringList* nextJsonPathObjectNames = NULL;
// if this next object is on the path then remove it from the
// path and carry on down. If it's not on the path then just
// drop the path from the next object.
if (fJsonPathObjectNames != NULL
&& !fJsonPathObjectNames->IsEmpty()
&& fNextItemName == fJsonPathObjectNames->StringAt(0)) {
nextJsonPathObjectNames = new BStringList(*fJsonPathObjectNames);
nextJsonPathObjectNames->Remove(0);
}
SetStackedListenerMainListener(
new SmdStackedObjectMessageEventListener(nextJsonPathObjectNames,
fMainListener, this));
break;
}
case B_JSON_ARRAY_START:
SetStackedListenerMainListener(new SmdStackedArrayEventListener(
fMainListener, this));
break;
case B_JSON_OBJECT_END:
SetStackedListenerMainListener(fParent);
delete this;
break;
case B_JSON_ARRAY_END:
HandleError(B_NOT_ALLOWED, JSON_EVENT_LISTENER_ANY_LINE,
"illegal state when processing events in an array");
return false;
}
return true;
}
// #pragma mark - StandardMetaDataJsonEventListener
StandardMetaDataJsonEventListener::StandardMetaDataJsonEventListener(
const BString& jsonPath,
StandardMetaData& metaData)
{
fMetaData = &metaData;
SetJsonPath(jsonPath);
fErrorStatus = B_OK;
fStackedListener = NULL;
}
StandardMetaDataJsonEventListener::~StandardMetaDataJsonEventListener()
{
if (fStackedListener != NULL)
delete fStackedListener;
}
void
StandardMetaDataJsonEventListener::SetJsonPath(const BString& jsonPath)
{
jsonPath.Split(".", true, fJsonPathObjectNames);
if (fJsonPathObjectNames.IsEmpty()) {
HandleError(B_BAD_VALUE, JSON_EVENT_LISTENER_ANY_LINE,
"json path required");
} else {
if (fJsonPathObjectNames.First() != "$") {
HandleError(B_BAD_VALUE, JSON_EVENT_LISTENER_ANY_LINE,
"illegal json path; should start with '$");
}
}
}
void
StandardMetaDataJsonEventListener::SetStackedListener(
SmdStackedEventListener *listener)
{
fStackedListener = listener;
}
bool
StandardMetaDataJsonEventListener::Handle(const BJsonEvent& event)
{
if (fErrorStatus != B_OK)
return false;
// best to exit early if the parsing has obtained all of the required
// data already.
if (fMetaData->IsPopulated())
return false;
if (fStackedListener != NULL)
return fStackedListener->Handle(event);
// the first thing that comes in must be an object container. It is
// bad data if it does not start with an object container.
switch (event.EventType()) {
case B_JSON_OBJECT_START:
{
BStringList* jsonPathObjectNames = new BStringList(
fJsonPathObjectNames);
jsonPathObjectNames->Remove(0);
SetStackedListener(
new SmdStackedObjectMessageEventListener(
jsonPathObjectNames, this, NULL)
);
}
break;
default:
HandleError(B_BAD_DATA, JSON_EVENT_LISTENER_ANY_LINE,
"the top level element must be an object");
return false;
}
return true;
}
void
StandardMetaDataJsonEventListener::HandleError(status_t status, int32 line,
const char* message)
{
fprintf(stderr, "an error has arisen processing the standard "
"meta data; %s\n", message);
fErrorStatus = status;
}
void
StandardMetaDataJsonEventListener::Complete()
{
}
status_t
StandardMetaDataJsonEventListener::ErrorStatus()
{
return fErrorStatus;
}
StandardMetaData*
StandardMetaDataJsonEventListener::MetaData()
{
return fMetaData;
}

View File

@ -0,0 +1,45 @@
/*
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef STANDARD_META_DATA_JSON_EVENT_LISTENER_H
#define STANDARD_META_DATA_JSON_EVENT_LISTENER_H
#include "StandardMetaData.h"
#include <JsonEventListener.h>
#include <String.h>
#include <StringList.h>
class SmdStackedEventListener;
class StandardMetaDataJsonEventListener : public BJsonEventListener {
friend class SmdStackedEventListener;
public:
StandardMetaDataJsonEventListener(
const BString& jsonPath,
StandardMetaData& metaData);
virtual ~StandardMetaDataJsonEventListener();
bool Handle(const BJsonEvent& event);
void HandleError(status_t status, int32 line,
const char* message);
void Complete();
status_t ErrorStatus();
protected:
void SetStackedListener(SmdStackedEventListener *listener);
private:
void SetJsonPath(const BString& jsonPath);
StandardMetaData* MetaData();
BStringList fJsonPathObjectNames;
StandardMetaData* fMetaData;
status_t fErrorStatus;
SmdStackedEventListener*
fStackedListener;
};
#endif // STANDARD_META_DATA_JSON_EVENT_LISTENER_H

View File

@ -387,7 +387,7 @@ BJson::ParseObject(JsonParseContext& jsonParseContext)
} else {
jsonParseContext.Listener()->HandleError(B_BAD_DATA,
jsonParseContext.LineNumber(), "expected"
"separator when parsing an object");
" separator when parsing an object");
}
}
}
@ -446,7 +446,7 @@ BJson::ParseArray(JsonParseContext& jsonParseContext)
} else {
jsonParseContext.Listener()->HandleError(B_BAD_DATA,
jsonParseContext.LineNumber(), "expected"
"separator when parsing an array");
" separator when parsing an array");
}
}
}

View File

@ -2,6 +2,7 @@ SubDir HAIKU_TOP src tests apps ;
SubInclude HAIKU_TOP src tests apps delay_shutdown ;
SubInclude HAIKU_TOP src tests apps fake_app_server ;
SubInclude HAIKU_TOP src tests apps haikudepot ;
SubInclude HAIKU_TOP src tests apps installer ;
SubInclude HAIKU_TOP src tests apps miniterminal ;
SubInclude HAIKU_TOP src tests apps partitioner ;

View File

@ -0,0 +1,20 @@
/*
* Copyright 2017, Andrew Lindesay, apl@lindesay.co.nz
* Distributed under the terms of the MIT License.
*/
#include <TestSuite.h>
#include <TestSuiteAddon.h>
#include "StandardMetaDataJsonEventListenerTest.h"
BTestSuite*
getTestSuite()
{
BTestSuite* suite = new BTestSuite("HaikuDepot");
StandardMetaDataJsonEventListenerTest::AddTests(*suite);
return suite;
}

View File

@ -0,0 +1,21 @@
SubDir HAIKU_TOP src tests apps haikudepot ;
SetSubDirSupportedPlatformsBeOSCompatible ;
AddSubDirSupportedPlatforms libbe_test ;
SubDirHdrs [ FDirName $(HAIKU_TOP) src apps haikudepot server ] ;
UsePrivateHeaders shared ;
UnitTestLib haikudepottest.so :
HaikuDepotTestAddon.cpp
StandardMetaData.cpp
StandardMetaDataJsonEventListener.cpp
StandardMetaDataJsonEventListenerTest.cpp
: be shared bnetapi [ TargetLibstdc++ ] [ TargetLibsupc++ ]
;
SEARCH on [ FGristFiles StandardMetaData.cpp StandardMetaDataJsonEventListener.cpp ]
= [ FDirName $(HAIKU_TOP) src apps haikudepot server ] ;

View File

@ -0,0 +1,139 @@
/*
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "StandardMetaData.h"
#include "StandardMetaDataJsonEventListener.h"
#include "StandardMetaDataJsonEventListenerTest.h"
#include <stdio.h>
#include <AutoDeleter.h>
#include <Json.h>
#include <cppunit/TestCaller.h>
#include <cppunit/TestSuite.h>
#define INPUT_TOP_LEVEL "{\"createTimestamp\":123456," \
"\"dataModifiedTimestamp\":789012}"
// This example is from the HDS server and comes back as part of the icons
// bundle; real-world example.
#define INPUT_ICON "{\"createTimestamp\":1501456139480," \
"\"createTimestampIso\":\"2017-07-30 23:08:59\"," \
"\"dataModifiedTimestamp\":1500536391000," \
"\"dataModifiedTimestampIso\":\"2017-07-20 07:39:51\"," \
"\"agent\":\"hds\"," \
"\"agentVersion\":\"1.0.86\"" \
"}"
#define INPUT_THIRD_LEVEL "{" \
"\"createTimestamp\":99999," \
"\"gonk\":{\"zink\":[ 123, { \"a\":\"b\" } ]}\n," \
"\"rink\":{" \
"\"tonk\":{" \
"\"createTimestamp\":665544," \
"\"dataModifiedTimestamp\":554433" \
"}" \
"}," \
"\"cake\": { \"type\": \"bananas\" }," \
"\"createTimestamp\":99999," \
"}"
#define INPUT_BROKEN "{\"broken\",,"
StandardMetaDataJsonEventListenerTest::StandardMetaDataJsonEventListenerTest()
{
}
StandardMetaDataJsonEventListenerTest::~StandardMetaDataJsonEventListenerTest()
{
}
void
StandardMetaDataJsonEventListenerTest::TestGeneric(
const char* input, const char* jsonPath, status_t expectedStatus,
uint64_t expectedCreateTimestamp, uint64_t expectedDataModifiedTimestamp)
{
StandardMetaData metaData;
StandardMetaDataJsonEventListener listener(jsonPath, metaData);
BDataIO* inputData = new BMemoryIO(input, strlen(input));
ObjectDeleter<BDataIO> inputDataDeleter(inputData);
BMallocIO* outputData = new BMallocIO();
ObjectDeleter<BMallocIO> outputDataDeleter(outputData);
// ----------------------
BPrivate::BJson::Parse(inputData, &listener);
// ----------------------
CPPUNIT_ASSERT_EQUAL(expectedStatus, listener.ErrorStatus());
CPPUNIT_ASSERT_EQUAL(expectedCreateTimestamp,
metaData.GetCreateTimestamp());
CPPUNIT_ASSERT_EQUAL(expectedDataModifiedTimestamp,
metaData.GetDataModifiedTimestamp());
}
void
StandardMetaDataJsonEventListenerTest::TestTopLevel()
{
TestGeneric(INPUT_TOP_LEVEL, "$", B_OK, 123456L, 789012L);
}
void
StandardMetaDataJsonEventListenerTest::TestIcon()
{
TestGeneric(INPUT_ICON, "$", B_OK, 1501456139480ULL, 1500536391000ULL);
}
void
StandardMetaDataJsonEventListenerTest::TestThirdLevel()
{
TestGeneric(INPUT_THIRD_LEVEL, "$.rink.tonk", B_OK, 665544L, 554433L);
}
void
StandardMetaDataJsonEventListenerTest::TestBroken()
{
TestGeneric(INPUT_BROKEN, "$", B_BAD_DATA, 0L, 0L);
}
/*static*/ void
StandardMetaDataJsonEventListenerTest::AddTests(BTestSuite& parent)
{
CppUnit::TestSuite& suite = *new CppUnit::TestSuite(
"StandardMetaDataJsonEventListenerTest");
suite.addTest(
new CppUnit::TestCaller<StandardMetaDataJsonEventListenerTest>(
"StandardMetaDataJsonEventListenerTest::TestTopLevel",
&StandardMetaDataJsonEventListenerTest::TestTopLevel));
suite.addTest(
new CppUnit::TestCaller<StandardMetaDataJsonEventListenerTest>(
"StandardMetaDataJsonEventListenerTest::TestIcon",
&StandardMetaDataJsonEventListenerTest::TestIcon));
suite.addTest(
new CppUnit::TestCaller<StandardMetaDataJsonEventListenerTest>(
"StandardMetaDataJsonEventListenerTest::TestThirdLevel",
&StandardMetaDataJsonEventListenerTest::TestThirdLevel));
suite.addTest(
new CppUnit::TestCaller<StandardMetaDataJsonEventListenerTest>(
"StandardMetaDataJsonEventListenerTest::TestBroken",
&StandardMetaDataJsonEventListenerTest::TestBroken));
parent.addTest("StandardMetaDataJsonEventListenerTest", &suite);
}

View File

@ -0,0 +1,35 @@
/*
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>
* Distributed under the terms of the MIT License.
*/
#ifndef STANDARD_META_DATA_JSON_EVENT_LISTENER_TEST_H
#define STANDARD_META_DATA_JSON_EVENT_LISTENER_TEST_H
#include <TestCase.h>
#include <TestSuite.h>
class StandardMetaDataJsonEventListenerTest : public CppUnit::TestCase {
public:
StandardMetaDataJsonEventListenerTest();
virtual ~StandardMetaDataJsonEventListenerTest();
void TestTopLevel();
void TestIcon();
void TestThirdLevel();
void TestBroken();
static void AddTests(BTestSuite& suite);
private:
void TestGeneric(const char* input,
const char* jsonPath,
status_t expectedStatus,
uint64_t expectedCreateTimestamp,
uint64_t expectedDataModifiedTimestamp);
};
#endif // STANDARD_META_DATA_JSON_EVENT_LISTENER_TEST_H