mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'feature/scripting-storage'
This commit is contained in:
commit
292ae8dcf8
|
@ -55,6 +55,7 @@ if(NOT LIBMGBA_ONLY)
|
||||||
set(USE_SQLITE3 ON CACHE BOOL "Whether or not to enable SQLite3 support")
|
set(USE_SQLITE3 ON CACHE BOOL "Whether or not to enable SQLite3 support")
|
||||||
set(USE_ELF ON CACHE BOOL "Whether or not to enable ELF support")
|
set(USE_ELF ON CACHE BOOL "Whether or not to enable ELF support")
|
||||||
set(USE_LUA ON CACHE BOOL "Whether or not to enable Lua scripting support")
|
set(USE_LUA ON CACHE BOOL "Whether or not to enable Lua scripting support")
|
||||||
|
set(USE_JSON_C ON CACHE BOOL "Whether or not to enable JSON-C support")
|
||||||
set(M_CORE_GBA ON CACHE BOOL "Build Game Boy Advance core")
|
set(M_CORE_GBA ON CACHE BOOL "Build Game Boy Advance core")
|
||||||
set(M_CORE_GB ON CACHE BOOL "Build Game Boy core")
|
set(M_CORE_GB ON CACHE BOOL "Build Game Boy core")
|
||||||
set(USE_LZMA ON CACHE BOOL "Whether or not to enable 7-Zip support")
|
set(USE_LZMA ON CACHE BOOL "Whether or not to enable 7-Zip support")
|
||||||
|
@ -477,6 +478,7 @@ endif()
|
||||||
if(DISABLE_DEPS)
|
if(DISABLE_DEPS)
|
||||||
set(USE_GDB_STUB OFF)
|
set(USE_GDB_STUB OFF)
|
||||||
set(USE_DISCORD_RPC OFF)
|
set(USE_DISCORD_RPC OFF)
|
||||||
|
set(USE_JSON_C OFF)
|
||||||
set(USE_SQLITE3 OFF)
|
set(USE_SQLITE3 OFF)
|
||||||
set(USE_PNG OFF)
|
set(USE_PNG OFF)
|
||||||
set(USE_ZLIB OFF)
|
set(USE_ZLIB OFF)
|
||||||
|
@ -765,11 +767,30 @@ endif()
|
||||||
|
|
||||||
if(ENABLE_SCRIPTING)
|
if(ENABLE_SCRIPTING)
|
||||||
list(APPEND ENABLES SCRIPTING)
|
list(APPEND ENABLES SCRIPTING)
|
||||||
|
find_feature(USE_JSON_C "json-c")
|
||||||
if(NOT USE_LUA VERSION_LESS 5.1)
|
if(NOT USE_LUA VERSION_LESS 5.1)
|
||||||
find_feature(USE_LUA "Lua" ${USE_LUA})
|
find_feature(USE_LUA "Lua" ${USE_LUA})
|
||||||
else()
|
else()
|
||||||
find_feature(USE_LUA "Lua")
|
find_feature(USE_LUA "Lua")
|
||||||
endif()
|
endif()
|
||||||
|
if(USE_JSON_C)
|
||||||
|
list(APPEND FEATURES JSON_C)
|
||||||
|
if(TARGET json-c::json-c)
|
||||||
|
list(APPEND DEPENDENCY_LIB json-c::json-c)
|
||||||
|
get_target_property(JSON_C_SONAME json-c::json-c IMPORTED_SONAME_NONE)
|
||||||
|
string(SUBSTRING "${JSON_C_SONAME}" 13 -1 JSON_C_SOVER)
|
||||||
|
else()
|
||||||
|
if(${json-c_VERSION} VERSION_LESS 0.13.0)
|
||||||
|
set(JSON_C_SOVER 3)
|
||||||
|
elseif(${json-c_VERSION} VERSION_LESS 0.15.0)
|
||||||
|
set(JSON_C_SOVER 4)
|
||||||
|
endif()
|
||||||
|
list(APPEND DEPENDENCY_LIB ${json-c_LIBRARIES})
|
||||||
|
include_directories(AFTER ${json-c_INCLUDE_DIRS})
|
||||||
|
link_directories(${json-c_LIBDIRS})
|
||||||
|
endif()
|
||||||
|
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libjson-c${JSON_C_SOVER}")
|
||||||
|
endif()
|
||||||
if(USE_LUA)
|
if(USE_LUA)
|
||||||
list(APPEND FEATURE_DEFINES USE_LUA)
|
list(APPEND FEATURE_DEFINES USE_LUA)
|
||||||
include_directories(AFTER ${LUA_INCLUDE_DIR})
|
include_directories(AFTER ${LUA_INCLUDE_DIR})
|
||||||
|
@ -1289,6 +1310,7 @@ if(NOT QUIET AND NOT LIBMGBA_ONLY)
|
||||||
else()
|
else()
|
||||||
message(STATUS " Lua: ${USE_LUA}")
|
message(STATUS " Lua: ${USE_LUA}")
|
||||||
endif()
|
endif()
|
||||||
|
message(STATUS " storage API: ${USE_JSON_C}")
|
||||||
endif()
|
endif()
|
||||||
message(STATUS "Frontends:")
|
message(STATUS "Frontends:")
|
||||||
message(STATUS " Qt: ${BUILD_QT}")
|
message(STATUS " Qt: ${BUILD_QT}")
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
/* Copyright (c) 2013-2023 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#ifndef M_SCRIPT_STORAGE_H
|
||||||
|
#define M_SCRIPT_STORAGE_H
|
||||||
|
|
||||||
|
#include <mgba-util/common.h>
|
||||||
|
|
||||||
|
CXX_GUARD_START
|
||||||
|
|
||||||
|
#include <mgba/script/context.h>
|
||||||
|
#include <mgba/script/macros.h>
|
||||||
|
|
||||||
|
struct VFile;
|
||||||
|
void mScriptContextAttachStorage(struct mScriptContext* context);
|
||||||
|
void mScriptStorageFlushAll(struct mScriptContext* context);
|
||||||
|
|
||||||
|
bool mScriptStorageSaveBucket(struct mScriptContext* context, const char* bucket);
|
||||||
|
bool mScriptStorageSaveBucketVF(struct mScriptContext* context, const char* bucket, struct VFile* vf);
|
||||||
|
bool mScriptStorageLoadBucket(struct mScriptContext* context, const char* bucket);
|
||||||
|
bool mScriptStorageLoadBucketVF(struct mScriptContext* context, const char* bucket, struct VFile* vf);
|
||||||
|
void mScriptStorageGetBucketPath(const char* bucket, char* out);
|
||||||
|
|
||||||
|
CXX_GUARD_END
|
||||||
|
|
||||||
|
#endif
|
|
@ -79,6 +79,10 @@
|
||||||
#cmakedefine USE_GDB_STUB
|
#cmakedefine USE_GDB_STUB
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef USE_JSON_C
|
||||||
|
#cmakedefine USE_JSON_C
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef USE_LIBAV
|
#ifndef USE_LIBAV
|
||||||
#cmakedefine USE_LIBAV
|
#cmakedefine USE_LIBAV
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include "scripting/ScriptingTextBufferModel.h"
|
#include "scripting/ScriptingTextBufferModel.h"
|
||||||
|
|
||||||
#include <mgba/script/input.h>
|
#include <mgba/script/input.h>
|
||||||
|
#include <mgba/script/storage.h>
|
||||||
#include <mgba-util/math.h>
|
#include <mgba-util/math.h>
|
||||||
#include <mgba-util/string.h>
|
#include <mgba-util/string.h>
|
||||||
|
|
||||||
|
@ -51,6 +52,9 @@ ScriptingController::ScriptingController(QObject* parent)
|
||||||
m_bufferModel = new ScriptingTextBufferModel(this);
|
m_bufferModel = new ScriptingTextBufferModel(this);
|
||||||
QObject::connect(m_bufferModel, &ScriptingTextBufferModel::textBufferCreated, this, &ScriptingController::textBufferCreated);
|
QObject::connect(m_bufferModel, &ScriptingTextBufferModel::textBufferCreated, this, &ScriptingController::textBufferCreated);
|
||||||
|
|
||||||
|
connect(&m_storageFlush, &QTimer::timeout, this, &ScriptingController::flushStorage);
|
||||||
|
m_storageFlush.setInterval(5);
|
||||||
|
|
||||||
mScriptGamepadInit(&m_gamepad);
|
mScriptGamepadInit(&m_gamepad);
|
||||||
|
|
||||||
init();
|
init();
|
||||||
|
@ -144,6 +148,10 @@ void ScriptingController::runCode(const QString& code) {
|
||||||
load(vf, "*prompt");
|
load(vf, "*prompt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ScriptingController::flushStorage() {
|
||||||
|
mScriptStorageFlushAll(&m_scriptContext);
|
||||||
|
}
|
||||||
|
|
||||||
bool ScriptingController::eventFilter(QObject* obj, QEvent* ev) {
|
bool ScriptingController::eventFilter(QObject* obj, QEvent* ev) {
|
||||||
event(obj, ev);
|
event(obj, ev);
|
||||||
return false;
|
return false;
|
||||||
|
@ -293,6 +301,7 @@ void ScriptingController::detachGamepad() {
|
||||||
void ScriptingController::init() {
|
void ScriptingController::init() {
|
||||||
mScriptContextInit(&m_scriptContext);
|
mScriptContextInit(&m_scriptContext);
|
||||||
mScriptContextAttachStdlib(&m_scriptContext);
|
mScriptContextAttachStdlib(&m_scriptContext);
|
||||||
|
mScriptContextAttachStorage(&m_scriptContext);
|
||||||
mScriptContextAttachSocket(&m_scriptContext);
|
mScriptContextAttachSocket(&m_scriptContext);
|
||||||
mScriptContextAttachInput(&m_scriptContext);
|
mScriptContextAttachInput(&m_scriptContext);
|
||||||
mScriptContextRegisterEngines(&m_scriptContext);
|
mScriptContextRegisterEngines(&m_scriptContext);
|
||||||
|
@ -308,6 +317,8 @@ void ScriptingController::init() {
|
||||||
if (m_engines.count() == 1) {
|
if (m_engines.count() == 1) {
|
||||||
m_activeEngine = *m_engines.begin();
|
m_activeEngine = *m_engines.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_storageFlush.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t ScriptingController::qtToScriptingKey(const QKeyEvent* event) {
|
uint32_t ScriptingController::qtToScriptingKey(const QKeyEvent* event) {
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
#include <mgba/script/context.h>
|
#include <mgba/script/context.h>
|
||||||
#include <mgba/script/input.h>
|
#include <mgba/script/input.h>
|
||||||
|
@ -55,6 +56,8 @@ public slots:
|
||||||
void reset();
|
void reset();
|
||||||
void runCode(const QString& code);
|
void runCode(const QString& code);
|
||||||
|
|
||||||
|
void flushStorage();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool eventFilter(QObject*, QEvent*) override;
|
bool eventFilter(QObject*, QEvent*) override;
|
||||||
|
|
||||||
|
@ -84,6 +87,8 @@ private:
|
||||||
|
|
||||||
std::shared_ptr<CoreController> m_controller;
|
std::shared_ptr<CoreController> m_controller;
|
||||||
InputController* m_inputController = nullptr;
|
InputController* m_inputController = nullptr;
|
||||||
|
|
||||||
|
QTimer m_storageFlush;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,12 +4,17 @@ set(SOURCE_FILES
|
||||||
input.c
|
input.c
|
||||||
socket.c
|
socket.c
|
||||||
stdlib.c
|
stdlib.c
|
||||||
|
storage.c
|
||||||
types.c)
|
types.c)
|
||||||
|
|
||||||
set(TEST_FILES
|
set(TEST_FILES
|
||||||
test/classes.c
|
test/classes.c
|
||||||
test/types.c)
|
test/types.c)
|
||||||
|
|
||||||
|
if(USE_JSON_C)
|
||||||
|
list(APPEND SOURCE_FILES storage.c)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(USE_LUA)
|
if(USE_LUA)
|
||||||
list(APPEND SOURCE_FILES engines/lua.c)
|
list(APPEND SOURCE_FILES engines/lua.c)
|
||||||
list(APPEND TEST_FILES
|
list(APPEND TEST_FILES
|
||||||
|
@ -17,6 +22,10 @@ if(USE_LUA)
|
||||||
test/input.c
|
test/input.c
|
||||||
test/lua.c
|
test/lua.c
|
||||||
test/stdlib.c)
|
test/stdlib.c)
|
||||||
|
|
||||||
|
if(USE_JSON_C)
|
||||||
|
list(APPEND TEST_FILES test/storage.c)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
source_group("Scripting" FILES ${SOURCE_FILES})
|
source_group("Scripting" FILES ${SOURCE_FILES})
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <mgba/internal/script/types.h>
|
#include <mgba/internal/script/types.h>
|
||||||
#include <mgba/script/context.h>
|
#include <mgba/script/context.h>
|
||||||
#include <mgba/script/input.h>
|
#include <mgba/script/input.h>
|
||||||
|
#include <mgba/script/storage.h>
|
||||||
#include <mgba-util/string.h>
|
#include <mgba-util/string.h>
|
||||||
|
|
||||||
struct mScriptContext context;
|
struct mScriptContext context;
|
||||||
|
@ -469,6 +470,7 @@ int main(int argc, char* argv[]) {
|
||||||
mScriptContextInit(&context);
|
mScriptContextInit(&context);
|
||||||
mScriptContextAttachStdlib(&context);
|
mScriptContextAttachStdlib(&context);
|
||||||
mScriptContextAttachSocket(&context);
|
mScriptContextAttachSocket(&context);
|
||||||
|
mScriptContextAttachStorage(&context);
|
||||||
mScriptContextAttachInput(&context);
|
mScriptContextAttachInput(&context);
|
||||||
mScriptContextSetTextBufferFactory(&context, NULL, NULL);
|
mScriptContextSetTextBufferFactory(&context, NULL, NULL);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,500 @@
|
||||||
|
/* Copyright (c) 2013-2023 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#include <mgba/script/storage.h>
|
||||||
|
|
||||||
|
#include <mgba/core/config.h>
|
||||||
|
#include <mgba-util/vfs.h>
|
||||||
|
|
||||||
|
#include <json.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#define STORAGE_LEN_MAX 64
|
||||||
|
|
||||||
|
struct mScriptStorageBucket {
|
||||||
|
char* name;
|
||||||
|
struct mScriptValue* root;
|
||||||
|
bool dirty;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct mScriptStorageContext {
|
||||||
|
struct Table buckets;
|
||||||
|
};
|
||||||
|
|
||||||
|
void mScriptStorageBucketDeinit(void*);
|
||||||
|
struct mScriptValue* mScriptStorageBucketGet(struct mScriptStorageBucket* bucket, const char* key);
|
||||||
|
static void mScriptStorageBucketSet(struct mScriptStorageBucket* bucket, const char* key, struct mScriptValue* value);
|
||||||
|
static void mScriptStorageBucketSetVoid(struct mScriptStorageBucket* bucket, const char* key, struct mScriptValue* value);
|
||||||
|
static void mScriptStorageBucketSetSInt(struct mScriptStorageBucket* bucket, const char* key, int64_t value);
|
||||||
|
static void mScriptStorageBucketSetUInt(struct mScriptStorageBucket* bucket, const char* key, uint64_t value);
|
||||||
|
static void mScriptStorageBucketSetFloat(struct mScriptStorageBucket* bucket, const char* key, double value);
|
||||||
|
static void mScriptStorageBucketSetBool(struct mScriptStorageBucket* bucket, const char* key, bool value);
|
||||||
|
static bool mScriptStorageBucketReload(struct mScriptStorageBucket* bucket);
|
||||||
|
static bool mScriptStorageBucketFlush(struct mScriptStorageBucket* bucket);
|
||||||
|
|
||||||
|
static void mScriptStorageContextDeinit(struct mScriptStorageContext*);
|
||||||
|
static void mScriptStorageContextFlushAll(struct mScriptStorageContext*);
|
||||||
|
struct mScriptStorageBucket* mScriptStorageGetBucket(struct mScriptStorageContext*, const char* name);
|
||||||
|
|
||||||
|
static bool mScriptStorageToJson(struct mScriptValue* value, struct json_object** out);
|
||||||
|
static struct mScriptValue* mScriptStorageFromJson(struct json_object* json);
|
||||||
|
|
||||||
|
mSCRIPT_DECLARE_STRUCT(mScriptStorageBucket);
|
||||||
|
mSCRIPT_DECLARE_STRUCT_METHOD(mScriptStorageBucket, WRAPPER, _get, mScriptStorageBucketGet, 1, CHARP, key);
|
||||||
|
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptStorageBucket, setSInt, mScriptStorageBucketSetSInt, 2, CHARP, key, S64, value);
|
||||||
|
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptStorageBucket, setUInt, mScriptStorageBucketSetUInt, 2, CHARP, key, U64, value);
|
||||||
|
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptStorageBucket, setFloat, mScriptStorageBucketSetFloat, 2, CHARP, key, F64, value);
|
||||||
|
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptStorageBucket, setBool, mScriptStorageBucketSetBool, 2, CHARP, key, BOOL, value);
|
||||||
|
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptStorageBucket, setStr, mScriptStorageBucketSet, 2, CHARP, key, WSTR, value);
|
||||||
|
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptStorageBucket, setList, mScriptStorageBucketSet, 2, CHARP, key, WLIST, value);
|
||||||
|
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptStorageBucket, setTable, mScriptStorageBucketSet, 2, CHARP, key, WTABLE, value);
|
||||||
|
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptStorageBucket, setVoid, mScriptStorageBucketSetVoid, 2, CHARP, key, NUL, value);
|
||||||
|
mSCRIPT_DECLARE_STRUCT_METHOD(mScriptStorageBucket, BOOL, reload, mScriptStorageBucketReload, 0);
|
||||||
|
mSCRIPT_DECLARE_STRUCT_METHOD(mScriptStorageBucket, BOOL, flush, mScriptStorageBucketFlush, 0);
|
||||||
|
|
||||||
|
mSCRIPT_DEFINE_STRUCT(mScriptStorageBucket)
|
||||||
|
mSCRIPT_DEFINE_CLASS_DOCSTRING(
|
||||||
|
"A single 'bucket' of stored data, appropriate for a single script to store its data. "
|
||||||
|
"Fields can be set directly on the bucket objct, e.g. if you want to store a value called "
|
||||||
|
"`foo` on a bucket named `bucket`, you can directly assign to it as `bucket.foo = value`, "
|
||||||
|
"and retrieve it in the same way later. Primitive types (numbers, strings, lists and tables) "
|
||||||
|
"can be stored in buckets, but complex data types (e.g. a bucket itself) cannot. Data "
|
||||||
|
"stored in a bucket is periodically flushed to disk and persists between sessions."
|
||||||
|
)
|
||||||
|
mSCRIPT_DEFINE_STRUCT_DEFAULT_SET(mScriptStorageBucket, setSInt)
|
||||||
|
mSCRIPT_DEFINE_STRUCT_DEFAULT_SET(mScriptStorageBucket, setUInt)
|
||||||
|
mSCRIPT_DEFINE_STRUCT_DEFAULT_SET(mScriptStorageBucket, setFloat)
|
||||||
|
mSCRIPT_DEFINE_STRUCT_DEFAULT_SET(mScriptStorageBucket, setBool)
|
||||||
|
mSCRIPT_DEFINE_STRUCT_DEFAULT_SET(mScriptStorageBucket, setStr)
|
||||||
|
mSCRIPT_DEFINE_STRUCT_DEFAULT_SET(mScriptStorageBucket, setList)
|
||||||
|
mSCRIPT_DEFINE_STRUCT_DEFAULT_SET(mScriptStorageBucket, setTable)
|
||||||
|
mSCRIPT_DEFINE_STRUCT_DEFAULT_SET(mScriptStorageBucket, setVoid)
|
||||||
|
mSCRIPT_DEFINE_STRUCT_DEFAULT_GET(mScriptStorageBucket)
|
||||||
|
mSCRIPT_DEFINE_DOCSTRING("Reload the state of the bucket from disk")
|
||||||
|
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptStorageBucket, reload)
|
||||||
|
mSCRIPT_DEFINE_DOCSTRING("Flush the bucket to disk manually")
|
||||||
|
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptStorageBucket, flush)
|
||||||
|
mSCRIPT_DEFINE_END;
|
||||||
|
|
||||||
|
mSCRIPT_DECLARE_STRUCT(mScriptStorageContext);
|
||||||
|
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptStorageContext, _deinit, mScriptStorageContextDeinit, 0);
|
||||||
|
mSCRIPT_DECLARE_STRUCT_METHOD(mScriptStorageContext, S(mScriptStorageBucket), getBucket, mScriptStorageGetBucket, 1, CHARP, key);
|
||||||
|
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptStorageContext, flushAll, mScriptStorageContextFlushAll, 0);
|
||||||
|
|
||||||
|
mSCRIPT_DEFINE_STRUCT(mScriptStorageContext)
|
||||||
|
mSCRIPT_DEFINE_STRUCT_DEINIT(mScriptStorageContext)
|
||||||
|
mSCRIPT_DEFINE_DOCSTRING(
|
||||||
|
"Get a bucket with the given name. Names can contain letters, numbers, "
|
||||||
|
"underscores and periods. If a given bucket doesn't exist, it is created."
|
||||||
|
)
|
||||||
|
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptStorageContext, getBucket)
|
||||||
|
mSCRIPT_DEFINE_DOCSTRING("Flush all buckets to disk manually")
|
||||||
|
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptStorageContext, flushAll)
|
||||||
|
mSCRIPT_DEFINE_END;
|
||||||
|
|
||||||
|
struct mScriptValue* mScriptStorageBucketGet(struct mScriptStorageBucket* bucket, const char* key) {
|
||||||
|
struct mScriptValue* val = mScriptTableLookup(bucket->root, &mSCRIPT_MAKE_CHARP(key));
|
||||||
|
if (val) {
|
||||||
|
mScriptValueRef(val);
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mScriptStorageBucketSet(struct mScriptStorageBucket* bucket, const char* key, struct mScriptValue* value) {
|
||||||
|
struct mScriptValue* vkey = mScriptStringCreateFromUTF8(key);
|
||||||
|
if (value->type->base == mSCRIPT_TYPE_WRAPPER) {
|
||||||
|
value = mScriptValueUnwrap(value);
|
||||||
|
}
|
||||||
|
mScriptTableInsert(bucket->root, vkey, value);
|
||||||
|
mScriptValueDeref(vkey);
|
||||||
|
bucket->dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mScriptStorageBucketSetVoid(struct mScriptStorageBucket* bucket, const char* key, struct mScriptValue* value) {
|
||||||
|
UNUSED(value);
|
||||||
|
struct mScriptValue* vkey = mScriptStringCreateFromUTF8(key);
|
||||||
|
mScriptTableInsert(bucket->root, vkey, &mScriptValueNull);
|
||||||
|
mScriptValueDeref(vkey);
|
||||||
|
bucket->dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MAKE_SCALAR_SETTER(NAME, TYPE) \
|
||||||
|
void mScriptStorageBucketSet ## NAME (struct mScriptStorageBucket* bucket, const char* key, mSCRIPT_TYPE_C_ ## TYPE value) { \
|
||||||
|
struct mScriptValue* vkey = mScriptStringCreateFromUTF8(key); \
|
||||||
|
struct mScriptValue* vval = mScriptValueAlloc(mSCRIPT_TYPE_MS_ ## TYPE); \
|
||||||
|
vval->value.mSCRIPT_TYPE_FIELD_ ## TYPE = value; \
|
||||||
|
mScriptTableInsert(bucket->root, vkey, vval); \
|
||||||
|
mScriptValueDeref(vkey); \
|
||||||
|
mScriptValueDeref(vval); \
|
||||||
|
bucket->dirty = true; \
|
||||||
|
}
|
||||||
|
|
||||||
|
MAKE_SCALAR_SETTER(SInt, S64)
|
||||||
|
MAKE_SCALAR_SETTER(UInt, U64)
|
||||||
|
MAKE_SCALAR_SETTER(Float, F64)
|
||||||
|
MAKE_SCALAR_SETTER(Bool, BOOL)
|
||||||
|
|
||||||
|
void mScriptStorageGetBucketPath(const char* bucket, char* out) {
|
||||||
|
mCoreConfigDirectory(out, PATH_MAX);
|
||||||
|
|
||||||
|
strncat(out, PATH_SEP "storage" PATH_SEP, PATH_MAX);
|
||||||
|
mkdir(out, 0755);
|
||||||
|
|
||||||
|
char suffix[STORAGE_LEN_MAX + 6];
|
||||||
|
snprintf(suffix, sizeof(suffix), "%s.json", bucket);
|
||||||
|
strncat(out, suffix, PATH_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct json_object* _tableToJson(struct mScriptValue* rootVal) {
|
||||||
|
bool ok = true;
|
||||||
|
|
||||||
|
struct TableIterator iter;
|
||||||
|
struct json_object* rootObj = json_object_new_object();
|
||||||
|
if (mScriptTableIteratorStart(rootVal, &iter)) {
|
||||||
|
do {
|
||||||
|
struct mScriptValue* key = mScriptTableIteratorGetKey(rootVal, &iter);
|
||||||
|
struct mScriptValue* value = mScriptTableIteratorGetValue(rootVal, &iter);
|
||||||
|
const char* ckey;
|
||||||
|
if (key->type == mSCRIPT_TYPE_MS_CHARP) {
|
||||||
|
ckey = key->value.copaque;
|
||||||
|
} else if (key->type == mSCRIPT_TYPE_MS_STR) {
|
||||||
|
ckey = key->value.string->buffer;
|
||||||
|
} else {
|
||||||
|
ok = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct json_object* obj;
|
||||||
|
ok = mScriptStorageToJson(value, &obj);
|
||||||
|
|
||||||
|
if (!ok || json_object_object_add(rootObj, ckey, obj) < 0) {
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
} while (mScriptTableIteratorNext(rootVal, &iter) && ok);
|
||||||
|
}
|
||||||
|
if (!ok) {
|
||||||
|
json_object_put(rootObj);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return rootObj;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mScriptStorageToJson(struct mScriptValue* value, struct json_object** out) {
|
||||||
|
if (value->type->base == mSCRIPT_TYPE_WRAPPER) {
|
||||||
|
value = mScriptValueUnwrap(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t i;
|
||||||
|
bool ok = true;
|
||||||
|
struct json_object* obj = NULL;
|
||||||
|
switch (value->type->base) {
|
||||||
|
case mSCRIPT_TYPE_SINT:
|
||||||
|
obj = json_object_new_int64(value->value.s64);
|
||||||
|
break;
|
||||||
|
case mSCRIPT_TYPE_UINT:
|
||||||
|
if (value->type == mSCRIPT_TYPE_MS_BOOL) {
|
||||||
|
obj = json_object_new_boolean(value->value.u32);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
obj = json_object_new_uint64(value->value.u64);
|
||||||
|
break;
|
||||||
|
case mSCRIPT_TYPE_FLOAT:
|
||||||
|
obj = json_object_new_double(value->value.f64);
|
||||||
|
break;
|
||||||
|
case mSCRIPT_TYPE_STRING:
|
||||||
|
obj = json_object_new_string_len(value->value.string->buffer, value->value.string->size);
|
||||||
|
break;
|
||||||
|
case mSCRIPT_TYPE_LIST:
|
||||||
|
obj = json_object_new_array_ext(mScriptListSize(value->value.list));
|
||||||
|
for (i = 0; i < mScriptListSize(value->value.list); ++i) {
|
||||||
|
struct json_object* listObj;
|
||||||
|
ok = mScriptStorageToJson(mScriptListGetPointer(value->value.list, i), &listObj);
|
||||||
|
if (!ok) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
json_object_array_add(obj, listObj);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case mSCRIPT_TYPE_TABLE:
|
||||||
|
obj = _tableToJson(value);
|
||||||
|
if (!obj) {
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case mSCRIPT_TYPE_VOID:
|
||||||
|
obj = NULL;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ok = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ok) {
|
||||||
|
if (obj) {
|
||||||
|
json_object_put(obj);
|
||||||
|
}
|
||||||
|
*out = NULL;
|
||||||
|
} else {
|
||||||
|
*out = obj;
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool _mScriptStorageBucketFlushVF(struct mScriptStorageBucket* bucket, struct VFile* vf) {
|
||||||
|
struct json_object* rootObj;
|
||||||
|
bool ok = mScriptStorageToJson(bucket->root, &rootObj);
|
||||||
|
if (!ok) {
|
||||||
|
vf->close(vf);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* json = json_object_to_json_string_ext(rootObj, JSON_C_TO_STRING_PRETTY_TAB);
|
||||||
|
if (!json) {
|
||||||
|
json_object_put(rootObj);
|
||||||
|
vf->close(vf);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
vf->write(vf, json, strlen(json));
|
||||||
|
vf->close(vf);
|
||||||
|
|
||||||
|
bucket->dirty = false;
|
||||||
|
|
||||||
|
json_object_put(rootObj);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mScriptStorageBucketFlush(struct mScriptStorageBucket* bucket) {
|
||||||
|
char path[PATH_MAX];
|
||||||
|
mScriptStorageGetBucketPath(bucket->name, path);
|
||||||
|
struct VFile* vf = VFileOpen(path, O_WRONLY | O_CREAT | O_TRUNC);
|
||||||
|
return _mScriptStorageBucketFlushVF(bucket, vf);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mScriptStorageSaveBucketVF(struct mScriptContext* context, const char* bucketName, struct VFile* vf) {
|
||||||
|
struct mScriptValue* value = mScriptContextGetGlobal(context, "storage");
|
||||||
|
if (!value) {
|
||||||
|
vf->close(vf);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
struct mScriptStorageContext* storage = value->value.opaque;
|
||||||
|
struct mScriptStorageBucket* bucket = mScriptStorageGetBucket(storage, bucketName);
|
||||||
|
return _mScriptStorageBucketFlushVF(bucket, vf);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mScriptStorageSaveBucket(struct mScriptContext* context, const char* bucketName) {
|
||||||
|
char path[PATH_MAX];
|
||||||
|
mScriptStorageGetBucketPath(bucketName, path);
|
||||||
|
struct VFile* vf = VFileOpen(path, O_WRONLY | O_CREAT | O_TRUNC);
|
||||||
|
return mScriptStorageSaveBucketVF(context, bucketName, vf);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct mScriptValue* mScriptStorageFromJson(struct json_object* json) {
|
||||||
|
enum json_type type = json_object_get_type(json);
|
||||||
|
struct mScriptValue* value = NULL;
|
||||||
|
switch (type) {
|
||||||
|
case json_type_null:
|
||||||
|
return &mScriptValueNull;
|
||||||
|
case json_type_int:
|
||||||
|
value = mScriptValueAlloc(mSCRIPT_TYPE_MS_S64);
|
||||||
|
value->value.s64 = json_object_get_int64(json);
|
||||||
|
break;
|
||||||
|
case json_type_double:
|
||||||
|
value = mScriptValueAlloc(mSCRIPT_TYPE_MS_F64);
|
||||||
|
value->value.f64 = json_object_get_double(json);
|
||||||
|
break;
|
||||||
|
case json_type_boolean:
|
||||||
|
value = mScriptValueAlloc(mSCRIPT_TYPE_MS_BOOL);
|
||||||
|
value->value.u32 = json_object_get_boolean(json);
|
||||||
|
break;
|
||||||
|
case json_type_string:
|
||||||
|
value = mScriptStringCreateFromBytes(json_object_get_string(json), json_object_get_string_len(json));
|
||||||
|
break;
|
||||||
|
case json_type_array:
|
||||||
|
value = mScriptValueAlloc(mSCRIPT_TYPE_MS_LIST);
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < json_object_array_length(json); ++i) {
|
||||||
|
struct mScriptValue* vval = mScriptStorageFromJson(json_object_array_get_idx(json, i));
|
||||||
|
if (!vval) {
|
||||||
|
mScriptValueDeref(value);
|
||||||
|
value = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
mScriptValueWrap(vval, mScriptListAppend(value->value.list));
|
||||||
|
mScriptValueDeref(vval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case json_type_object:
|
||||||
|
value = mScriptValueAlloc(mSCRIPT_TYPE_MS_TABLE);
|
||||||
|
{
|
||||||
|
json_object_object_foreach(json, jkey, jval) {
|
||||||
|
struct mScriptValue* vval = mScriptStorageFromJson(jval);
|
||||||
|
if (!vval) {
|
||||||
|
mScriptValueDeref(value);
|
||||||
|
value = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
struct mScriptValue* vkey = mScriptStringCreateFromUTF8(jkey);
|
||||||
|
mScriptTableInsert(value, vkey, vval);
|
||||||
|
mScriptValueDeref(vkey);
|
||||||
|
mScriptValueDeref(vval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct mScriptValue* _mScriptStorageLoadJson(struct VFile* vf) {
|
||||||
|
ssize_t size = vf->size(vf);
|
||||||
|
if (size < 2) {
|
||||||
|
vf->close(vf);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
char* json = calloc(1, size + 1);
|
||||||
|
if (vf->read(vf, json, size) != size) {
|
||||||
|
vf->close(vf);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
vf->close(vf);
|
||||||
|
|
||||||
|
struct json_object* obj = json_tokener_parse(json);
|
||||||
|
free(json);
|
||||||
|
if (!obj) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct mScriptValue* root = mScriptStorageFromJson(obj);
|
||||||
|
json_object_put(obj);
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mScriptStorageBucketReload(struct mScriptStorageBucket* bucket) {
|
||||||
|
char path[PATH_MAX];
|
||||||
|
mScriptStorageGetBucketPath(bucket->name, path);
|
||||||
|
struct VFile* vf = VFileOpen(path, O_RDONLY);
|
||||||
|
if (!vf) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
struct mScriptValue* root = _mScriptStorageLoadJson(vf);
|
||||||
|
if (!root) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (bucket->root) {
|
||||||
|
mScriptValueDeref(bucket->root);
|
||||||
|
}
|
||||||
|
bucket->root = root;
|
||||||
|
|
||||||
|
bucket->dirty = false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mScriptStorageLoadBucketVF(struct mScriptContext* context, const char* bucketName, struct VFile* vf) {
|
||||||
|
struct mScriptValue* value = mScriptContextGetGlobal(context, "storage");
|
||||||
|
if (!value) {
|
||||||
|
vf->close(vf);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
struct mScriptStorageContext* storage = value->value.opaque;
|
||||||
|
struct mScriptValue* root = _mScriptStorageLoadJson(vf);
|
||||||
|
if (!root) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
struct mScriptStorageBucket* bucket = mScriptStorageGetBucket(storage, bucketName);
|
||||||
|
mScriptValueDeref(bucket->root);
|
||||||
|
bucket->root = root;
|
||||||
|
|
||||||
|
bucket->dirty = false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mScriptStorageLoadBucket(struct mScriptContext* context, const char* bucketName) {
|
||||||
|
char path[PATH_MAX];
|
||||||
|
mScriptStorageGetBucketPath(bucketName, path);
|
||||||
|
struct VFile* vf = VFileOpen(path, O_RDONLY);
|
||||||
|
if (!vf) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return mScriptStorageLoadBucketVF(context, bucketName, vf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mScriptContextAttachStorage(struct mScriptContext* context) {
|
||||||
|
struct mScriptStorageContext* storage = calloc(1, sizeof(*storage));
|
||||||
|
struct mScriptValue* value = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mScriptStorageContext));
|
||||||
|
value->flags = mSCRIPT_VALUE_FLAG_FREE_BUFFER;
|
||||||
|
value->value.opaque = storage;
|
||||||
|
|
||||||
|
HashTableInit(&storage->buckets, 0, mScriptStorageBucketDeinit);
|
||||||
|
|
||||||
|
mScriptContextSetGlobal(context, "storage", value);
|
||||||
|
mScriptContextSetDocstring(context, "storage", "Singleton instance of struct::mScriptStorageContext");
|
||||||
|
}
|
||||||
|
|
||||||
|
void mScriptStorageFlushAll(struct mScriptContext* context) {
|
||||||
|
struct mScriptValue* value = mScriptContextGetGlobal(context, "storage");
|
||||||
|
if (!value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
struct mScriptStorageContext* storage = value->value.opaque;
|
||||||
|
mScriptStorageContextFlushAll(storage);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mScriptStorageContextDeinit(struct mScriptStorageContext* storage) {
|
||||||
|
HashTableDeinit(&storage->buckets);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mScriptStorageContextFlushAll(struct mScriptStorageContext* storage) {
|
||||||
|
struct TableIterator iter;
|
||||||
|
if (HashTableIteratorStart(&storage->buckets, &iter)) {
|
||||||
|
do {
|
||||||
|
struct mScriptStorageBucket* bucket = HashTableIteratorGetValue(&storage->buckets, &iter);
|
||||||
|
mScriptStorageBucketFlush(bucket);
|
||||||
|
} while (HashTableIteratorNext(&storage->buckets, &iter));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct mScriptStorageBucket* mScriptStorageGetBucket(struct mScriptStorageContext* storage, const char* name) {
|
||||||
|
if (!name) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if name is allowed
|
||||||
|
// Currently only names matching /[0-9A-Za-z_.]+/ are allowed
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; name[i]; ++i) {
|
||||||
|
if (i >= STORAGE_LEN_MAX) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (!isalnum(name[i]) && name[i] != '_' && name[i] != '.') {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
struct mScriptStorageBucket* bucket = HashTableLookup(&storage->buckets, name);
|
||||||
|
if (bucket) {
|
||||||
|
return bucket;
|
||||||
|
}
|
||||||
|
|
||||||
|
bucket = calloc(1, sizeof(*bucket));
|
||||||
|
bucket->name = strdup(name);
|
||||||
|
if (!mScriptStorageBucketReload(bucket)) {
|
||||||
|
bucket->root = mScriptValueAlloc(mSCRIPT_TYPE_MS_TABLE);
|
||||||
|
}
|
||||||
|
HashTableInsert(&storage->buckets, name, bucket);
|
||||||
|
return bucket;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mScriptStorageBucketDeinit(void* data) {
|
||||||
|
struct mScriptStorageBucket* bucket = data;
|
||||||
|
if (bucket->dirty) {
|
||||||
|
mScriptStorageBucketFlush(bucket);
|
||||||
|
}
|
||||||
|
mScriptValueDeref(bucket->root);
|
||||||
|
free(bucket->name);
|
||||||
|
free(bucket);
|
||||||
|
}
|
|
@ -0,0 +1,554 @@
|
||||||
|
/* Copyright (c) 2013-2023 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#include "util/test/suite.h"
|
||||||
|
|
||||||
|
#include <mgba/internal/script/lua.h>
|
||||||
|
#include <mgba/script/storage.h>
|
||||||
|
#include <mgba/script/types.h>
|
||||||
|
|
||||||
|
#include "script/test.h"
|
||||||
|
|
||||||
|
#define SETUP_LUA \
|
||||||
|
struct mScriptContext context; \
|
||||||
|
mScriptContextInit(&context); \
|
||||||
|
struct mScriptEngineContext* lua = mScriptContextRegisterEngine(&context, mSCRIPT_ENGINE_LUA); \
|
||||||
|
mScriptContextAttachStdlib(&context); \
|
||||||
|
mScriptContextAttachStorage(&context); \
|
||||||
|
char bucketPath[PATH_MAX]; \
|
||||||
|
mScriptStorageGetBucketPath("xtest", bucketPath); \
|
||||||
|
remove(bucketPath)
|
||||||
|
|
||||||
|
M_TEST_SUITE_SETUP(mScriptStorage) {
|
||||||
|
if (mSCRIPT_ENGINE_LUA->init) {
|
||||||
|
mSCRIPT_ENGINE_LUA->init(mSCRIPT_ENGINE_LUA);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
M_TEST_SUITE_TEARDOWN(mScriptStorage) {
|
||||||
|
if (mSCRIPT_ENGINE_LUA->deinit) {
|
||||||
|
mSCRIPT_ENGINE_LUA->deinit(mSCRIPT_ENGINE_LUA);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
M_TEST_DEFINE(basicInt) {
|
||||||
|
SETUP_LUA;
|
||||||
|
|
||||||
|
TEST_PROGRAM("bucket = storage:getBucket('xtest')");
|
||||||
|
TEST_PROGRAM("assert(bucket)");
|
||||||
|
TEST_PROGRAM("assert(not bucket.a)");
|
||||||
|
TEST_PROGRAM("bucket.a = 1");
|
||||||
|
TEST_PROGRAM("assert(bucket.a == 1)");
|
||||||
|
|
||||||
|
mScriptContextDeinit(&context);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_TEST_DEFINE(basicFloat) {
|
||||||
|
SETUP_LUA;
|
||||||
|
|
||||||
|
TEST_PROGRAM("bucket = storage:getBucket('xtest')");
|
||||||
|
TEST_PROGRAM("assert(bucket)");
|
||||||
|
TEST_PROGRAM("assert(not bucket.a)");
|
||||||
|
TEST_PROGRAM("bucket.a = 0.5");
|
||||||
|
TEST_PROGRAM("assert(bucket.a == 0.5)");
|
||||||
|
|
||||||
|
mScriptContextDeinit(&context);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_TEST_DEFINE(basicBool) {
|
||||||
|
SETUP_LUA;
|
||||||
|
|
||||||
|
TEST_PROGRAM("bucket = storage:getBucket('xtest')");
|
||||||
|
TEST_PROGRAM("assert(bucket)");
|
||||||
|
TEST_PROGRAM("assert(not bucket.a)");
|
||||||
|
TEST_PROGRAM("bucket.a = true");
|
||||||
|
TEST_PROGRAM("assert(bucket.a == true)");
|
||||||
|
|
||||||
|
mScriptContextDeinit(&context);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_TEST_DEFINE(basicNil) {
|
||||||
|
SETUP_LUA;
|
||||||
|
|
||||||
|
TEST_PROGRAM("bucket = storage:getBucket('xtest')");
|
||||||
|
TEST_PROGRAM("assert(bucket)");
|
||||||
|
TEST_PROGRAM("assert(not bucket.a)");
|
||||||
|
TEST_PROGRAM("bucket.a = nil");
|
||||||
|
TEST_PROGRAM("assert(bucket.a == nil)");
|
||||||
|
|
||||||
|
mScriptContextDeinit(&context);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_TEST_DEFINE(basicString) {
|
||||||
|
SETUP_LUA;
|
||||||
|
|
||||||
|
TEST_PROGRAM("bucket = storage:getBucket('xtest')");
|
||||||
|
TEST_PROGRAM("assert(bucket)");
|
||||||
|
TEST_PROGRAM("assert(not bucket.a)");
|
||||||
|
TEST_PROGRAM("bucket.a = 'hello'");
|
||||||
|
TEST_PROGRAM("assert(bucket.a == 'hello')");
|
||||||
|
|
||||||
|
mScriptContextDeinit(&context);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_TEST_DEFINE(basicList) {
|
||||||
|
SETUP_LUA;
|
||||||
|
|
||||||
|
TEST_PROGRAM("bucket = storage:getBucket('xtest')");
|
||||||
|
TEST_PROGRAM("assert(bucket)");
|
||||||
|
TEST_PROGRAM("assert(not bucket.a)");
|
||||||
|
TEST_PROGRAM("bucket.a = {1}");
|
||||||
|
TEST_PROGRAM("assert(#bucket.a == 1)");
|
||||||
|
TEST_PROGRAM("assert(bucket.a[1] == 1)");
|
||||||
|
|
||||||
|
mScriptContextDeinit(&context);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_TEST_DEFINE(basicTable) {
|
||||||
|
SETUP_LUA;
|
||||||
|
|
||||||
|
TEST_PROGRAM("bucket = storage:getBucket('xtest')");
|
||||||
|
TEST_PROGRAM("assert(bucket)");
|
||||||
|
TEST_PROGRAM("assert(not bucket.a)");
|
||||||
|
TEST_PROGRAM("bucket.a = {['a']=1}");
|
||||||
|
TEST_PROGRAM("assert(#bucket.a == 1)");
|
||||||
|
TEST_PROGRAM("assert(bucket.a.a == 1)");
|
||||||
|
|
||||||
|
mScriptContextDeinit(&context);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_TEST_DEFINE(nullByteString) {
|
||||||
|
SETUP_LUA;
|
||||||
|
|
||||||
|
TEST_PROGRAM("bucket = storage:getBucket('xtest')");
|
||||||
|
TEST_PROGRAM("assert(bucket)");
|
||||||
|
TEST_PROGRAM("assert(not bucket.a)");
|
||||||
|
TEST_PROGRAM("bucket.a = 'a\\x00b'");
|
||||||
|
TEST_PROGRAM("assert(bucket.a == 'a\\x00b')");
|
||||||
|
TEST_PROGRAM("assert(#bucket.a == 3)");
|
||||||
|
|
||||||
|
mScriptContextDeinit(&context);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_TEST_DEFINE(structured) {
|
||||||
|
SETUP_LUA;
|
||||||
|
|
||||||
|
TEST_PROGRAM("bucket = storage:getBucket('xtest')");
|
||||||
|
TEST_PROGRAM("assert(bucket)");
|
||||||
|
TEST_PROGRAM("assert(not bucket.a)");
|
||||||
|
TEST_PROGRAM(
|
||||||
|
"bucket.a = {\n"
|
||||||
|
" ['a'] = 1,\n"
|
||||||
|
" ['b'] = {1},\n"
|
||||||
|
" ['c'] = {\n"
|
||||||
|
" ['d'] = 1\n"
|
||||||
|
" }\n"
|
||||||
|
"}"
|
||||||
|
);
|
||||||
|
TEST_PROGRAM("assert(bucket.a)");
|
||||||
|
TEST_PROGRAM("assert(bucket.a.a == 1)");
|
||||||
|
TEST_PROGRAM("assert(#bucket.a.b == 1)");
|
||||||
|
TEST_PROGRAM("assert(bucket.a.b[1] == 1)");
|
||||||
|
TEST_PROGRAM("assert(#bucket.a.c == 1)");
|
||||||
|
TEST_PROGRAM("assert(bucket.a.c.d == 1)");
|
||||||
|
|
||||||
|
mScriptContextDeinit(&context);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_TEST_DEFINE(invalidObject) {
|
||||||
|
SETUP_LUA;
|
||||||
|
|
||||||
|
TEST_PROGRAM("bucket = storage:getBucket('xtest')");
|
||||||
|
TEST_PROGRAM("assert(bucket)");
|
||||||
|
TEST_PROGRAM("assert(not bucket.a)");
|
||||||
|
LOAD_PROGRAM("bucket.a = bucket");
|
||||||
|
assert_false(lua->run(lua));
|
||||||
|
|
||||||
|
mScriptContextDeinit(&context);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_TEST_DEFINE(serializeInt) {
|
||||||
|
SETUP_LUA;
|
||||||
|
|
||||||
|
TEST_PROGRAM("bucket = storage:getBucket('xtest')");
|
||||||
|
TEST_PROGRAM("assert(bucket)");
|
||||||
|
|
||||||
|
TEST_PROGRAM("bucket.a = 1");
|
||||||
|
struct VFile* vf = VFileOpen("test.json", O_CREAT | O_TRUNC | O_WRONLY);
|
||||||
|
assert_true(mScriptStorageSaveBucketVF(&context, "xtest", vf));
|
||||||
|
vf = VFileOpen("test.json", O_RDONLY);
|
||||||
|
assert_non_null(vf);
|
||||||
|
ssize_t size = vf->size(vf);
|
||||||
|
char* buf = calloc(1, size + 1);
|
||||||
|
assert_int_equal(vf->read(vf, buf, size), size);
|
||||||
|
assert_string_equal(buf, "{\"a\":1}");
|
||||||
|
free(buf);
|
||||||
|
vf->close(vf);
|
||||||
|
|
||||||
|
mScriptContextDeinit(&context);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_TEST_DEFINE(serializeFloat) {
|
||||||
|
SETUP_LUA;
|
||||||
|
|
||||||
|
TEST_PROGRAM("bucket = storage:getBucket('xtest')");
|
||||||
|
TEST_PROGRAM("assert(bucket)");
|
||||||
|
|
||||||
|
TEST_PROGRAM("bucket.a = 0.5");
|
||||||
|
struct VFile* vf = VFileOpen("test.json", O_CREAT | O_TRUNC | O_WRONLY);
|
||||||
|
assert_true(mScriptStorageSaveBucketVF(&context, "xtest", vf));
|
||||||
|
vf = VFileOpen("test.json", O_RDONLY);
|
||||||
|
assert_non_null(vf);
|
||||||
|
ssize_t size = vf->size(vf);
|
||||||
|
char* buf = calloc(1, size + 1);
|
||||||
|
assert_int_equal(vf->read(vf, buf, size), size);
|
||||||
|
assert_string_equal(buf, "{\"a\":0.5}");
|
||||||
|
free(buf);
|
||||||
|
vf->close(vf);
|
||||||
|
|
||||||
|
mScriptContextDeinit(&context);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_TEST_DEFINE(serializeBool) {
|
||||||
|
SETUP_LUA;
|
||||||
|
|
||||||
|
TEST_PROGRAM("bucket = storage:getBucket('xtest')");
|
||||||
|
TEST_PROGRAM("assert(bucket)");
|
||||||
|
|
||||||
|
TEST_PROGRAM("bucket.a = true");
|
||||||
|
struct VFile* vf = VFileOpen("test.json", O_CREAT | O_TRUNC | O_WRONLY);
|
||||||
|
assert_true(mScriptStorageSaveBucketVF(&context, "xtest", vf));
|
||||||
|
vf = VFileOpen("test.json", O_RDONLY);
|
||||||
|
assert_non_null(vf);
|
||||||
|
ssize_t size = vf->size(vf);
|
||||||
|
char* buf = calloc(1, size + 1);
|
||||||
|
assert_int_equal(vf->read(vf, buf, size), size);
|
||||||
|
assert_string_equal(buf, "{\"a\":true}");
|
||||||
|
free(buf);
|
||||||
|
vf->close(vf);
|
||||||
|
|
||||||
|
mScriptContextDeinit(&context);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_TEST_DEFINE(serializeNil) {
|
||||||
|
SETUP_LUA;
|
||||||
|
|
||||||
|
TEST_PROGRAM("bucket = storage:getBucket('xtest')");
|
||||||
|
TEST_PROGRAM("assert(bucket)");
|
||||||
|
|
||||||
|
TEST_PROGRAM("bucket.a = nil");
|
||||||
|
struct VFile* vf = VFileOpen("test.json", O_CREAT | O_TRUNC | O_WRONLY);
|
||||||
|
assert_true(mScriptStorageSaveBucketVF(&context, "xtest", vf));
|
||||||
|
vf = VFileOpen("test.json", O_RDONLY);
|
||||||
|
assert_non_null(vf);
|
||||||
|
ssize_t size = vf->size(vf);
|
||||||
|
char* buf = calloc(1, size + 1);
|
||||||
|
assert_int_equal(vf->read(vf, buf, size), size);
|
||||||
|
assert_string_equal(buf, "{\"a\":null}");
|
||||||
|
free(buf);
|
||||||
|
vf->close(vf);
|
||||||
|
|
||||||
|
mScriptContextDeinit(&context);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_TEST_DEFINE(serializeString) {
|
||||||
|
SETUP_LUA;
|
||||||
|
|
||||||
|
TEST_PROGRAM("bucket = storage:getBucket('xtest')");
|
||||||
|
TEST_PROGRAM("assert(bucket)");
|
||||||
|
|
||||||
|
TEST_PROGRAM("bucket.a = 'hello'");
|
||||||
|
struct VFile* vf = VFileOpen("test.json", O_CREAT | O_TRUNC | O_WRONLY);
|
||||||
|
assert_true(mScriptStorageSaveBucketVF(&context, "xtest", vf));
|
||||||
|
vf = VFileOpen("test.json", O_RDONLY);
|
||||||
|
assert_non_null(vf);
|
||||||
|
ssize_t size = vf->size(vf);
|
||||||
|
char* buf = calloc(1, size + 1);
|
||||||
|
assert_int_equal(vf->read(vf, buf, size), size);
|
||||||
|
assert_string_equal(buf, "{\"a\":\"hello\"}");
|
||||||
|
free(buf);
|
||||||
|
vf->close(vf);
|
||||||
|
|
||||||
|
mScriptContextDeinit(&context);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_TEST_DEFINE(serializeList) {
|
||||||
|
SETUP_LUA;
|
||||||
|
|
||||||
|
TEST_PROGRAM("bucket = storage:getBucket('xtest')");
|
||||||
|
TEST_PROGRAM("assert(bucket)");
|
||||||
|
|
||||||
|
TEST_PROGRAM("bucket.a = {1, 2}");
|
||||||
|
struct VFile* vf = VFileOpen("test.json", O_CREAT | O_TRUNC | O_WRONLY);
|
||||||
|
assert_true(mScriptStorageSaveBucketVF(&context, "xtest", vf));
|
||||||
|
vf = VFileOpen("test.json", O_RDONLY);
|
||||||
|
assert_non_null(vf);
|
||||||
|
ssize_t size = vf->size(vf);
|
||||||
|
char* buf = calloc(1, size + 1);
|
||||||
|
assert_int_equal(vf->read(vf, buf, size), size);
|
||||||
|
assert_string_equal(buf, "{\"a\":[1,2]}");
|
||||||
|
free(buf);
|
||||||
|
vf->close(vf);
|
||||||
|
|
||||||
|
mScriptContextDeinit(&context);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_TEST_DEFINE(serializeTable) {
|
||||||
|
SETUP_LUA;
|
||||||
|
|
||||||
|
TEST_PROGRAM("bucket = storage:getBucket('xtest')");
|
||||||
|
TEST_PROGRAM("assert(bucket)");
|
||||||
|
|
||||||
|
TEST_PROGRAM("bucket.a = {['b']=1}");
|
||||||
|
struct VFile* vf = VFileOpen("test.json", O_CREAT | O_TRUNC | O_WRONLY);
|
||||||
|
assert_true(mScriptStorageSaveBucketVF(&context, "xtest", vf));
|
||||||
|
vf = VFileOpen("test.json", O_RDONLY);
|
||||||
|
assert_non_null(vf);
|
||||||
|
ssize_t size = vf->size(vf);
|
||||||
|
char* buf = calloc(1, size + 1);
|
||||||
|
assert_int_equal(vf->read(vf, buf, size), size);
|
||||||
|
assert_string_equal(buf, "{\"a\":{\"b\":1}}");
|
||||||
|
free(buf);
|
||||||
|
vf->close(vf);
|
||||||
|
|
||||||
|
mScriptContextDeinit(&context);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_TEST_DEFINE(serializeNullByteString) {
|
||||||
|
SETUP_LUA;
|
||||||
|
|
||||||
|
TEST_PROGRAM("bucket = storage:getBucket('xtest')");
|
||||||
|
TEST_PROGRAM("assert(bucket)");
|
||||||
|
|
||||||
|
TEST_PROGRAM("bucket.a = 'a\\x00b'");
|
||||||
|
struct VFile* vf = VFileOpen("test.json", O_CREAT | O_TRUNC | O_WRONLY);
|
||||||
|
assert_true(mScriptStorageSaveBucketVF(&context, "xtest", vf));
|
||||||
|
vf = VFileOpen("test.json", O_RDONLY);
|
||||||
|
assert_non_null(vf);
|
||||||
|
ssize_t size = vf->size(vf);
|
||||||
|
char* buf = calloc(1, size + 1);
|
||||||
|
assert_int_equal(vf->read(vf, buf, size), size);
|
||||||
|
assert_string_equal(buf, "{\"a\":\"a\\u0000b\"}");
|
||||||
|
free(buf);
|
||||||
|
vf->close(vf);
|
||||||
|
|
||||||
|
mScriptContextDeinit(&context);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_TEST_DEFINE(deserializeInt) {
|
||||||
|
SETUP_LUA;
|
||||||
|
|
||||||
|
TEST_PROGRAM("bucket = storage:getBucket('xtest')");
|
||||||
|
TEST_PROGRAM("assert(bucket)");
|
||||||
|
|
||||||
|
TEST_PROGRAM("assert(not bucket.a)");
|
||||||
|
|
||||||
|
static const char* json = "{\"a\":1}";
|
||||||
|
struct VFile* vf = VFileFromConstMemory(json, strlen(json));
|
||||||
|
assert_true(mScriptStorageLoadBucketVF(&context, "xtest", vf));
|
||||||
|
TEST_PROGRAM("assert(bucket.a == 1)");
|
||||||
|
|
||||||
|
mScriptContextDeinit(&context);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_TEST_DEFINE(deserializeFloat) {
|
||||||
|
SETUP_LUA;
|
||||||
|
|
||||||
|
TEST_PROGRAM("bucket = storage:getBucket('xtest')");
|
||||||
|
TEST_PROGRAM("assert(bucket)");
|
||||||
|
|
||||||
|
TEST_PROGRAM("assert(not bucket.a)");
|
||||||
|
|
||||||
|
static const char* json = "{\"a\":0.5}";
|
||||||
|
struct VFile* vf = VFileFromConstMemory(json, strlen(json));
|
||||||
|
assert_true(mScriptStorageLoadBucketVF(&context, "xtest", vf));
|
||||||
|
TEST_PROGRAM("assert(bucket.a == 0.5)");
|
||||||
|
|
||||||
|
mScriptContextDeinit(&context);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_TEST_DEFINE(deserializeBool) {
|
||||||
|
SETUP_LUA;
|
||||||
|
|
||||||
|
TEST_PROGRAM("bucket = storage:getBucket('xtest')");
|
||||||
|
TEST_PROGRAM("assert(bucket)");
|
||||||
|
|
||||||
|
TEST_PROGRAM("assert(not bucket.a)");
|
||||||
|
|
||||||
|
static const char* json = "{\"a\":true}";
|
||||||
|
struct VFile* vf = VFileFromConstMemory(json, strlen(json));
|
||||||
|
assert_true(mScriptStorageLoadBucketVF(&context, "xtest", vf));
|
||||||
|
TEST_PROGRAM("assert(bucket.a == true)");
|
||||||
|
|
||||||
|
mScriptContextDeinit(&context);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_TEST_DEFINE(deserializeNil) {
|
||||||
|
SETUP_LUA;
|
||||||
|
|
||||||
|
TEST_PROGRAM("bucket = storage:getBucket('xtest')");
|
||||||
|
TEST_PROGRAM("assert(bucket)");
|
||||||
|
|
||||||
|
TEST_PROGRAM("assert(not bucket.a)");
|
||||||
|
|
||||||
|
static const char* json = "{\"a\":null}";
|
||||||
|
struct VFile* vf = VFileFromConstMemory(json, strlen(json));
|
||||||
|
assert_true(mScriptStorageLoadBucketVF(&context, "xtest", vf));
|
||||||
|
TEST_PROGRAM("assert(bucket.a == nil)");
|
||||||
|
|
||||||
|
mScriptContextDeinit(&context);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_TEST_DEFINE(deserializeString) {
|
||||||
|
SETUP_LUA;
|
||||||
|
|
||||||
|
TEST_PROGRAM("bucket = storage:getBucket('xtest')");
|
||||||
|
TEST_PROGRAM("assert(bucket)");
|
||||||
|
|
||||||
|
TEST_PROGRAM("assert(not bucket.a)");
|
||||||
|
|
||||||
|
static const char* json = "{\"a\":\"hello\"}";
|
||||||
|
struct VFile* vf = VFileFromConstMemory(json, strlen(json));
|
||||||
|
assert_true(mScriptStorageLoadBucketVF(&context, "xtest", vf));
|
||||||
|
TEST_PROGRAM("assert(bucket.a == 'hello')");
|
||||||
|
|
||||||
|
mScriptContextDeinit(&context);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_TEST_DEFINE(deserializeList) {
|
||||||
|
SETUP_LUA;
|
||||||
|
|
||||||
|
TEST_PROGRAM("bucket = storage:getBucket('xtest')");
|
||||||
|
TEST_PROGRAM("assert(bucket)");
|
||||||
|
|
||||||
|
TEST_PROGRAM("assert(not bucket.a)");
|
||||||
|
|
||||||
|
static const char* json = "{\"a\":[1,2]}";
|
||||||
|
struct VFile* vf = VFileFromConstMemory(json, strlen(json));
|
||||||
|
assert_true(mScriptStorageLoadBucketVF(&context, "xtest", vf));
|
||||||
|
TEST_PROGRAM("assert(#bucket.a == 2)");
|
||||||
|
TEST_PROGRAM("assert(bucket.a[1] == 1)");
|
||||||
|
TEST_PROGRAM("assert(bucket.a[2] == 2)");
|
||||||
|
|
||||||
|
mScriptContextDeinit(&context);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_TEST_DEFINE(deserializeTable) {
|
||||||
|
SETUP_LUA;
|
||||||
|
|
||||||
|
TEST_PROGRAM("bucket = storage:getBucket('xtest')");
|
||||||
|
TEST_PROGRAM("assert(bucket)");
|
||||||
|
|
||||||
|
TEST_PROGRAM("assert(not bucket.a)");
|
||||||
|
|
||||||
|
static const char* json = "{\"a\":{\"b\":1}}";
|
||||||
|
struct VFile* vf = VFileFromConstMemory(json, strlen(json));
|
||||||
|
assert_true(mScriptStorageLoadBucketVF(&context, "xtest", vf));
|
||||||
|
TEST_PROGRAM("assert(bucket.a.b == 1)");
|
||||||
|
|
||||||
|
mScriptContextDeinit(&context);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_TEST_DEFINE(deserializeNullByteString) {
|
||||||
|
SETUP_LUA;
|
||||||
|
|
||||||
|
TEST_PROGRAM("bucket = storage:getBucket('xtest')");
|
||||||
|
TEST_PROGRAM("assert(bucket)");
|
||||||
|
|
||||||
|
TEST_PROGRAM("assert(not bucket.a)");
|
||||||
|
|
||||||
|
static const char* json = "{\"a\":\"a\\u0000b\"}";
|
||||||
|
struct VFile* vf = VFileFromConstMemory(json, strlen(json));
|
||||||
|
assert_true(mScriptStorageLoadBucketVF(&context, "xtest", vf));
|
||||||
|
TEST_PROGRAM("assert(bucket.a == 'a\\x00b')");
|
||||||
|
TEST_PROGRAM("assert(bucket.a ~= 'a\\x00c')");
|
||||||
|
|
||||||
|
mScriptContextDeinit(&context);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_TEST_DEFINE(deserializeError) {
|
||||||
|
SETUP_LUA;
|
||||||
|
|
||||||
|
TEST_PROGRAM("bucket = storage:getBucket('xtest')");
|
||||||
|
TEST_PROGRAM("assert(bucket)");
|
||||||
|
|
||||||
|
TEST_PROGRAM("assert(not bucket.a)");
|
||||||
|
|
||||||
|
static const char* json = "{a:1}";
|
||||||
|
struct VFile* vf = VFileFromConstMemory(json, strlen(json));
|
||||||
|
assert_false(mScriptStorageLoadBucketVF(&context, "xtest", vf));
|
||||||
|
TEST_PROGRAM("assert(not bucket.a)");
|
||||||
|
|
||||||
|
mScriptContextDeinit(&context);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_TEST_DEFINE(structuredRoundTrip) {
|
||||||
|
SETUP_LUA;
|
||||||
|
|
||||||
|
TEST_PROGRAM("bucket = storage:getBucket('xtest')");
|
||||||
|
TEST_PROGRAM("assert(bucket)");
|
||||||
|
TEST_PROGRAM("assert(not bucket.a)");
|
||||||
|
TEST_PROGRAM(
|
||||||
|
"bucket.a = {\n"
|
||||||
|
" ['a'] = 1,\n"
|
||||||
|
" ['b'] = {1},\n"
|
||||||
|
" ['c'] = {\n"
|
||||||
|
" ['d'] = 1\n"
|
||||||
|
" }\n"
|
||||||
|
"}"
|
||||||
|
);
|
||||||
|
|
||||||
|
struct VFile* vf = VFileOpen("test.json", O_CREAT | O_TRUNC | O_WRONLY);
|
||||||
|
assert_true(mScriptStorageSaveBucketVF(&context, "xtest", vf));
|
||||||
|
|
||||||
|
TEST_PROGRAM("bucket.a = nil")
|
||||||
|
TEST_PROGRAM("assert(not bucket.a)");
|
||||||
|
|
||||||
|
vf = VFileOpen("test.json", O_RDONLY);
|
||||||
|
assert_non_null(vf);
|
||||||
|
assert_true(mScriptStorageLoadBucketVF(&context, "xtest", vf));
|
||||||
|
|
||||||
|
TEST_PROGRAM("assert(bucket.a)");
|
||||||
|
TEST_PROGRAM("assert(bucket.a.a == 1)");
|
||||||
|
TEST_PROGRAM("assert(#bucket.a.b == 1)");
|
||||||
|
TEST_PROGRAM("assert(bucket.a.b[1] == 1)");
|
||||||
|
TEST_PROGRAM("assert(#bucket.a.c == 1)");
|
||||||
|
TEST_PROGRAM("assert(bucket.a.c.d == 1)");
|
||||||
|
|
||||||
|
mScriptContextDeinit(&context);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptStorage,
|
||||||
|
cmocka_unit_test(basicInt),
|
||||||
|
cmocka_unit_test(basicFloat),
|
||||||
|
cmocka_unit_test(basicBool),
|
||||||
|
cmocka_unit_test(basicNil),
|
||||||
|
cmocka_unit_test(basicString),
|
||||||
|
cmocka_unit_test(basicList),
|
||||||
|
cmocka_unit_test(basicTable),
|
||||||
|
cmocka_unit_test(nullByteString),
|
||||||
|
cmocka_unit_test(invalidObject),
|
||||||
|
cmocka_unit_test(structured),
|
||||||
|
cmocka_unit_test(serializeInt),
|
||||||
|
cmocka_unit_test(serializeFloat),
|
||||||
|
cmocka_unit_test(serializeBool),
|
||||||
|
cmocka_unit_test(serializeNil),
|
||||||
|
cmocka_unit_test(serializeString),
|
||||||
|
cmocka_unit_test(serializeList),
|
||||||
|
cmocka_unit_test(serializeTable),
|
||||||
|
cmocka_unit_test(serializeNullByteString),
|
||||||
|
cmocka_unit_test(deserializeInt),
|
||||||
|
cmocka_unit_test(deserializeFloat),
|
||||||
|
cmocka_unit_test(deserializeBool),
|
||||||
|
cmocka_unit_test(deserializeNil),
|
||||||
|
cmocka_unit_test(deserializeString),
|
||||||
|
cmocka_unit_test(deserializeList),
|
||||||
|
cmocka_unit_test(deserializeTable),
|
||||||
|
cmocka_unit_test(deserializeNullByteString),
|
||||||
|
cmocka_unit_test(deserializeError),
|
||||||
|
cmocka_unit_test(structuredRoundTrip),
|
||||||
|
)
|
Loading…
Reference in New Issue