diff --git a/src/apps/haikudepot/Jamfile b/src/apps/haikudepot/Jamfile index 69855664ec..182dc0e92d 100644 --- a/src/apps/haikudepot/Jamfile +++ b/src/apps/haikudepot/Jamfile @@ -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++ ] -; +; \ No newline at end of file diff --git a/src/apps/haikudepot/server/IconMetaData.cpp b/src/apps/haikudepot/server/IconMetaData.cpp deleted file mode 100644 index e7d65ec043..0000000000 --- a/src/apps/haikudepot/server/IconMetaData.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2017, Andrew Lindesay . - * 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()); -} - diff --git a/src/apps/haikudepot/server/ServerIconExportUpdateProcess.cpp b/src/apps/haikudepot/server/ServerIconExportUpdateProcess.cpp index c7d8de14d4..6466b3e04e 100644 --- a/src/apps/haikudepot/server/ServerIconExportUpdateProcess.cpp +++ b/src/apps/haikudepot/server/ServerIconExportUpdateProcess.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -19,6 +20,7 @@ #include #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; } diff --git a/src/apps/haikudepot/server/ServerIconExportUpdateProcess.h b/src/apps/haikudepot/server/ServerIconExportUpdateProcess.h index d1b0a39e28..9bcc67cff7 100644 --- a/src/apps/haikudepot/server/ServerIconExportUpdateProcess.h +++ b/src/apps/haikudepot/server/ServerIconExportUpdateProcess.h @@ -14,7 +14,7 @@ #include #include -#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; diff --git a/src/apps/haikudepot/server/StandardMetaData.cpp b/src/apps/haikudepot/server/StandardMetaData.cpp new file mode 100644 index 0000000000..5da9acb7d1 --- /dev/null +++ b/src/apps/haikudepot/server/StandardMetaData.cpp @@ -0,0 +1,71 @@ +/* + * Copyright 2017, Andrew Lindesay . + * 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; +} diff --git a/src/apps/haikudepot/server/IconMetaData.h b/src/apps/haikudepot/server/StandardMetaData.h similarity index 66% rename from src/apps/haikudepot/server/IconMetaData.h rename to src/apps/haikudepot/server/StandardMetaData.h index aef6e836c5..0b1ed9057b 100644 --- a/src/apps/haikudepot/server/IconMetaData.h +++ b/src/apps/haikudepot/server/StandardMetaData.h @@ -2,10 +2,8 @@ * Copyright 2017, Andrew Lindesay . * 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 #include @@ -14,14 +12,17 @@ #include -/* 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 diff --git a/src/apps/haikudepot/server/StandardMetaDataJsonEventListener.cpp b/src/apps/haikudepot/server/StandardMetaDataJsonEventListener.cpp new file mode 100644 index 0000000000..dbdb5dda0d --- /dev/null +++ b/src/apps/haikudepot/server/StandardMetaDataJsonEventListener.cpp @@ -0,0 +1,408 @@ +/* + * Copyright 2017, Andrew Lindesay . + * 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; +} \ No newline at end of file diff --git a/src/apps/haikudepot/server/StandardMetaDataJsonEventListener.h b/src/apps/haikudepot/server/StandardMetaDataJsonEventListener.h new file mode 100644 index 0000000000..7d4be36d2f --- /dev/null +++ b/src/apps/haikudepot/server/StandardMetaDataJsonEventListener.h @@ -0,0 +1,45 @@ +/* + * Copyright 2017, Andrew Lindesay . + * 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 +#include +#include + +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 \ No newline at end of file diff --git a/src/kits/shared/Json.cpp b/src/kits/shared/Json.cpp index 3784740f3f..9e34d234da 100644 --- a/src/kits/shared/Json.cpp +++ b/src/kits/shared/Json.cpp @@ -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"); } } } diff --git a/src/tests/apps/Jamfile b/src/tests/apps/Jamfile index 35b936da9c..e14d14cfdc 100644 --- a/src/tests/apps/Jamfile +++ b/src/tests/apps/Jamfile @@ -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 ; diff --git a/src/tests/apps/haikudepot/HaikuDepotTestAddon.cpp b/src/tests/apps/haikudepot/HaikuDepotTestAddon.cpp new file mode 100644 index 0000000000..f996a93a58 --- /dev/null +++ b/src/tests/apps/haikudepot/HaikuDepotTestAddon.cpp @@ -0,0 +1,20 @@ +/* + * Copyright 2017, Andrew Lindesay, apl@lindesay.co.nz + * Distributed under the terms of the MIT License. + */ + +#include +#include + +#include "StandardMetaDataJsonEventListenerTest.h" + + +BTestSuite* +getTestSuite() +{ + BTestSuite* suite = new BTestSuite("HaikuDepot"); + + StandardMetaDataJsonEventListenerTest::AddTests(*suite); + + return suite; +} diff --git a/src/tests/apps/haikudepot/Jamfile b/src/tests/apps/haikudepot/Jamfile new file mode 100644 index 0000000000..b621a12dfa --- /dev/null +++ b/src/tests/apps/haikudepot/Jamfile @@ -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 ] ; \ No newline at end of file diff --git a/src/tests/apps/haikudepot/StandardMetaDataJsonEventListenerTest.cpp b/src/tests/apps/haikudepot/StandardMetaDataJsonEventListenerTest.cpp new file mode 100644 index 0000000000..b08a4d5287 --- /dev/null +++ b/src/tests/apps/haikudepot/StandardMetaDataJsonEventListenerTest.cpp @@ -0,0 +1,139 @@ +/* + * Copyright 2017, Andrew Lindesay . + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#include "StandardMetaData.h" +#include "StandardMetaDataJsonEventListener.h" +#include "StandardMetaDataJsonEventListenerTest.h" + +#include + +#include +#include + +#include +#include + + +#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 inputDataDeleter(inputData); + BMallocIO* outputData = new BMallocIO(); + ObjectDeleter 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::TestTopLevel", + &StandardMetaDataJsonEventListenerTest::TestTopLevel)); + + suite.addTest( + new CppUnit::TestCaller( + "StandardMetaDataJsonEventListenerTest::TestIcon", + &StandardMetaDataJsonEventListenerTest::TestIcon)); + + suite.addTest( + new CppUnit::TestCaller( + "StandardMetaDataJsonEventListenerTest::TestThirdLevel", + &StandardMetaDataJsonEventListenerTest::TestThirdLevel)); + + suite.addTest( + new CppUnit::TestCaller( + "StandardMetaDataJsonEventListenerTest::TestBroken", + &StandardMetaDataJsonEventListenerTest::TestBroken)); + + parent.addTest("StandardMetaDataJsonEventListenerTest", &suite); +} \ No newline at end of file diff --git a/src/tests/apps/haikudepot/StandardMetaDataJsonEventListenerTest.h b/src/tests/apps/haikudepot/StandardMetaDataJsonEventListenerTest.h new file mode 100644 index 0000000000..2ba49ff8d9 --- /dev/null +++ b/src/tests/apps/haikudepot/StandardMetaDataJsonEventListenerTest.h @@ -0,0 +1,35 @@ +/* + * Copyright 2017, Andrew Lindesay + * 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 +#include + + +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