5e37ebfed0
Expose `automerge::AutoCommit::with_actor()` through `AMcreate()`. Add notes to clarify the purpose of `AMfreeStack()`, `AMpop()`, `AMpush()`, `AMpushCallback()`, and `AMresultStack`.
1755 lines
91 KiB
C
1755 lines
91 KiB
C
#include <float.h>
|
|
#include <setjmp.h>
|
|
#include <stdarg.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
/* third-party */
|
|
#include <cmocka.h>
|
|
|
|
/* local */
|
|
#include <automerge-c/automerge.h>
|
|
#include "../stack_utils.h"
|
|
|
|
/**
|
|
* \brief default import init() should return a promise
|
|
*/
|
|
static void test_default_import_init_should_return_a_promise(void** state);
|
|
|
|
/**
|
|
* \brief should create, clone and free
|
|
*/
|
|
static void test_create_clone_and_free(void** state) {
|
|
AMresultStack* stack = *state;
|
|
/* const doc1 = create() */
|
|
AMdoc* const doc1 = AMpush(&stack, AMcreate(NULL), AM_VALUE_DOC, cmocka_cb).doc;
|
|
/* const doc2 = doc1.clone() */
|
|
AMdoc* const doc2 = AMpush(&stack, AMclone(doc1), AM_VALUE_DOC, cmocka_cb).doc;
|
|
}
|
|
|
|
/**
|
|
* \brief should be able to start and commit
|
|
*/
|
|
static void test_start_and_commit(void** state) {
|
|
AMresultStack* stack = *state;
|
|
/* const doc = create() */
|
|
AMdoc* const doc = AMpush(&stack, AMcreate(NULL), AM_VALUE_DOC, cmocka_cb).doc;
|
|
/* doc.commit() */
|
|
AMpush(&stack, AMcommit(doc, NULL, NULL), AM_VALUE_CHANGE_HASHES, cmocka_cb);
|
|
}
|
|
|
|
/**
|
|
* \brief getting a nonexistent prop does not throw an error
|
|
*/
|
|
static void test_getting_a_nonexistent_prop_does_not_throw_an_error(void** state) {
|
|
AMresultStack* stack = *state;
|
|
/* const doc = create() */
|
|
AMdoc* const doc = AMpush(&stack, AMcreate(NULL), AM_VALUE_DOC, cmocka_cb).doc;
|
|
/* const root = "_root" */
|
|
/* const result = doc.getWithType(root, "hello") */
|
|
/* assert.deepEqual(result, undefined) */
|
|
AMpush(&stack,
|
|
AMmapGet(doc, AM_ROOT, "hello", NULL),
|
|
AM_VALUE_VOID,
|
|
cmocka_cb);
|
|
}
|
|
|
|
/**
|
|
* \brief should be able to set and get a simple value
|
|
*/
|
|
static void test_should_be_able_to_set_and_get_a_simple_value(void** state) {
|
|
AMresultStack* stack = *state;
|
|
/* const doc: Automerge = create("aabbcc") */
|
|
AMdoc* const doc = AMpush(&stack,
|
|
AMcreate(AMpush(&stack,
|
|
AMactorIdInitStr("aabbcc"),
|
|
AM_VALUE_ACTOR_ID,
|
|
cmocka_cb).actor_id),
|
|
AM_VALUE_DOC,
|
|
cmocka_cb).doc;
|
|
/* const root = "_root" */
|
|
/* let result */
|
|
/* */
|
|
/* doc.put(root, "hello", "world") */
|
|
AMfree(AMmapPutStr(doc, AM_ROOT, "hello", "world"));
|
|
/* doc.put(root, "number1", 5, "uint") */
|
|
AMfree(AMmapPutUint(doc, AM_ROOT, "number1", 5));
|
|
/* doc.put(root, "number2", 5) */
|
|
AMfree(AMmapPutInt(doc, AM_ROOT, "number2", 5));
|
|
/* doc.put(root, "number3", 5.5) */
|
|
AMfree(AMmapPutF64(doc, AM_ROOT, "number3", 5.5));
|
|
/* doc.put(root, "number4", 5.5, "f64") */
|
|
AMfree(AMmapPutF64(doc, AM_ROOT, "number4", 5.5));
|
|
/* doc.put(root, "number5", 5.5, "int") */
|
|
AMfree(AMmapPutInt(doc, AM_ROOT, "number5", 5.5));
|
|
/* doc.put(root, "bool", true) */
|
|
AMfree(AMmapPutBool(doc, AM_ROOT, "bool", true));
|
|
/* doc.put(root, "time1", 1000, "timestamp") */
|
|
AMfree(AMmapPutTimestamp(doc, AM_ROOT, "time1", 1000));
|
|
/* doc.put(root, "time2", new Date(1001)) */
|
|
AMfree(AMmapPutTimestamp(doc, AM_ROOT, "time2", 1001));
|
|
/* doc.putObject(root, "list", []); */
|
|
AMfree(AMmapPutObject(doc, AM_ROOT, "list", AM_OBJ_TYPE_LIST));
|
|
/* doc.put(root, "null", null) */
|
|
AMfree(AMmapPutNull(doc, AM_ROOT, "null"));
|
|
/* */
|
|
/* result = doc.getWithType(root, "hello") */
|
|
/* assert.deepEqual(result, ["str", "world"]) */
|
|
/* assert.deepEqual(doc.get("/", "hello"), "world") */
|
|
assert_string_equal(AMpush(&stack,
|
|
AMmapGet(doc, AM_ROOT, "hello", NULL),
|
|
AM_VALUE_STR,
|
|
cmocka_cb).str, "world");
|
|
/* assert.deepEqual(doc.get("/", "hello"), "world") */
|
|
/* */
|
|
/* result = doc.getWithType(root, "number1") */
|
|
/* assert.deepEqual(result, ["uint", 5]) */
|
|
assert_int_equal(AMpush(&stack,
|
|
AMmapGet(doc, AM_ROOT, "number1", NULL),
|
|
AM_VALUE_UINT,
|
|
cmocka_cb).uint, 5);
|
|
/* assert.deepEqual(doc.get("/", "number1"), 5) */
|
|
/* */
|
|
/* result = doc.getWithType(root, "number2") */
|
|
/* assert.deepEqual(result, ["int", 5]) */
|
|
assert_int_equal(AMpush(&stack,
|
|
AMmapGet(doc, AM_ROOT, "number2", NULL),
|
|
AM_VALUE_INT,
|
|
cmocka_cb).int_, 5);
|
|
/* */
|
|
/* result = doc.getWithType(root, "number3") */
|
|
/* assert.deepEqual(result, ["f64", 5.5]) */
|
|
assert_float_equal(AMpush(&stack,
|
|
AMmapGet(doc, AM_ROOT, "number3", NULL),
|
|
AM_VALUE_F64,
|
|
cmocka_cb).f64, 5.5, DBL_EPSILON);
|
|
/* */
|
|
/* result = doc.getWithType(root, "number4") */
|
|
/* assert.deepEqual(result, ["f64", 5.5]) */
|
|
assert_float_equal(AMpush(&stack,
|
|
AMmapGet(doc, AM_ROOT, "number4", NULL),
|
|
AM_VALUE_F64,
|
|
cmocka_cb).f64, 5.5, DBL_EPSILON);
|
|
/* */
|
|
/* result = doc.getWithType(root, "number5") */
|
|
/* assert.deepEqual(result, ["int", 5]) */
|
|
assert_int_equal(AMpush(&stack,
|
|
AMmapGet(doc, AM_ROOT, "number5", NULL),
|
|
AM_VALUE_INT,
|
|
cmocka_cb).int_, 5);
|
|
/* */
|
|
/* result = doc.getWithType(root, "bool") */
|
|
/* assert.deepEqual(result, ["boolean", true]) */
|
|
assert_int_equal(AMpush(&stack,
|
|
AMmapGet(doc, AM_ROOT, "bool", NULL),
|
|
AM_VALUE_BOOLEAN,
|
|
cmocka_cb).boolean, true);
|
|
/* */
|
|
/* doc.put(root, "bool", false, "boolean") */
|
|
AMfree(AMmapPutBool(doc, AM_ROOT, "bool", false));
|
|
/* */
|
|
/* result = doc.getWithType(root, "bool") */
|
|
/* assert.deepEqual(result, ["boolean", false]) */
|
|
assert_int_equal(AMpush(&stack,
|
|
AMmapGet(doc, AM_ROOT, "bool", NULL),
|
|
AM_VALUE_BOOLEAN,
|
|
cmocka_cb).boolean, false);
|
|
/* */
|
|
/* result = doc.getWithType(root, "time1") */
|
|
/* assert.deepEqual(result, ["timestamp", new Date(1000)]) */
|
|
assert_int_equal(AMpush(&stack,
|
|
AMmapGet(doc, AM_ROOT, "time1", NULL),
|
|
AM_VALUE_TIMESTAMP,
|
|
cmocka_cb).timestamp, 1000);
|
|
/* */
|
|
/* result = doc.getWithType(root, "time2") */
|
|
/* assert.deepEqual(result, ["timestamp", new Date(1001)]) */
|
|
assert_int_equal(AMpush(&stack,
|
|
AMmapGet(doc, AM_ROOT, "time2", NULL),
|
|
AM_VALUE_TIMESTAMP,
|
|
cmocka_cb).timestamp, 1001);
|
|
/* */
|
|
/* result = doc.getWithType(root, "list") */
|
|
/* assert.deepEqual(result, ["list", "10@aabbcc"]); */
|
|
AMobjId const* const list = AMpush(&stack,
|
|
AMmapGet(doc, AM_ROOT, "list", NULL),
|
|
AM_VALUE_OBJ_ID,
|
|
cmocka_cb).obj_id;
|
|
assert_int_equal(AMobjIdCounter(list), 10);
|
|
assert_string_equal(AMactorIdStr(AMobjIdActorId(list)), "aabbcc");
|
|
/* */
|
|
/* result = doc.getWithType(root, "null") */
|
|
/* assert.deepEqual(result, ["null", null]); */
|
|
AMpush(&stack,
|
|
AMmapGet(doc, AM_ROOT, "null", NULL),
|
|
AM_VALUE_NULL,
|
|
cmocka_cb);
|
|
}
|
|
|
|
/**
|
|
* \brief should be able to use bytes
|
|
*/
|
|
static void test_should_be_able_to_use_bytes(void** state) {
|
|
AMresultStack* stack = *state;
|
|
/* const doc = create() */
|
|
AMdoc* const doc = AMpush(&stack, AMcreate(NULL), AM_VALUE_DOC, cmocka_cb).doc;
|
|
/* doc.put("_root", "data1", new Uint8Array([10, 11, 12])); */
|
|
static uint8_t const DATA1[] = {10, 11, 12};
|
|
AMfree(AMmapPutBytes(doc, AM_ROOT, "data1", DATA1, sizeof(DATA1)));
|
|
/* doc.put("_root", "data2", new Uint8Array([13, 14, 15]), "bytes"); */
|
|
static uint8_t const DATA2[] = {13, 14, 15};
|
|
AMfree(AMmapPutBytes(doc, AM_ROOT, "data2", DATA2, sizeof(DATA2)));
|
|
/* const value1 = doc.getWithType("_root", "data1") */
|
|
AMbyteSpan const value1 = AMpush(&stack,
|
|
AMmapGet(doc, AM_ROOT, "data1", NULL),
|
|
AM_VALUE_BYTES,
|
|
cmocka_cb).bytes;
|
|
/* assert.deepEqual(value1, ["bytes", new Uint8Array([10, 11, 12])]); */
|
|
assert_int_equal(value1.count, sizeof(DATA1));
|
|
assert_memory_equal(value1.src, DATA1, sizeof(DATA1));
|
|
/* const value2 = doc.getWithType("_root", "data2") */
|
|
AMbyteSpan const value2 = AMpush(&stack,
|
|
AMmapGet(doc, AM_ROOT, "data2", NULL),
|
|
AM_VALUE_BYTES,
|
|
cmocka_cb).bytes;
|
|
/* assert.deepEqual(value2, ["bytes", new Uint8Array([13, 14, 15])]); */
|
|
assert_int_equal(value2.count, sizeof(DATA2));
|
|
assert_memory_equal(value2.src, DATA2, sizeof(DATA2));
|
|
}
|
|
|
|
/**
|
|
* \brief should be able to make subobjects
|
|
*/
|
|
static void test_should_be_able_to_make_subobjects(void** state) {
|
|
AMresultStack* stack = *state;
|
|
/* const doc = create() */
|
|
AMdoc* const doc = AMpush(&stack, AMcreate(NULL), AM_VALUE_DOC, cmocka_cb).doc;
|
|
/* const root = "_root" */
|
|
/* let result */
|
|
/* */
|
|
/* const submap = doc.putObject(root, "submap", {}) */
|
|
AMobjId const* const submap = AMpush(
|
|
&stack,
|
|
AMmapPutObject(doc, AM_ROOT, "submap", AM_OBJ_TYPE_MAP),
|
|
AM_VALUE_OBJ_ID,
|
|
cmocka_cb).obj_id;
|
|
/* doc.put(submap, "number", 6, "uint") */
|
|
AMfree(AMmapPutUint(doc, submap, "number", 6));
|
|
/* assert.strictEqual(doc.pendingOps(), 2) */
|
|
assert_int_equal(AMpendingOps(doc), 2);
|
|
/* */
|
|
/* result = doc.getWithType(root, "submap") */
|
|
/* assert.deepEqual(result, ["map", submap]) */
|
|
assert_true(AMobjIdEqual(AMpush(&stack,
|
|
AMmapGet(doc, AM_ROOT, "submap", NULL),
|
|
AM_VALUE_OBJ_ID,
|
|
cmocka_cb).obj_id,
|
|
submap));
|
|
/* */
|
|
/* result = doc.getWithType(submap, "number") */
|
|
/* assert.deepEqual(result, ["uint", 6]) */
|
|
assert_int_equal(AMpush(&stack,
|
|
AMmapGet(doc, submap, "number", NULL),
|
|
AM_VALUE_UINT,
|
|
cmocka_cb).uint,
|
|
6);
|
|
}
|
|
|
|
/**
|
|
* \brief should be able to make lists
|
|
*/
|
|
static void test_should_be_able_to_make_lists(void** state) {
|
|
AMresultStack* stack = *state;
|
|
/* const doc = create() */
|
|
AMdoc* const doc = AMpush(&stack, AMcreate(NULL), AM_VALUE_DOC, cmocka_cb).doc;
|
|
/* const root = "_root" */
|
|
/* */
|
|
/* const sublist = doc.putObject(root, "numbers", []) */
|
|
AMobjId const* const sublist = AMpush(
|
|
&stack,
|
|
AMmapPutObject(doc, AM_ROOT, "numbers", AM_OBJ_TYPE_LIST),
|
|
AM_VALUE_OBJ_ID,
|
|
cmocka_cb).obj_id;
|
|
/* doc.insert(sublist, 0, "a"); */
|
|
AMfree(AMlistPutStr(doc, sublist, 0, true, "a"));
|
|
/* doc.insert(sublist, 1, "b"); */
|
|
AMfree(AMlistPutStr(doc, sublist, 1, true, "b"));
|
|
/* doc.insert(sublist, 2, "c"); */
|
|
AMfree(AMlistPutStr(doc, sublist, 2, true, "c"));
|
|
/* doc.insert(sublist, 0, "z"); */
|
|
AMfree(AMlistPutStr(doc, sublist, 0, true, "z"));
|
|
/* */
|
|
/* assert.deepEqual(doc.getWithType(sublist, 0), ["str", "z"]) */
|
|
assert_string_equal(AMpush(&stack,
|
|
AMlistGet(doc, sublist, 0, NULL),
|
|
AM_VALUE_STR,
|
|
cmocka_cb).str, "z");
|
|
/* assert.deepEqual(doc.getWithType(sublist, 1), ["str", "a"]) */
|
|
assert_string_equal(AMpush(&stack,
|
|
AMlistGet(doc, sublist, 1, NULL),
|
|
AM_VALUE_STR,
|
|
cmocka_cb).str, "a");
|
|
/* assert.deepEqual(doc.getWithType(sublist, 2), ["str", "b"]) */
|
|
assert_string_equal(AMpush(&stack,
|
|
AMlistGet(doc, sublist, 2, NULL),
|
|
AM_VALUE_STR,
|
|
cmocka_cb).str, "b");
|
|
/* assert.deepEqual(doc.getWithType(sublist, 3), ["str", "c"]) */
|
|
assert_string_equal(AMpush(&stack,
|
|
AMlistGet(doc, sublist, 3, NULL),
|
|
AM_VALUE_STR,
|
|
cmocka_cb).str, "c");
|
|
/* assert.deepEqual(doc.length(sublist), 4) */
|
|
assert_int_equal(AMobjSize(doc, sublist, NULL), 4);
|
|
/* */
|
|
/* doc.put(sublist, 2, "b v2"); */
|
|
AMfree(AMlistPutStr(doc, sublist, 2, false, "b v2"));
|
|
/* */
|
|
/* assert.deepEqual(doc.getWithType(sublist, 2), ["str", "b v2"]) */
|
|
assert_string_equal(AMpush(&stack,
|
|
AMlistGet(doc, sublist, 2, NULL),
|
|
AM_VALUE_STR,
|
|
cmocka_cb).str, "b v2");
|
|
/* assert.deepEqual(doc.length(sublist), 4) */
|
|
assert_int_equal(AMobjSize(doc, sublist, NULL), 4);
|
|
}
|
|
|
|
/**
|
|
* \brief lists have insert, set, splice, and push ops
|
|
*/
|
|
static void test_lists_have_insert_set_splice_and_push_ops(void** state) {
|
|
AMresultStack* stack = *state;
|
|
/* const doc = create() */
|
|
AMdoc* const doc = AMpush(&stack, AMcreate(NULL), AM_VALUE_DOC, cmocka_cb).doc;
|
|
/* const root = "_root" */
|
|
/* */
|
|
/* const sublist = doc.putObject(root, "letters", []) */
|
|
AMobjId const* const sublist = AMpush(
|
|
&stack,
|
|
AMmapPutObject(doc, AM_ROOT, "letters", AM_OBJ_TYPE_LIST),
|
|
AM_VALUE_OBJ_ID,
|
|
cmocka_cb).obj_id;
|
|
/* doc.insert(sublist, 0, "a"); */
|
|
AMfree(AMlistPutStr(doc, sublist, 0, true, "a"));
|
|
/* doc.insert(sublist, 0, "b"); */
|
|
AMfree(AMlistPutStr(doc, sublist, 0, true, "b"));
|
|
/* assert.deepEqual(doc.materialize(), { letters: ["b", "a"] }) */
|
|
AMmapItems doc_items = AMpush(&stack,
|
|
AMmapRange(doc, AM_ROOT, NULL, NULL, NULL),
|
|
AM_VALUE_MAP_ITEMS,
|
|
cmocka_cb).map_items;
|
|
AMmapItem const* doc_item = AMmapItemsNext(&doc_items, 1);
|
|
assert_string_equal(AMmapItemKey(doc_item), "letters");
|
|
{
|
|
AMlistItems list_items = AMpush(
|
|
&stack,
|
|
AMlistRange(doc, AMmapItemObjId(doc_item), 0, SIZE_MAX, NULL),
|
|
AM_VALUE_LIST_ITEMS,
|
|
cmocka_cb).list_items;
|
|
assert_string_equal(AMlistItemValue(AMlistItemsNext(&list_items, 1)).str,
|
|
"b");
|
|
assert_string_equal(AMlistItemValue(AMlistItemsNext(&list_items, 1)).str,
|
|
"a");
|
|
assert_null(AMlistItemsNext(&list_items, 1));
|
|
}
|
|
/* doc.push(sublist, "c"); */
|
|
AMfree(AMlistPutStr(doc, sublist, SIZE_MAX, true, "c"));
|
|
/* const heads = doc.getHeads() */
|
|
AMchangeHashes const heads = AMpush(&stack,
|
|
AMgetHeads(doc),
|
|
AM_VALUE_CHANGE_HASHES,
|
|
cmocka_cb).change_hashes;
|
|
/* assert.deepEqual(doc.materialize(), { letters: ["b", "a", "c"] }) */
|
|
doc_items = AMpush(&stack,
|
|
AMmapRange(doc, AM_ROOT, NULL, NULL, NULL),
|
|
AM_VALUE_MAP_ITEMS,
|
|
cmocka_cb).map_items;
|
|
doc_item = AMmapItemsNext(&doc_items, 1);
|
|
assert_string_equal(AMmapItemKey(doc_item), "letters");
|
|
{
|
|
AMlistItems list_items = AMpush(
|
|
&stack,
|
|
AMlistRange(doc, AMmapItemObjId(doc_item), 0, SIZE_MAX, NULL),
|
|
AM_VALUE_LIST_ITEMS,
|
|
cmocka_cb).list_items;
|
|
assert_string_equal(AMlistItemValue(AMlistItemsNext(&list_items, 1)).str,
|
|
"b");
|
|
assert_string_equal(AMlistItemValue(AMlistItemsNext(&list_items, 1)).str,
|
|
"a");
|
|
assert_string_equal(AMlistItemValue(AMlistItemsNext(&list_items, 1)).str,
|
|
"c");
|
|
assert_null(AMlistItemsNext(&list_items, 1));
|
|
}
|
|
/* doc.push(sublist, 3, "timestamp"); */
|
|
AMfree(AMlistPutTimestamp(doc, sublist, SIZE_MAX, true, 3));
|
|
/* assert.deepEqual(doc.materialize(), { letters: ["b", "a", "c", new Date(3)] })*/
|
|
doc_items = AMpush(&stack,
|
|
AMmapRange(doc, AM_ROOT, NULL, NULL, NULL),
|
|
AM_VALUE_MAP_ITEMS,
|
|
cmocka_cb).map_items;
|
|
doc_item = AMmapItemsNext(&doc_items, 1);
|
|
assert_string_equal(AMmapItemKey(doc_item), "letters");
|
|
{
|
|
AMlistItems list_items = AMpush(
|
|
&stack,
|
|
AMlistRange(doc, AMmapItemObjId(doc_item), 0, SIZE_MAX, NULL),
|
|
AM_VALUE_LIST_ITEMS,
|
|
cmocka_cb).list_items;
|
|
assert_string_equal(AMlistItemValue(AMlistItemsNext(&list_items, 1)).str,
|
|
"b");
|
|
assert_string_equal(AMlistItemValue(AMlistItemsNext(&list_items, 1)).str,
|
|
"a");
|
|
assert_string_equal(AMlistItemValue(AMlistItemsNext(&list_items, 1)).str,
|
|
"c");
|
|
assert_int_equal(AMlistItemValue(AMlistItemsNext(&list_items, 1)).timestamp,
|
|
3);
|
|
assert_null(AMlistItemsNext(&list_items, 1));
|
|
}
|
|
/* doc.splice(sublist, 1, 1, ["d", "e", "f"]); */
|
|
static AMvalue const DATA[] = {{.str_tag = AM_VALUE_STR, .str = "d"},
|
|
{.str_tag = AM_VALUE_STR, .str = "e"},
|
|
{.str_tag = AM_VALUE_STR, .str = "f"}};
|
|
AMfree(AMsplice(doc, sublist, 1, 1, DATA, sizeof(DATA)/sizeof(AMvalue)));
|
|
/* assert.deepEqual(doc.materialize(), { letters: ["b", "d", "e", "f", "c", new Date(3)] })*/
|
|
doc_items = AMpush(&stack,
|
|
AMmapRange(doc, AM_ROOT, NULL, NULL, NULL),
|
|
AM_VALUE_MAP_ITEMS,
|
|
cmocka_cb).map_items;
|
|
doc_item = AMmapItemsNext(&doc_items, 1);
|
|
assert_string_equal(AMmapItemKey(doc_item), "letters");
|
|
{
|
|
AMlistItems list_items = AMpush(
|
|
&stack,
|
|
AMlistRange(doc, AMmapItemObjId(doc_item), 0, SIZE_MAX, NULL),
|
|
AM_VALUE_LIST_ITEMS,
|
|
cmocka_cb).list_items;
|
|
assert_string_equal(AMlistItemValue(AMlistItemsNext(&list_items, 1)).str,
|
|
"b");
|
|
assert_string_equal(AMlistItemValue(AMlistItemsNext(&list_items, 1)).str,
|
|
"d");
|
|
assert_string_equal(AMlistItemValue(AMlistItemsNext(&list_items, 1)).str,
|
|
"e");
|
|
assert_string_equal(AMlistItemValue(AMlistItemsNext(&list_items, 1)).str,
|
|
"f");
|
|
assert_string_equal(AMlistItemValue(AMlistItemsNext(&list_items, 1)).str,
|
|
"c");
|
|
assert_int_equal(AMlistItemValue(AMlistItemsNext(&list_items, 1)).timestamp,
|
|
3);
|
|
assert_null(AMlistItemsNext(&list_items, 1));
|
|
}
|
|
/* doc.put(sublist, 0, "z"); */
|
|
AMfree(AMlistPutStr(doc, sublist, 0, false, "z"));
|
|
/* assert.deepEqual(doc.materialize(), { letters: ["z", "d", "e", "f", "c", new Date(3)] })*/
|
|
doc_items = AMpush(&stack,
|
|
AMmapRange(doc, AM_ROOT, NULL, NULL, NULL),
|
|
AM_VALUE_MAP_ITEMS,
|
|
cmocka_cb).map_items;
|
|
doc_item = AMmapItemsNext(&doc_items, 1);
|
|
assert_string_equal(AMmapItemKey(doc_item), "letters");
|
|
{
|
|
AMlistItems list_items = AMpush(
|
|
&stack,
|
|
AMlistRange(doc, AMmapItemObjId(doc_item), 0, SIZE_MAX, NULL),
|
|
AM_VALUE_LIST_ITEMS,
|
|
cmocka_cb).list_items;
|
|
assert_string_equal(AMlistItemValue(AMlistItemsNext(&list_items, 1)).str,
|
|
"z");
|
|
assert_string_equal(AMlistItemValue(AMlistItemsNext(&list_items, 1)).str,
|
|
"d");
|
|
assert_string_equal(AMlistItemValue(AMlistItemsNext(&list_items, 1)).str,
|
|
"e");
|
|
assert_string_equal(AMlistItemValue(AMlistItemsNext(&list_items, 1)).str,
|
|
"f");
|
|
assert_string_equal(AMlistItemValue(AMlistItemsNext(&list_items, 1)).str,
|
|
"c");
|
|
assert_int_equal(AMlistItemValue(AMlistItemsNext(&list_items, 1)).timestamp,
|
|
3);
|
|
assert_null(AMlistItemsNext(&list_items, 1));
|
|
}
|
|
/* assert.deepEqual(doc.materialize(sublist), ["z", "d", "e", "f", "c", new Date(3)])*/
|
|
AMlistItems sublist_items = AMpush(
|
|
&stack,
|
|
AMlistRange(doc, sublist, 0, SIZE_MAX, NULL),
|
|
AM_VALUE_LIST_ITEMS,
|
|
cmocka_cb).list_items;
|
|
assert_string_equal(AMlistItemValue(AMlistItemsNext(&sublist_items, 1)).str,
|
|
"z");
|
|
assert_string_equal(AMlistItemValue(AMlistItemsNext(&sublist_items, 1)).str,
|
|
"d");
|
|
assert_string_equal(AMlistItemValue(AMlistItemsNext(&sublist_items, 1)).str,
|
|
"e");
|
|
assert_string_equal(AMlistItemValue(AMlistItemsNext(&sublist_items, 1)).str,
|
|
"f");
|
|
assert_string_equal(AMlistItemValue(AMlistItemsNext(&sublist_items, 1)).str,
|
|
"c");
|
|
assert_int_equal(AMlistItemValue(AMlistItemsNext(&sublist_items, 1)).timestamp,
|
|
3);
|
|
assert_null(AMlistItemsNext(&sublist_items, 1));
|
|
/* assert.deepEqual(doc.length(sublist), 6) */
|
|
assert_int_equal(AMobjSize(doc, sublist, NULL), 6);
|
|
/* assert.deepEqual(doc.materialize("/", heads), { letters: ["b", "a", "c"] })*/
|
|
doc_items = AMpush(&stack,
|
|
AMmapRange(doc, AM_ROOT, NULL, NULL, &heads),
|
|
AM_VALUE_MAP_ITEMS,
|
|
cmocka_cb).map_items;
|
|
doc_item = AMmapItemsNext(&doc_items, 1);
|
|
assert_string_equal(AMmapItemKey(doc_item), "letters");
|
|
{
|
|
AMlistItems list_items = AMpush(
|
|
&stack,
|
|
AMlistRange(doc, AMmapItemObjId(doc_item), 0, SIZE_MAX, &heads),
|
|
AM_VALUE_LIST_ITEMS,
|
|
cmocka_cb).list_items;
|
|
assert_string_equal(AMlistItemValue(AMlistItemsNext(&list_items, 1)).str,
|
|
"b");
|
|
assert_string_equal(AMlistItemValue(AMlistItemsNext(&list_items, 1)).str,
|
|
"a");
|
|
assert_string_equal(AMlistItemValue(AMlistItemsNext(&list_items, 1)).str,
|
|
"c");
|
|
assert_null(AMlistItemsNext(&list_items, 1));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief should be able to delete non-existent props
|
|
*/
|
|
static void test_should_be_able_to_delete_non_existent_props(void** state) {
|
|
AMresultStack* stack = *state;
|
|
/* const doc = create() */
|
|
AMdoc* const doc = AMpush(&stack, AMcreate(NULL), AM_VALUE_DOC, cmocka_cb).doc;
|
|
/* */
|
|
/* doc.put("_root", "foo", "bar") */
|
|
AMfree(AMmapPutStr(doc, AM_ROOT, "foo", "bar"));
|
|
/* doc.put("_root", "bip", "bap") */
|
|
AMfree(AMmapPutStr(doc, AM_ROOT, "bip", "bap"));
|
|
/* const hash1 = doc.commit() */
|
|
AMchangeHashes const hash1 = AMpush(&stack,
|
|
AMcommit(doc, NULL, NULL),
|
|
AM_VALUE_CHANGE_HASHES,
|
|
cmocka_cb).change_hashes;
|
|
/* */
|
|
/* assert.deepEqual(doc.keys("_root"), ["bip", "foo"]) */
|
|
AMstrs keys = AMpush(&stack,
|
|
AMkeys(doc, AM_ROOT, NULL),
|
|
AM_VALUE_STRS,
|
|
cmocka_cb).strs;
|
|
assert_string_equal(AMstrsNext(&keys, 1), "bip");
|
|
assert_string_equal(AMstrsNext(&keys, 1), "foo");
|
|
/* */
|
|
/* doc.delete("_root", "foo") */
|
|
AMfree(AMmapDelete(doc, AM_ROOT, "foo"));
|
|
/* doc.delete("_root", "baz") */
|
|
AMfree(AMmapDelete(doc, AM_ROOT, "baz"));
|
|
/* const hash2 = doc.commit() */
|
|
AMchangeHashes const hash2 = AMpush(&stack,
|
|
AMcommit(doc, NULL, NULL),
|
|
AM_VALUE_CHANGE_HASHES,
|
|
cmocka_cb).change_hashes;
|
|
/* */
|
|
/* assert.deepEqual(doc.keys("_root"), ["bip"]) */
|
|
keys = AMpush(&stack,
|
|
AMkeys(doc, AM_ROOT, NULL),
|
|
AM_VALUE_STRS,
|
|
cmocka_cb).strs;
|
|
assert_string_equal(AMstrsNext(&keys, 1), "bip");
|
|
/* assert.deepEqual(doc.keys("_root", [hash1]), ["bip", "foo"]) */
|
|
keys = AMpush(&stack,
|
|
AMkeys(doc, AM_ROOT, &hash1),
|
|
AM_VALUE_STRS,
|
|
cmocka_cb).strs;
|
|
assert_string_equal(AMstrsNext(&keys, 1), "bip");
|
|
assert_string_equal(AMstrsNext(&keys, 1), "foo");
|
|
/* assert.deepEqual(doc.keys("_root", [hash2]), ["bip"]) */
|
|
keys = AMpush(&stack,
|
|
AMkeys(doc, AM_ROOT, &hash2),
|
|
AM_VALUE_STRS,
|
|
cmocka_cb).strs;
|
|
assert_string_equal(AMstrsNext(&keys, 1), "bip");
|
|
}
|
|
|
|
/**
|
|
* \brief should be able to del
|
|
*/
|
|
static void test_should_be_able_to_del(void **state) {
|
|
AMresultStack* stack = *state;
|
|
/* const doc = create() */
|
|
AMdoc* const doc = AMpush(&stack, AMcreate(NULL), AM_VALUE_DOC, cmocka_cb).doc;
|
|
/* const root = "_root" */
|
|
/* */
|
|
/* doc.put(root, "xxx", "xxx"); */
|
|
AMfree(AMmapPutStr(doc, AM_ROOT, "xxx", "xxx"));
|
|
/* assert.deepEqual(doc.getWithType(root, "xxx"), ["str", "xxx"]) */
|
|
assert_string_equal(AMpush(&stack,
|
|
AMmapGet(doc, AM_ROOT, "xxx", NULL),
|
|
AM_VALUE_STR,
|
|
cmocka_cb).str, "xxx");
|
|
/* doc.delete(root, "xxx"); */
|
|
AMfree(AMmapDelete(doc, AM_ROOT, "xxx"));
|
|
/* assert.deepEqual(doc.getWithType(root, "xxx"), undefined) */
|
|
AMpush(&stack,
|
|
AMmapGet(doc, AM_ROOT, "xxx", NULL),
|
|
AM_VALUE_VOID,
|
|
cmocka_cb);
|
|
}
|
|
|
|
/**
|
|
* \brief should be able to use counters
|
|
*/
|
|
static void test_should_be_able_to_use_counters(void** state) {
|
|
AMresultStack* stack = *state;
|
|
/* const doc = create() */
|
|
AMdoc* const doc = AMpush(&stack, AMcreate(NULL), AM_VALUE_DOC, cmocka_cb).doc;
|
|
/* const root = "_root" */
|
|
/* */
|
|
/* doc.put(root, "counter", 10, "counter"); */
|
|
AMfree(AMmapPutCounter(doc, AM_ROOT, "counter", 10));
|
|
/* assert.deepEqual(doc.getWithType(root, "counter"), ["counter", 10]) */
|
|
assert_int_equal(AMpush(&stack,
|
|
AMmapGet(doc, AM_ROOT, "counter", NULL),
|
|
AM_VALUE_COUNTER,
|
|
cmocka_cb).counter, 10);
|
|
/* doc.increment(root, "counter", 10); */
|
|
AMfree(AMmapIncrement(doc, AM_ROOT, "counter", 10));
|
|
/* assert.deepEqual(doc.getWithType(root, "counter"), ["counter", 20]) */
|
|
assert_int_equal(AMpush(&stack,
|
|
AMmapGet(doc, AM_ROOT, "counter", NULL),
|
|
AM_VALUE_COUNTER,
|
|
cmocka_cb).counter, 20);
|
|
/* doc.increment(root, "counter", -5); */
|
|
AMfree(AMmapIncrement(doc, AM_ROOT, "counter", -5));
|
|
/* assert.deepEqual(doc.getWithType(root, "counter"), ["counter", 15]) */
|
|
assert_int_equal(AMpush(&stack,
|
|
AMmapGet(doc, AM_ROOT, "counter", NULL),
|
|
AM_VALUE_COUNTER,
|
|
cmocka_cb).counter, 15);
|
|
}
|
|
|
|
/**
|
|
* \brief should be able to splice text
|
|
*/
|
|
static void test_should_be_able_to_splice_text(void** state) {
|
|
AMresultStack* stack = *state;
|
|
/* const doc = create() */
|
|
AMdoc* const doc = AMpush(&stack, AMcreate(NULL), AM_VALUE_DOC, cmocka_cb).doc;
|
|
/* const root = "_root"; */
|
|
/* */
|
|
/* const text = doc.putObject(root, "text", ""); */
|
|
AMobjId const* const text = AMpush(
|
|
&stack,
|
|
AMmapPutObject(doc, AM_ROOT, "text", AM_OBJ_TYPE_TEXT),
|
|
AM_VALUE_OBJ_ID,
|
|
cmocka_cb).obj_id;
|
|
/* doc.splice(text, 0, 0, "hello ") */
|
|
AMfree(AMspliceText(doc, text, 0, 0, "hello "));
|
|
/* doc.splice(text, 6, 0, ["w", "o", "r", "l", "d"]) */
|
|
static AMvalue const WORLD[] = {{.str_tag = AM_VALUE_STR, .str = "w"},
|
|
{.str_tag = AM_VALUE_STR, .str = "o"},
|
|
{.str_tag = AM_VALUE_STR, .str = "r"},
|
|
{.str_tag = AM_VALUE_STR, .str = "l"},
|
|
{.str_tag = AM_VALUE_STR, .str = "d"}};
|
|
AMfree(AMsplice(doc, text, 6, 0, WORLD, sizeof(WORLD)/sizeof(AMvalue)));
|
|
/* doc.splice(text, 11, 0, ["!", "?"]) */
|
|
static AMvalue const INTERROBANG[] = {{.str_tag = AM_VALUE_STR, .str = "!"},
|
|
{.str_tag = AM_VALUE_STR, .str = "?"}};
|
|
AMfree(AMsplice(doc, text, 11, 0, INTERROBANG, sizeof(INTERROBANG)/sizeof(AMvalue)));
|
|
/* assert.deepEqual(doc.getWithType(text, 0), ["str", "h"]) */
|
|
assert_string_equal(AMpush(&stack,
|
|
AMlistGet(doc, text, 0, NULL),
|
|
AM_VALUE_STR,
|
|
cmocka_cb).str, "h");
|
|
/* assert.deepEqual(doc.getWithType(text, 1), ["str", "e"]) */
|
|
assert_string_equal(AMpush(&stack,
|
|
AMlistGet(doc, text, 1, NULL),
|
|
AM_VALUE_STR,
|
|
cmocka_cb).str, "e");
|
|
/* assert.deepEqual(doc.getWithType(text, 9), ["str", "l"]) */
|
|
assert_string_equal(AMpush(&stack,
|
|
AMlistGet(doc, text, 9, NULL),
|
|
AM_VALUE_STR,
|
|
cmocka_cb).str, "l");
|
|
/* assert.deepEqual(doc.getWithType(text, 10), ["str", "d"]) */
|
|
assert_string_equal(AMpush(&stack,
|
|
AMlistGet(doc, text, 10, NULL),
|
|
AM_VALUE_STR,
|
|
cmocka_cb).str, "d");
|
|
/* assert.deepEqual(doc.getWithType(text, 11), ["str", "!"]) */
|
|
assert_string_equal(AMpush(&stack,
|
|
AMlistGet(doc, text, 11, NULL),
|
|
AM_VALUE_STR,
|
|
cmocka_cb).str, "!");
|
|
/* assert.deepEqual(doc.getWithType(text, 12), ["str", "?"]) */
|
|
assert_string_equal(AMpush(&stack,
|
|
AMlistGet(doc, text, 12, NULL),
|
|
AM_VALUE_STR,
|
|
cmocka_cb).str, "?");
|
|
}
|
|
|
|
/**
|
|
* \brief should be able to insert objects into text
|
|
*/
|
|
static void test_should_be_able_to_insert_objects_into_text(void** state) {
|
|
AMresultStack* stack = *state;
|
|
/* const doc = create() */
|
|
AMdoc* const doc = AMpush(&stack, AMcreate(NULL), AM_VALUE_DOC, cmocka_cb).doc;
|
|
/* const text = doc.putObject("/", "text", "Hello world"); */
|
|
AMobjId const* const text = AMpush(
|
|
&stack,
|
|
AMmapPutObject(doc, AM_ROOT, "text", AM_OBJ_TYPE_TEXT),
|
|
AM_VALUE_OBJ_ID,
|
|
cmocka_cb).obj_id;
|
|
AMfree(AMspliceText(doc, text, 0, 0, "Hello world"));
|
|
/* const obj = doc.insertObject(text, 6, { hello: "world" }); */
|
|
AMobjId const* const obj = AMpush(
|
|
&stack,
|
|
AMlistPutObject(doc, text, 6, true, AM_OBJ_TYPE_MAP),
|
|
AM_VALUE_OBJ_ID,
|
|
cmocka_cb).obj_id;
|
|
AMfree(AMmapPutStr(doc, obj, "hello", "world"));
|
|
/* assert.deepEqual(doc.text(text), "Hello \ufffcworld"); */
|
|
assert_string_equal(AMpush(&stack,
|
|
AMtext(doc, text, NULL),
|
|
AM_VALUE_STR,
|
|
cmocka_cb).str, "Hello \ufffcworld");
|
|
/* assert.deepEqual(doc.getWithType(text, 6), ["map", obj]); */
|
|
assert_true(AMobjIdEqual(AMpush(&stack,
|
|
AMlistGet(doc, text, 6, NULL),
|
|
AM_VALUE_OBJ_ID,
|
|
cmocka_cb).obj_id, obj));
|
|
/* assert.deepEqual(doc.getWithType(obj, "hello"), ["str", "world"]); */
|
|
assert_string_equal(AMpush(&stack,
|
|
AMmapGet(doc, obj, "hello", NULL),
|
|
AM_VALUE_STR,
|
|
cmocka_cb).str, "world");
|
|
}
|
|
|
|
/**
|
|
* \brief should be able save all or incrementally
|
|
*/
|
|
static void test_should_be_able_to_save_all_or_incrementally(void** state) {
|
|
AMresultStack* stack = *state;
|
|
/* const doc = create() */
|
|
AMdoc* const doc = AMpush(&stack, AMcreate(NULL), AM_VALUE_DOC, cmocka_cb).doc;
|
|
/* */
|
|
/* doc.put("_root", "foo", 1) */
|
|
AMfree(AMmapPutInt(doc, AM_ROOT, "foo", 1));
|
|
/* */
|
|
/* const save1 = doc.save() */
|
|
AMbyteSpan const save1 = AMpush(&stack,
|
|
AMsave(doc),
|
|
AM_VALUE_BYTES,
|
|
cmocka_cb).bytes;
|
|
/* */
|
|
/* doc.put("_root", "bar", 2) */
|
|
AMfree(AMmapPutInt(doc, AM_ROOT, "bar", 2));
|
|
/* */
|
|
/* const saveMidway = doc.clone().save(); */
|
|
AMbyteSpan const saveMidway = AMpush(&stack,
|
|
AMsave(
|
|
AMpush(&stack,
|
|
AMclone(doc),
|
|
AM_VALUE_DOC,
|
|
cmocka_cb).doc),
|
|
AM_VALUE_BYTES,
|
|
cmocka_cb).bytes;
|
|
/* */
|
|
/* const save2 = doc.saveIncremental(); */
|
|
AMbyteSpan const save2 = AMpush(&stack,
|
|
AMsaveIncremental(doc),
|
|
AM_VALUE_BYTES,
|
|
cmocka_cb).bytes;
|
|
/* */
|
|
/* doc.put("_root", "baz", 3); */
|
|
AMfree(AMmapPutInt(doc, AM_ROOT, "baz", 3));
|
|
/* */
|
|
/* const save3 = doc.saveIncremental(); */
|
|
AMbyteSpan const save3 = AMpush(&stack,
|
|
AMsaveIncremental(doc),
|
|
AM_VALUE_BYTES,
|
|
cmocka_cb).bytes;
|
|
/* */
|
|
/* const saveA = doc.save(); */
|
|
AMbyteSpan const saveA = AMpush(&stack,
|
|
AMsave(doc),
|
|
AM_VALUE_BYTES,
|
|
cmocka_cb).bytes;
|
|
/* const saveB = new Uint8Array([...save1, ...save2, ...save3]); */
|
|
size_t const saveB_count = save1.count + save2.count + save3.count;
|
|
uint8_t* const saveB_src = test_malloc(saveB_count);
|
|
memcpy(saveB_src, save1.src, save1.count);
|
|
memcpy(saveB_src + save1.count, save2.src, save2.count);
|
|
memcpy(saveB_src + save1.count + save2.count, save3.src, save3.count);
|
|
/* */
|
|
/* assert.notDeepEqual(saveA, saveB); */
|
|
assert_memory_not_equal(saveA.src, saveB_src, saveA.count);
|
|
/* */
|
|
/* const docA = load(saveA); */
|
|
AMdoc* const docA = AMpush(&stack,
|
|
AMload(saveA.src, saveA.count),
|
|
AM_VALUE_DOC,
|
|
cmocka_cb).doc;
|
|
/* const docB = load(saveB); */
|
|
AMdoc* const docB = AMpush(&stack,
|
|
AMload(saveB_src, saveB_count),
|
|
AM_VALUE_DOC,
|
|
cmocka_cb).doc;
|
|
test_free(saveB_src);
|
|
/* const docC = load(saveMidway) */
|
|
AMdoc* const docC = AMpush(&stack,
|
|
AMload(saveMidway.src, saveMidway.count),
|
|
AM_VALUE_DOC,
|
|
cmocka_cb).doc;
|
|
/* docC.loadIncremental(save3) */
|
|
AMfree(AMloadIncremental(docC, save3.src, save3.count));
|
|
/* */
|
|
/* assert.deepEqual(docA.keys("_root"), docB.keys("_root")); */
|
|
AMstrs const keysA = AMpush(&stack,
|
|
AMkeys(docA, AM_ROOT, NULL),
|
|
AM_VALUE_STRS,
|
|
cmocka_cb).strs;
|
|
AMstrs const keysB = AMpush(&stack,
|
|
AMkeys(docB, AM_ROOT, NULL),
|
|
AM_VALUE_STRS,
|
|
cmocka_cb).strs;
|
|
assert_int_equal(AMstrsCmp(&keysA, &keysB), 0);
|
|
/* assert.deepEqual(docA.save(), docB.save()); */
|
|
AMbyteSpan const save = AMpush(&stack,
|
|
AMsave(docA),
|
|
AM_VALUE_BYTES,
|
|
cmocka_cb).bytes;
|
|
assert_memory_equal(save.src,
|
|
AMpush(&stack,
|
|
AMsave(docB),
|
|
AM_VALUE_BYTES,
|
|
cmocka_cb).bytes.src,
|
|
save.count);
|
|
/* assert.deepEqual(docA.save(), docC.save()); */
|
|
assert_memory_equal(save.src,
|
|
AMpush(&stack,
|
|
AMsave(docC),
|
|
AM_VALUE_BYTES,
|
|
cmocka_cb).bytes.src,
|
|
save.count);
|
|
}
|
|
|
|
/**
|
|
* \brief should be able to splice text #2
|
|
*/
|
|
static void test_should_be_able_to_splice_text_2(void** state) {
|
|
AMresultStack* stack = *state;
|
|
/* const doc = create() */
|
|
AMdoc* const doc = AMpush(&stack, AMcreate(NULL), AM_VALUE_DOC, cmocka_cb).doc;
|
|
/* const text = doc.putObject("_root", "text", ""); */
|
|
AMobjId const* const text = AMpush(
|
|
&stack,
|
|
AMmapPutObject(doc, AM_ROOT, "text", AM_OBJ_TYPE_TEXT),
|
|
AM_VALUE_OBJ_ID,
|
|
cmocka_cb).obj_id;
|
|
/* doc.splice(text, 0, 0, "hello world"); */
|
|
AMfree(AMspliceText(doc, text, 0, 0, "hello world"));
|
|
/* const hash1 = doc.commit(); */
|
|
AMchangeHashes const hash1 = AMpush(&stack,
|
|
AMcommit(doc, NULL, NULL),
|
|
AM_VALUE_CHANGE_HASHES,
|
|
cmocka_cb).change_hashes;
|
|
/* doc.splice(text, 6, 0, "big bad "); */
|
|
AMfree(AMspliceText(doc, text, 6, 0, "big bad "));
|
|
/* const hash2 = doc.commit(); */
|
|
AMchangeHashes const hash2 = AMpush(&stack,
|
|
AMcommit(doc, NULL, NULL),
|
|
AM_VALUE_CHANGE_HASHES,
|
|
cmocka_cb).change_hashes;
|
|
/* assert.strictEqual(doc.text(text), "hello big bad world") */
|
|
assert_string_equal(AMpush(&stack,
|
|
AMtext(doc, text, NULL),
|
|
AM_VALUE_STR,
|
|
cmocka_cb).str, "hello big bad world");
|
|
/* assert.strictEqual(doc.length(text), 19) */
|
|
assert_int_equal(AMobjSize(doc, text, NULL), 19);
|
|
/* assert.strictEqual(doc.text(text, [hash1]), "hello world") */
|
|
assert_string_equal(AMpush(&stack,
|
|
AMtext(doc, text, &hash1),
|
|
AM_VALUE_STR,
|
|
cmocka_cb).str, "hello world");
|
|
/* assert.strictEqual(doc.length(text, [hash1]), 11) */
|
|
assert_int_equal(AMobjSize(doc, text, &hash1), 11);
|
|
/* assert.strictEqual(doc.text(text, [hash2]), "hello big bad world") */
|
|
assert_string_equal(AMpush(&stack,
|
|
AMtext(doc, text, &hash2),
|
|
AM_VALUE_STR,
|
|
cmocka_cb).str, "hello big bad world");
|
|
/* assert.strictEqual(doc.length(text, [hash2]), 19) */
|
|
assert_int_equal(AMobjSize(doc, text, &hash2), 19);
|
|
}
|
|
|
|
/**
|
|
* \brief local inc increments all visible counters in a map
|
|
*/
|
|
static void test_local_inc_increments_all_visible_counters_in_a_map(void** state) {
|
|
AMresultStack* stack = *state;
|
|
/* const doc1 = create("aaaa") */
|
|
AMdoc* const doc1 = AMpush(&stack,
|
|
AMcreate(AMpush(&stack,
|
|
AMactorIdInitStr("aaaa"),
|
|
AM_VALUE_ACTOR_ID,
|
|
cmocka_cb).actor_id),
|
|
AM_VALUE_DOC,
|
|
cmocka_cb).doc;
|
|
/* doc1.put("_root", "hello", "world") */
|
|
AMfree(AMmapPutStr(doc1, AM_ROOT, "hello", "world"));
|
|
/* const doc2 = load(doc1.save(), "bbbb"); */
|
|
AMbyteSpan const save = AMpush(&stack,
|
|
AMsave(doc1),
|
|
AM_VALUE_BYTES,
|
|
cmocka_cb).bytes;
|
|
AMdoc* const doc2 = AMpush(&stack,
|
|
AMload(save.src, save.count),
|
|
AM_VALUE_DOC,
|
|
cmocka_cb).doc;
|
|
AMfree(AMsetActorId(doc2, AMpush(&stack,
|
|
AMactorIdInitStr("bbbb"),
|
|
AM_VALUE_ACTOR_ID,
|
|
cmocka_cb).actor_id));
|
|
/* const doc3 = load(doc1.save(), "cccc"); */
|
|
AMdoc* const doc3 = AMpush(&stack,
|
|
AMload(save.src, save.count),
|
|
AM_VALUE_DOC,
|
|
cmocka_cb).doc;
|
|
AMfree(AMsetActorId(doc3, AMpush(&stack,
|
|
AMactorIdInitStr("cccc"),
|
|
AM_VALUE_ACTOR_ID,
|
|
cmocka_cb).actor_id));
|
|
/* let heads = doc1.getHeads() */
|
|
AMchangeHashes const heads1 = AMpush(&stack,
|
|
AMgetHeads(doc1),
|
|
AM_VALUE_CHANGE_HASHES,
|
|
cmocka_cb).change_hashes;
|
|
/* doc1.put("_root", "cnt", 20) */
|
|
AMfree(AMmapPutInt(doc1, AM_ROOT, "cnt", 20));
|
|
/* doc2.put("_root", "cnt", 0, "counter") */
|
|
AMfree(AMmapPutCounter(doc2, AM_ROOT, "cnt", 0));
|
|
/* doc3.put("_root", "cnt", 10, "counter") */
|
|
AMfree(AMmapPutCounter(doc3, AM_ROOT, "cnt", 10));
|
|
/* doc1.applyChanges(doc2.getChanges(heads)) */
|
|
AMchanges const changes2 = AMpush(&stack,
|
|
AMgetChanges(doc2, &heads1),
|
|
AM_VALUE_CHANGES,
|
|
cmocka_cb).changes;
|
|
AMfree(AMapplyChanges(doc1, &changes2));
|
|
/* doc1.applyChanges(doc3.getChanges(heads)) */
|
|
AMchanges const changes3 = AMpush(&stack,
|
|
AMgetChanges(doc3, &heads1),
|
|
AM_VALUE_CHANGES,
|
|
cmocka_cb).changes;
|
|
AMfree(AMapplyChanges(doc1, &changes3));
|
|
/* let result = doc1.getAll("_root", "cnt") */
|
|
AMobjItems result = AMpush(&stack,
|
|
AMmapGetAll(doc1, AM_ROOT, "cnt", NULL),
|
|
AM_VALUE_OBJ_ITEMS,
|
|
cmocka_cb).obj_items;
|
|
/* assert.deepEqual(result, [
|
|
['int', 20, '2@aaaa'],
|
|
['counter', 0, '2@bbbb'],
|
|
['counter', 10, '2@cccc'],
|
|
]) */
|
|
AMobjItem const* result_item = AMobjItemsNext(&result, 1);
|
|
assert_int_equal(AMobjItemValue(result_item).int_, 20);
|
|
assert_int_equal(AMobjIdCounter(AMobjItemObjId(result_item)), 2);
|
|
assert_string_equal(AMactorIdStr(AMobjIdActorId(AMobjItemObjId(result_item))),
|
|
"aaaa");
|
|
result_item = AMobjItemsNext(&result, 1);
|
|
assert_int_equal(AMobjItemValue(result_item).counter, 0);
|
|
assert_int_equal(AMobjIdCounter(AMobjItemObjId(result_item)), 2);
|
|
assert_string_equal(AMactorIdStr(AMobjIdActorId(AMobjItemObjId(result_item))),
|
|
"bbbb");
|
|
result_item = AMobjItemsNext(&result, 1);
|
|
assert_int_equal(AMobjItemValue(result_item).counter, 10);
|
|
assert_int_equal(AMobjIdCounter(AMobjItemObjId(result_item)), 2);
|
|
assert_string_equal(AMactorIdStr(AMobjIdActorId(AMobjItemObjId(result_item))),
|
|
"cccc");
|
|
/* doc1.increment("_root", "cnt", 5) */
|
|
AMfree(AMmapIncrement(doc1, AM_ROOT, "cnt", 5));
|
|
/* result = doc1.getAll("_root", "cnt") */
|
|
result = AMpush(&stack,
|
|
AMmapGetAll(doc1, AM_ROOT, "cnt", NULL),
|
|
AM_VALUE_OBJ_ITEMS,
|
|
cmocka_cb).obj_items;
|
|
/* assert.deepEqual(result, [
|
|
['counter', 5, '2@bbbb'],
|
|
['counter', 15, '2@cccc'],
|
|
]) */
|
|
result_item = AMobjItemsNext(&result, 1);
|
|
assert_int_equal(AMobjItemValue(result_item).counter, 5);
|
|
assert_int_equal(AMobjIdCounter(AMobjItemObjId(result_item)), 2);
|
|
assert_string_equal(AMactorIdStr(AMobjIdActorId(AMobjItemObjId(result_item))),
|
|
"bbbb");
|
|
result_item = AMobjItemsNext(&result, 1);
|
|
assert_int_equal(AMobjItemValue(result_item).counter, 15);
|
|
assert_int_equal(AMobjIdCounter(AMobjItemObjId(result_item)), 2);
|
|
assert_string_equal(AMactorIdStr(AMobjIdActorId(AMobjItemObjId(result_item))),
|
|
"cccc");
|
|
/* */
|
|
/* const save1 = doc1.save() */
|
|
AMbyteSpan const save1 = AMpush(&stack,
|
|
AMsave(doc1),
|
|
AM_VALUE_BYTES,
|
|
cmocka_cb).bytes;
|
|
/* const doc4 = load(save1) */
|
|
AMdoc* const doc4 = AMpush(&stack,
|
|
AMload(save1.src, save1.count),
|
|
AM_VALUE_DOC,
|
|
cmocka_cb).doc;
|
|
/* assert.deepEqual(doc4.save(), save1); */
|
|
assert_memory_equal(AMpush(&stack,
|
|
AMsave(doc4),
|
|
AM_VALUE_BYTES,
|
|
cmocka_cb).bytes.src,
|
|
save1.src,
|
|
save1.count);
|
|
}
|
|
|
|
/**
|
|
* \brief local inc increments all visible counters in a sequence
|
|
*/
|
|
static void test_local_inc_increments_all_visible_counters_in_a_sequence(void** state) {
|
|
AMresultStack* stack = *state;
|
|
/* const doc1 = create("aaaa") */
|
|
AMdoc* const doc1 = AMpush(&stack,
|
|
AMcreate(AMpush(&stack,
|
|
AMactorIdInitStr("aaaa"),
|
|
AM_VALUE_ACTOR_ID,
|
|
cmocka_cb).actor_id),
|
|
AM_VALUE_DOC,
|
|
cmocka_cb).doc;
|
|
/* const seq = doc1.putObject("_root", "seq", []) */
|
|
AMobjId const* const seq = AMpush(
|
|
&stack,
|
|
AMmapPutObject(doc1, AM_ROOT, "seq", AM_OBJ_TYPE_LIST),
|
|
AM_VALUE_OBJ_ID,
|
|
cmocka_cb).obj_id;
|
|
/* doc1.insert(seq, 0, "hello") */
|
|
AMfree(AMlistPutStr(doc1, seq, 0, true, "hello"));
|
|
/* const doc2 = load(doc1.save(), "bbbb"); */
|
|
AMbyteSpan const save1 = AMpush(&stack,
|
|
AMsave(doc1),
|
|
AM_VALUE_BYTES,
|
|
cmocka_cb).bytes;
|
|
AMdoc* const doc2 = AMpush(&stack,
|
|
AMload(save1.src, save1.count),
|
|
AM_VALUE_DOC,
|
|
cmocka_cb).doc;
|
|
AMfree(AMsetActorId(doc2, AMpush(&stack,
|
|
AMactorIdInitStr("bbbb"),
|
|
AM_VALUE_ACTOR_ID,
|
|
cmocka_cb).actor_id));
|
|
/* const doc3 = load(doc1.save(), "cccc"); */
|
|
AMdoc* const doc3 = AMpush(&stack,
|
|
AMload(save1.src, save1.count),
|
|
AM_VALUE_DOC,
|
|
cmocka_cb).doc;
|
|
AMfree(AMsetActorId(doc3, AMpush(&stack,
|
|
AMactorIdInitStr("cccc"),
|
|
AM_VALUE_ACTOR_ID,
|
|
cmocka_cb).actor_id));
|
|
/* let heads = doc1.getHeads() */
|
|
AMchangeHashes const heads1 = AMpush(&stack,
|
|
AMgetHeads(doc1),
|
|
AM_VALUE_CHANGE_HASHES,
|
|
cmocka_cb).change_hashes;
|
|
/* doc1.put(seq, 0, 20) */
|
|
AMfree(AMlistPutInt(doc1, seq, 0, false, 20));
|
|
/* doc2.put(seq, 0, 0, "counter") */
|
|
AMfree(AMlistPutCounter(doc2, seq, 0, false, 0));
|
|
/* doc3.put(seq, 0, 10, "counter") */
|
|
AMfree(AMlistPutCounter(doc3, seq, 0, false, 10));
|
|
/* doc1.applyChanges(doc2.getChanges(heads)) */
|
|
AMchanges const changes2 = AMpush(&stack,
|
|
AMgetChanges(doc2, &heads1),
|
|
AM_VALUE_CHANGES,
|
|
cmocka_cb).changes;
|
|
AMfree(AMapplyChanges(doc1, &changes2));
|
|
/* doc1.applyChanges(doc3.getChanges(heads)) */
|
|
AMchanges const changes3 = AMpush(&stack,
|
|
AMgetChanges(doc3, &heads1),
|
|
AM_VALUE_CHANGES,
|
|
cmocka_cb).changes;
|
|
AMfree(AMapplyChanges(doc1, &changes3));
|
|
/* let result = doc1.getAll(seq, 0) */
|
|
AMobjItems result = AMpush(&stack,
|
|
AMlistGetAll(doc1, seq, 0, NULL),
|
|
AM_VALUE_OBJ_ITEMS,
|
|
cmocka_cb).obj_items;
|
|
/* assert.deepEqual(result, [
|
|
['int', 20, '3@aaaa'],
|
|
['counter', 0, '3@bbbb'],
|
|
['counter', 10, '3@cccc'],
|
|
]) */
|
|
AMobjItem const* result_item = AMobjItemsNext(&result, 1);
|
|
assert_int_equal(AMobjItemValue(result_item).int_, 20);
|
|
assert_int_equal(AMobjIdCounter(AMobjItemObjId(result_item)), 3);
|
|
assert_string_equal(AMactorIdStr(AMobjIdActorId(AMobjItemObjId(result_item))),
|
|
"aaaa");
|
|
result_item = AMobjItemsNext(&result, 1);
|
|
assert_int_equal(AMobjItemValue(result_item).counter, 0);
|
|
assert_int_equal(AMobjIdCounter(AMobjItemObjId(result_item)), 3);
|
|
assert_string_equal(AMactorIdStr(AMobjIdActorId(AMobjItemObjId(result_item))),
|
|
"bbbb");
|
|
result_item = AMobjItemsNext(&result, 1);
|
|
assert_int_equal(AMobjItemValue(result_item).counter, 10);
|
|
assert_int_equal(AMobjIdCounter(AMobjItemObjId(result_item)), 3);
|
|
assert_string_equal(AMactorIdStr(AMobjIdActorId(AMobjItemObjId(result_item))),
|
|
"cccc");
|
|
/* doc1.increment(seq, 0, 5) */
|
|
AMfree(AMlistIncrement(doc1, seq, 0, 5));
|
|
/* result = doc1.getAll(seq, 0) */
|
|
result = AMpush(&stack,
|
|
AMlistGetAll(doc1, seq, 0, NULL),
|
|
AM_VALUE_OBJ_ITEMS,
|
|
cmocka_cb).obj_items;
|
|
/* assert.deepEqual(result, [
|
|
['counter', 5, '3@bbbb'],
|
|
['counter', 15, '3@cccc'],
|
|
]) */
|
|
result_item = AMobjItemsNext(&result, 1);
|
|
assert_int_equal(AMobjItemValue(result_item).counter, 5);
|
|
assert_int_equal(AMobjIdCounter(AMobjItemObjId(result_item)), 3);
|
|
assert_string_equal(AMactorIdStr(AMobjIdActorId(AMobjItemObjId(result_item))),
|
|
"bbbb");
|
|
result_item = AMobjItemsNext(&result, 1);
|
|
assert_int_equal(AMobjItemValue(result_item).counter, 15);
|
|
assert_int_equal(AMobjIdCounter(AMobjItemObjId(result_item)), 3);
|
|
assert_string_equal(AMactorIdStr(AMobjIdActorId(AMobjItemObjId(result_item))),
|
|
"cccc");
|
|
/* */
|
|
/* const save = doc1.save() */
|
|
AMbyteSpan const save = AMpush(&stack,
|
|
AMsave(doc1),
|
|
AM_VALUE_BYTES,
|
|
cmocka_cb).bytes;
|
|
/* const doc4 = load(save) */
|
|
AMdoc* const doc4 = AMpush(&stack,
|
|
AMload(save.src, save.count),
|
|
AM_VALUE_DOC,
|
|
cmocka_cb).doc;
|
|
/* assert.deepEqual(doc4.save(), save); */
|
|
assert_memory_equal(AMpush(&stack,
|
|
AMsave(doc4),
|
|
AM_VALUE_BYTES,
|
|
cmocka_cb).bytes.src,
|
|
save.src,
|
|
save.count);
|
|
}
|
|
|
|
/**
|
|
* \brief paths can be used instead of objids
|
|
*/
|
|
static void test_paths_can_be_used_instead_of_objids(void** state);
|
|
|
|
/**
|
|
* \brief should be able to fetch changes by hash
|
|
*/
|
|
static void test_should_be_able_to_fetch_changes_by_hash(void** state) {
|
|
AMresultStack* stack = *state;
|
|
/* const doc1 = create("aaaa") */
|
|
AMdoc* const doc1 = AMpush(&stack,
|
|
AMcreate(AMpush(&stack,
|
|
AMactorIdInitStr("aaaa"),
|
|
AM_VALUE_ACTOR_ID,
|
|
cmocka_cb).actor_id),
|
|
AM_VALUE_DOC,
|
|
cmocka_cb).doc;
|
|
/* const doc2 = create("bbbb") */
|
|
AMdoc* const doc2 = AMpush(&stack,
|
|
AMcreate(AMpush(&stack,
|
|
AMactorIdInitStr("bbbb"),
|
|
AM_VALUE_ACTOR_ID,
|
|
cmocka_cb).actor_id),
|
|
AM_VALUE_DOC,
|
|
cmocka_cb).doc;
|
|
/* doc1.put("/", "a", "b") */
|
|
AMfree(AMmapPutStr(doc1, AM_ROOT, "a", "b"));
|
|
/* doc2.put("/", "b", "c") */
|
|
AMfree(AMmapPutStr(doc2, AM_ROOT, "b", "c"));
|
|
/* const head1 = doc1.getHeads() */
|
|
AMchangeHashes head1 = AMpush(&stack,
|
|
AMgetHeads(doc1),
|
|
AM_VALUE_CHANGE_HASHES,
|
|
cmocka_cb).change_hashes;
|
|
/* const head2 = doc2.getHeads() */
|
|
AMchangeHashes head2 = AMpush(&stack,
|
|
AMgetHeads(doc2),
|
|
AM_VALUE_CHANGE_HASHES,
|
|
cmocka_cb).change_hashes;
|
|
/* const change1 = doc1.getChangeByHash(head1[0])
|
|
if (change1 === null) { throw new RangeError("change1 should not be null") }*/
|
|
AMbyteSpan const change_hash1 = AMchangeHashesNext(&head1, 1);
|
|
AMchanges change1 = AMpush(
|
|
&stack,
|
|
AMgetChangeByHash(doc1, change_hash1.src, change_hash1.count),
|
|
AM_VALUE_CHANGES,
|
|
cmocka_cb).changes;
|
|
/* const change2 = doc1.getChangeByHash(head2[0])
|
|
assert.deepEqual(change2, null) */
|
|
AMbyteSpan const change_hash2 = AMchangeHashesNext(&head2, 1);
|
|
AMpush(&stack,
|
|
AMgetChangeByHash(doc1, change_hash2.src, change_hash2.count),
|
|
AM_VALUE_VOID,
|
|
cmocka_cb);
|
|
/* assert.deepEqual(decodeChange(change1).hash, head1[0]) */
|
|
assert_memory_equal(AMchangeHash(AMchangesNext(&change1, 1)).src,
|
|
change_hash1.src,
|
|
change_hash1.count);
|
|
}
|
|
|
|
/**
|
|
* \brief recursive sets are possible
|
|
*/
|
|
static void test_recursive_sets_are_possible(void** state) {
|
|
AMresultStack* stack = *state;
|
|
/* const doc = create("aaaa") */
|
|
AMdoc* const doc = AMpush(&stack,
|
|
AMcreate(AMpush(&stack,
|
|
AMactorIdInitStr("aaaa"),
|
|
AM_VALUE_ACTOR_ID,
|
|
cmocka_cb).actor_id),
|
|
AM_VALUE_DOC,
|
|
cmocka_cb).doc;
|
|
/* const l1 = doc.putObject("_root", "list", [{ foo: "bar" }, [1, 2, 3]])*/
|
|
AMobjId const* const l1 = AMpush(
|
|
&stack,
|
|
AMmapPutObject(doc, AM_ROOT, "list", AM_OBJ_TYPE_LIST),
|
|
AM_VALUE_OBJ_ID,
|
|
cmocka_cb).obj_id;
|
|
{
|
|
AMobjId const* const map = AMpush(
|
|
&stack,
|
|
AMlistPutObject(doc, l1, 0, true, AM_OBJ_TYPE_MAP),
|
|
AM_VALUE_OBJ_ID,
|
|
cmocka_cb).obj_id;
|
|
AMfree(AMmapPutStr(doc, map, "foo", "bar"));
|
|
AMobjId const* const list = AMpush(
|
|
&stack,
|
|
AMlistPutObject(doc, l1, SIZE_MAX, true, AM_OBJ_TYPE_LIST),
|
|
AM_VALUE_OBJ_ID,
|
|
cmocka_cb).obj_id;
|
|
for (int value = 1; value != 4; ++value) {
|
|
AMfree(AMlistPutInt(doc, list, SIZE_MAX, true, value));
|
|
}
|
|
}
|
|
/* const l2 = doc.insertObject(l1, 0, { zip: ["a", "b"] }) */
|
|
AMobjId const* const l2 = AMpush(
|
|
&stack,
|
|
AMlistPutObject(doc, l1, 0, true, AM_OBJ_TYPE_MAP),
|
|
AM_VALUE_OBJ_ID,
|
|
cmocka_cb).obj_id;
|
|
{
|
|
AMobjId const* const list = AMpush(
|
|
&stack,
|
|
AMmapPutObject(doc, l2, "zip", AM_OBJ_TYPE_LIST),
|
|
AM_VALUE_OBJ_ID,
|
|
cmocka_cb).obj_id;
|
|
AMfree(AMlistPutStr(doc, list, SIZE_MAX, true, "a"));
|
|
AMfree(AMlistPutStr(doc, list, SIZE_MAX, true, "b"));
|
|
}
|
|
/* const l3 = doc.putObject("_root", "info1", "hello world") // 'text' object*/
|
|
AMobjId const* const l3 = AMpush(
|
|
&stack,
|
|
AMmapPutObject(doc, AM_ROOT, "info1", AM_OBJ_TYPE_TEXT),
|
|
AM_VALUE_OBJ_ID,
|
|
cmocka_cb).obj_id;
|
|
AMfree(AMspliceText(doc, l3, 0, 0, "hello world"));
|
|
/* doc.put("_root", "info2", "hello world") // 'str' */
|
|
AMfree(AMmapPutStr(doc, AM_ROOT, "info2", "hello world"));
|
|
/* const l4 = doc.putObject("_root", "info3", "hello world") */
|
|
AMobjId const* const l4 = AMpush(
|
|
&stack,
|
|
AMmapPutObject(doc, AM_ROOT, "info3", AM_OBJ_TYPE_TEXT),
|
|
AM_VALUE_OBJ_ID,
|
|
cmocka_cb).obj_id;
|
|
AMfree(AMspliceText(doc, l4, 0, 0, "hello world"));
|
|
/* assert.deepEqual(doc.materialize(), {
|
|
"list": [{ zip: ["a", "b"] }, { foo: "bar" }, [1, 2, 3]],
|
|
"info1": "hello world",
|
|
"info2": "hello world",
|
|
"info3": "hello world",
|
|
}) */
|
|
AMmapItems doc_items = AMpush(&stack,
|
|
AMmapRange(doc, AM_ROOT, NULL, NULL, NULL),
|
|
AM_VALUE_MAP_ITEMS,
|
|
cmocka_cb).map_items;
|
|
AMmapItem const* doc_item = AMmapItemsNext(&doc_items, 1);
|
|
assert_string_equal(AMmapItemKey(doc_item), "info1");
|
|
assert_string_equal(AMpush(&stack,
|
|
AMtext(doc, AMmapItemObjId(doc_item), NULL),
|
|
AM_VALUE_STR,
|
|
cmocka_cb).str, "hello world");
|
|
doc_item = AMmapItemsNext(&doc_items, 1);
|
|
assert_string_equal(AMmapItemKey(doc_item), "info2");
|
|
assert_string_equal(AMmapItemValue(doc_item).str, "hello world");
|
|
doc_item = AMmapItemsNext(&doc_items, 1);
|
|
assert_string_equal(AMmapItemKey(doc_item), "info3");
|
|
assert_string_equal(AMpush(&stack,
|
|
AMtext(doc, AMmapItemObjId(doc_item), NULL),
|
|
AM_VALUE_STR,
|
|
cmocka_cb).str, "hello world");
|
|
doc_item = AMmapItemsNext(&doc_items, 1);
|
|
assert_string_equal(AMmapItemKey(doc_item), "list");
|
|
{
|
|
AMlistItems list_items = AMpush(
|
|
&stack,
|
|
AMlistRange(doc, AMmapItemObjId(doc_item), 0, SIZE_MAX, NULL),
|
|
AM_VALUE_LIST_ITEMS,
|
|
cmocka_cb).list_items;
|
|
AMlistItem const* list_item = AMlistItemsNext(&list_items, 1);
|
|
{
|
|
AMmapItems map_items = AMpush(
|
|
&stack,
|
|
AMmapRange(doc, AMlistItemObjId(list_item), NULL, NULL, NULL),
|
|
AM_VALUE_MAP_ITEMS,
|
|
cmocka_cb).map_items;
|
|
AMmapItem const* map_item = AMmapItemsNext(&map_items, 1);
|
|
assert_string_equal(AMmapItemKey(map_item), "zip");
|
|
{
|
|
AMlistItems list_items = AMpush(
|
|
&stack,
|
|
AMlistRange(doc, AMmapItemObjId(map_item), 0, SIZE_MAX, NULL),
|
|
AM_VALUE_LIST_ITEMS,
|
|
cmocka_cb).list_items;
|
|
assert_string_equal(AMlistItemValue(
|
|
AMlistItemsNext(&list_items, 1)).str,
|
|
"a");
|
|
assert_string_equal(AMlistItemValue(
|
|
AMlistItemsNext(&list_items, 1)).str,
|
|
"b");
|
|
}
|
|
}
|
|
list_item = AMlistItemsNext(&list_items, 1);
|
|
{
|
|
AMmapItems map_items = AMpush(
|
|
&stack,
|
|
AMmapRange(doc, AMlistItemObjId(list_item), NULL, NULL, NULL),
|
|
AM_VALUE_MAP_ITEMS,
|
|
cmocka_cb).map_items;
|
|
AMmapItem const* map_item = AMmapItemsNext(&map_items, 1);
|
|
assert_string_equal(AMmapItemKey(map_item), "foo");
|
|
assert_string_equal(AMmapItemValue(map_item).str, "bar");
|
|
}
|
|
list_item = AMlistItemsNext(&list_items, 1);
|
|
{
|
|
AMlistItems list_items = AMpush(
|
|
&stack,
|
|
AMlistRange(doc, AMlistItemObjId(list_item), 0, SIZE_MAX, NULL),
|
|
AM_VALUE_LIST_ITEMS,
|
|
cmocka_cb).list_items;
|
|
assert_int_equal(AMlistItemValue(
|
|
AMlistItemsNext(&list_items, 1)).int_,
|
|
1);
|
|
assert_int_equal(AMlistItemValue(
|
|
AMlistItemsNext(&list_items, 1)).int_,
|
|
2);
|
|
assert_int_equal(AMlistItemValue(
|
|
AMlistItemsNext(&list_items, 1)).int_,
|
|
3);
|
|
}
|
|
}
|
|
/* assert.deepEqual(doc.materialize(l2), { zip: ["a", "b"] }) */
|
|
AMmapItems map_items = AMpush(
|
|
&stack,
|
|
AMmapRange(doc, l2, NULL, NULL, NULL),
|
|
AM_VALUE_MAP_ITEMS,
|
|
cmocka_cb).map_items;
|
|
AMmapItem const* map_item = AMmapItemsNext(&map_items, 1);
|
|
assert_string_equal(AMmapItemKey(map_item), "zip");
|
|
{
|
|
AMlistItems list_items = AMpush(
|
|
&stack,
|
|
AMlistRange(doc, AMmapItemObjId(map_item), 0, SIZE_MAX, NULL),
|
|
AM_VALUE_LIST_ITEMS,
|
|
cmocka_cb).list_items;
|
|
assert_string_equal(AMlistItemValue(
|
|
AMlistItemsNext(&list_items, 1)).str,
|
|
"a");
|
|
assert_string_equal(AMlistItemValue(
|
|
AMlistItemsNext(&list_items, 1)).str,
|
|
"b");
|
|
}
|
|
/* assert.deepEqual(doc.materialize(l1), [{ zip: ["a", "b"] }, { foo: "bar" }, [1, 2, 3]])*/
|
|
AMlistItems list_items = AMpush(
|
|
&stack,
|
|
AMlistRange(doc, l1, 0, SIZE_MAX, NULL),
|
|
AM_VALUE_LIST_ITEMS,
|
|
cmocka_cb).list_items;
|
|
AMlistItem const* list_item = AMlistItemsNext(&list_items, 1);
|
|
{
|
|
AMmapItems map_items = AMpush(
|
|
&stack,
|
|
AMmapRange(doc, AMlistItemObjId(list_item), NULL, NULL, NULL),
|
|
AM_VALUE_MAP_ITEMS,
|
|
cmocka_cb).map_items;
|
|
AMmapItem const* map_item = AMmapItemsNext(&map_items, 1);
|
|
assert_string_equal(AMmapItemKey(map_item), "zip");
|
|
{
|
|
AMlistItems list_items = AMpush(
|
|
&stack,
|
|
AMlistRange(doc, AMmapItemObjId(map_item), 0, SIZE_MAX, NULL),
|
|
AM_VALUE_LIST_ITEMS,
|
|
cmocka_cb).list_items;
|
|
assert_string_equal(
|
|
AMlistItemValue(AMlistItemsNext(&list_items, 1)).str, "a");
|
|
assert_string_equal(AMlistItemValue(
|
|
AMlistItemsNext(&list_items, 1)).str, "b");
|
|
}
|
|
}
|
|
list_item = AMlistItemsNext(&list_items, 1);
|
|
{
|
|
AMmapItems map_items = AMpush(
|
|
&stack,
|
|
AMmapRange(doc, AMlistItemObjId(list_item), NULL, NULL, NULL),
|
|
AM_VALUE_MAP_ITEMS,
|
|
cmocka_cb).map_items;
|
|
AMmapItem const* map_item = AMmapItemsNext(&map_items, 1);
|
|
assert_string_equal(AMmapItemKey(map_item), "foo");
|
|
assert_string_equal(AMmapItemValue(map_item).str, "bar");
|
|
}
|
|
list_item = AMlistItemsNext(&list_items, 1);
|
|
{
|
|
AMlistItems list_items = AMpush(
|
|
&stack,
|
|
AMlistRange(doc, AMlistItemObjId(list_item), 0, SIZE_MAX, NULL),
|
|
AM_VALUE_LIST_ITEMS,
|
|
cmocka_cb).list_items;
|
|
assert_int_equal(AMlistItemValue(AMlistItemsNext(&list_items, 1)).int_,
|
|
1);
|
|
assert_int_equal(AMlistItemValue(AMlistItemsNext(&list_items, 1)).int_,
|
|
2);
|
|
assert_int_equal(AMlistItemValue(AMlistItemsNext(&list_items, 1)).int_,
|
|
3);
|
|
}
|
|
/* assert.deepEqual(doc.materialize(l4), "hello world") */
|
|
assert_string_equal(AMpush(&stack,
|
|
AMtext(doc, l4, NULL),
|
|
AM_VALUE_STR,
|
|
cmocka_cb).str, "hello world");
|
|
}
|
|
|
|
/**
|
|
* \brief only returns an object id when objects are created
|
|
*/
|
|
static void test_only_returns_an_object_id_when_objects_are_created(void** state) {
|
|
AMresultStack* stack = *state;
|
|
/* const doc = create("aaaa") */
|
|
AMdoc* const doc = AMpush(&stack,
|
|
AMcreate(AMpush(&stack,
|
|
AMactorIdInitStr("aaaa"),
|
|
AM_VALUE_ACTOR_ID,
|
|
cmocka_cb).actor_id),
|
|
AM_VALUE_DOC,
|
|
cmocka_cb).doc;
|
|
/* const r1 = doc.put("_root", "foo", "bar")
|
|
assert.deepEqual(r1, null); */
|
|
AMpush(&stack,
|
|
AMmapPutStr(doc, AM_ROOT, "foo", "bar"),
|
|
AM_VALUE_VOID,
|
|
cmocka_cb);
|
|
/* const r2 = doc.putObject("_root", "list", []) */
|
|
AMobjId const* const r2 = AMpush(
|
|
&stack,
|
|
AMmapPutObject(doc, AM_ROOT, "list", AM_OBJ_TYPE_LIST),
|
|
AM_VALUE_OBJ_ID,
|
|
cmocka_cb).obj_id;
|
|
/* const r3 = doc.put("_root", "counter", 10, "counter")
|
|
assert.deepEqual(r3, null); */
|
|
AMpush(&stack,
|
|
AMmapPutCounter(doc, AM_ROOT, "counter", 10),
|
|
AM_VALUE_VOID,
|
|
cmocka_cb);
|
|
/* const r4 = doc.increment("_root", "counter", 1)
|
|
assert.deepEqual(r4, null); */
|
|
AMpush(&stack,
|
|
AMmapIncrement(doc, AM_ROOT, "counter", 1),
|
|
AM_VALUE_VOID,
|
|
cmocka_cb);
|
|
/* const r5 = doc.delete("_root", "counter")
|
|
assert.deepEqual(r5, null); */
|
|
AMpush(&stack,
|
|
AMmapDelete(doc, AM_ROOT, "counter"),
|
|
AM_VALUE_VOID,
|
|
cmocka_cb);
|
|
/* const r6 = doc.insert(r2, 0, 10);
|
|
assert.deepEqual(r6, null); */
|
|
AMpush(&stack,
|
|
AMlistPutInt(doc, r2, 0, true, 10),
|
|
AM_VALUE_VOID,
|
|
cmocka_cb);
|
|
/* const r7 = doc.insertObject(r2, 0, {}); */
|
|
AMobjId const* const r7 = AMpush(
|
|
&stack,
|
|
AMlistPutObject(doc, r2, 0, true, AM_OBJ_TYPE_LIST),
|
|
AM_VALUE_OBJ_ID,
|
|
cmocka_cb).obj_id;
|
|
/* const r8 = doc.splice(r2, 1, 0, ["a", "b", "c"]); */
|
|
AMvalue const STRS[] = {{.str_tag = AM_VALUE_STR, .str = "a",
|
|
.str_tag = AM_VALUE_STR, .str = "b",
|
|
.str_tag = AM_VALUE_STR, .str = "c"}};
|
|
AMpush(&stack,
|
|
AMsplice(doc, r2, 1, 0, STRS, sizeof(STRS)/sizeof(AMvalue)),
|
|
AM_VALUE_VOID,
|
|
cmocka_cb);
|
|
/* assert.deepEqual(r2, "2@aaaa"); */
|
|
assert_int_equal(AMobjIdCounter(r2), 2);
|
|
assert_string_equal(AMactorIdStr(AMobjIdActorId(r2)), "aaaa");
|
|
/* assert.deepEqual(r7, "7@aaaa"); */
|
|
assert_int_equal(AMobjIdCounter(r7), 7);
|
|
assert_string_equal(AMactorIdStr(AMobjIdActorId(r7)), "aaaa");
|
|
}
|
|
|
|
/**
|
|
* \brief objects without properties are preserved
|
|
*/
|
|
static void test_objects_without_properties_are_preserved(void** state) {
|
|
AMresultStack* stack = *state;
|
|
/* const doc1 = create("aaaa") */
|
|
AMdoc* const doc1 = AMpush(&stack,
|
|
AMcreate(AMpush(&stack,
|
|
AMactorIdInitStr("aaaa"),
|
|
AM_VALUE_ACTOR_ID,
|
|
cmocka_cb).actor_id),
|
|
AM_VALUE_DOC,
|
|
cmocka_cb).doc;
|
|
/* const a = doc1.putObject("_root", "a", {}); */
|
|
AMobjId const* const a = AMpush(
|
|
&stack,
|
|
AMmapPutObject(doc1, AM_ROOT, "a", AM_OBJ_TYPE_MAP),
|
|
AM_VALUE_OBJ_ID,
|
|
cmocka_cb).obj_id;
|
|
/* const b = doc1.putObject("_root", "b", {}); */
|
|
AMobjId const* const b = AMpush(
|
|
&stack,
|
|
AMmapPutObject(doc1, AM_ROOT, "b", AM_OBJ_TYPE_MAP),
|
|
AM_VALUE_OBJ_ID,
|
|
cmocka_cb).obj_id;
|
|
/* const c = doc1.putObject("_root", "c", {}); */
|
|
AMobjId const* const c = AMpush(
|
|
&stack,
|
|
AMmapPutObject(doc1, AM_ROOT, "c", AM_OBJ_TYPE_MAP),
|
|
AM_VALUE_OBJ_ID,
|
|
cmocka_cb).obj_id;
|
|
/* const d = doc1.put(c, "d", "dd"); */
|
|
AMfree(AMmapPutStr(doc1, c, "d", "dd"));
|
|
/* const saved = doc1.save(); */
|
|
AMbyteSpan const saved = AMpush(&stack,
|
|
AMsave(doc1),
|
|
AM_VALUE_BYTES,
|
|
cmocka_cb).bytes;
|
|
/* const doc2 = load(saved); */
|
|
AMdoc* const doc2 = AMpush(&stack,
|
|
AMload(saved.src, saved.count),
|
|
AM_VALUE_DOC,
|
|
cmocka_cb).doc;
|
|
/* assert.deepEqual(doc2.getWithType("_root", "a"), ["map", a]) */
|
|
AMmapItems doc_items = AMpush(&stack,
|
|
AMmapRange(doc2, AM_ROOT, NULL, NULL, NULL),
|
|
AM_VALUE_MAP_ITEMS,
|
|
cmocka_cb).map_items;
|
|
assert_true(AMobjIdEqual(AMmapItemObjId(AMmapItemsNext(&doc_items, 1)), a));
|
|
/* assert.deepEqual(doc2.keys(a), []) */
|
|
AMstrs keys = AMpush(&stack,
|
|
AMkeys(doc1, a, NULL),
|
|
AM_VALUE_STRS,
|
|
cmocka_cb).strs;
|
|
assert_int_equal(AMstrsSize(&keys), 0);
|
|
/* assert.deepEqual(doc2.getWithType("_root", "b"), ["map", b]) */
|
|
assert_true(AMobjIdEqual(AMmapItemObjId(AMmapItemsNext(&doc_items, 1)), b));
|
|
/* assert.deepEqual(doc2.keys(b), []) */
|
|
keys = AMpush(&stack, AMkeys(doc1, b, NULL), AM_VALUE_STRS, cmocka_cb).strs;
|
|
assert_int_equal(AMstrsSize(&keys), 0);
|
|
/* assert.deepEqual(doc2.getWithType("_root", "c"), ["map", c]) */
|
|
assert_true(AMobjIdEqual(AMmapItemObjId(AMmapItemsNext(&doc_items, 1)), c));
|
|
/* assert.deepEqual(doc2.keys(c), ["d"]) */
|
|
keys = AMpush(&stack, AMkeys(doc1, c, NULL), AM_VALUE_STRS, cmocka_cb).strs;
|
|
assert_string_equal(AMstrsNext(&keys, 1), "d");
|
|
/* assert.deepEqual(doc2.getWithType(c, "d"), ["str", "dd"]) */
|
|
AMobjItems obj_items = AMpush(&stack,
|
|
AMobjValues(doc1, c, NULL),
|
|
AM_VALUE_OBJ_ITEMS,
|
|
cmocka_cb).obj_items;
|
|
assert_string_equal(AMobjItemValue(AMobjItemsNext(&obj_items, 1)).str, "dd");
|
|
}
|
|
|
|
/**
|
|
* \brief should allow you to forkAt a heads
|
|
*/
|
|
static void test_should_allow_you_to_forkAt_a_heads(void** state) {
|
|
AMresultStack* stack = *state;
|
|
/* const A = create("aaaaaa") */
|
|
AMdoc* const A = AMpush(&stack,
|
|
AMcreate(AMpush(&stack,
|
|
AMactorIdInitStr("aaaaaa"),
|
|
AM_VALUE_ACTOR_ID,
|
|
cmocka_cb).actor_id),
|
|
AM_VALUE_DOC,
|
|
cmocka_cb).doc;
|
|
/* A.put("/", "key1", "val1"); */
|
|
AMfree(AMmapPutStr(A, AM_ROOT, "key1", "val1"));
|
|
/* A.put("/", "key2", "val2"); */
|
|
AMfree(AMmapPutStr(A, AM_ROOT, "key2", "val2"));
|
|
/* const heads1 = A.getHeads(); */
|
|
AMchangeHashes const heads1 = AMpush(&stack,
|
|
AMgetHeads(A),
|
|
AM_VALUE_CHANGE_HASHES,
|
|
cmocka_cb).change_hashes;
|
|
/* const B = A.fork("bbbbbb") */
|
|
AMdoc* const B = AMpush(&stack, AMfork(A, NULL), AM_VALUE_DOC, cmocka_cb).doc;
|
|
AMfree(AMsetActorId(B, AMpush(&stack,
|
|
AMactorIdInitStr("bbbbbb"),
|
|
AM_VALUE_ACTOR_ID,
|
|
cmocka_cb).actor_id));
|
|
/* A.put("/", "key3", "val3"); */
|
|
AMfree(AMmapPutStr(A, AM_ROOT, "key3", "val3"));
|
|
/* B.put("/", "key4", "val4"); */
|
|
AMfree(AMmapPutStr(B, AM_ROOT, "key4", "val4"));
|
|
/* A.merge(B) */
|
|
AMfree(AMmerge(A, B));
|
|
/* const heads2 = A.getHeads(); */
|
|
AMchangeHashes const heads2 = AMpush(&stack,
|
|
AMgetHeads(A),
|
|
AM_VALUE_CHANGE_HASHES,
|
|
cmocka_cb).change_hashes;
|
|
/* A.put("/", "key5", "val5"); */
|
|
AMfree(AMmapPutStr(A, AM_ROOT, "key5", "val5"));
|
|
/* assert.deepEqual(A.forkAt(heads1).materialize("/"), A.materialize("/", heads1))*/
|
|
AMmapItems AforkAt1_items = AMpush(
|
|
&stack,
|
|
AMmapRange(
|
|
AMpush(&stack, AMfork(A, &heads1), AM_VALUE_DOC, cmocka_cb).doc,
|
|
AM_ROOT, NULL, NULL, NULL),
|
|
AM_VALUE_MAP_ITEMS,
|
|
cmocka_cb).map_items;
|
|
AMmapItems A1_items = AMpush(&stack,
|
|
AMmapRange(A, AM_ROOT, NULL, NULL, &heads1),
|
|
AM_VALUE_MAP_ITEMS,
|
|
cmocka_cb).map_items;
|
|
assert_true(AMmapItemsEqual(&AforkAt1_items, &A1_items));
|
|
/* assert.deepEqual(A.forkAt(heads2).materialize("/"), A.materialize("/", heads2))*/
|
|
AMmapItems AforkAt2_items = AMpush(
|
|
&stack,
|
|
AMmapRange(
|
|
AMpush(&stack, AMfork(A, &heads2), AM_VALUE_DOC, cmocka_cb).doc,
|
|
AM_ROOT, NULL, NULL, NULL),
|
|
AM_VALUE_MAP_ITEMS,
|
|
cmocka_cb).map_items;
|
|
AMmapItems A2_items = AMpush(&stack,
|
|
AMmapRange(A, AM_ROOT, NULL, NULL, &heads2),
|
|
AM_VALUE_MAP_ITEMS,
|
|
cmocka_cb).map_items;
|
|
assert_true(AMmapItemsEqual(&AforkAt2_items, &A2_items));
|
|
}
|
|
|
|
/**
|
|
* \brief should handle merging text conflicts then saving & loading
|
|
*/
|
|
static void test_should_handle_merging_text_conflicts_then_saving_and_loading(void** state) {
|
|
AMresultStack* stack = *state;
|
|
/* const A = create("aabbcc") */
|
|
AMdoc* const A = AMpush(&stack,
|
|
AMcreate(AMpush(&stack,
|
|
AMactorIdInitStr("aabbcc"),
|
|
AM_VALUE_ACTOR_ID,
|
|
cmocka_cb).actor_id),
|
|
AM_VALUE_DOC,
|
|
cmocka_cb).doc;
|
|
/* const At = A.putObject('_root', 'text', "") */
|
|
AMobjId const* const At = AMpush(
|
|
&stack,
|
|
AMmapPutObject(A, AM_ROOT, "text", AM_OBJ_TYPE_TEXT),
|
|
AM_VALUE_OBJ_ID,
|
|
cmocka_cb).obj_id;
|
|
/* A.splice(At, 0, 0, 'hello') */
|
|
AMfree(AMspliceText(A, At, 0, 0, "hello"));
|
|
/* */
|
|
/* const B = A.fork() */
|
|
AMdoc* const B = AMpush(&stack, AMfork(A, NULL), AM_VALUE_DOC, cmocka_cb).doc;
|
|
/* */
|
|
/* assert.deepEqual(B.getWithType("_root", "text"), ["text", At]) */
|
|
assert_string_equal(AMpush(&stack,
|
|
AMtext(B,
|
|
AMpush(&stack,
|
|
AMmapGet(B, AM_ROOT, "text", NULL),
|
|
AM_VALUE_OBJ_ID,
|
|
cmocka_cb).obj_id,
|
|
NULL),
|
|
AM_VALUE_STR,
|
|
cmocka_cb).str,
|
|
AMpush(&stack,
|
|
AMtext(A, At, NULL),
|
|
AM_VALUE_STR,
|
|
cmocka_cb).str);
|
|
/* */
|
|
/* B.splice(At, 4, 1) */
|
|
AMfree(AMspliceText(B, At, 4, 1, NULL));
|
|
/* B.splice(At, 4, 0, '!') */
|
|
AMfree(AMspliceText(B, At, 4, 0, "!"));
|
|
/* B.splice(At, 5, 0, ' ') */
|
|
AMfree(AMspliceText(B, At, 5, 0, " "));
|
|
/* B.splice(At, 6, 0, 'world') */
|
|
AMfree(AMspliceText(B, At, 6, 0, "world"));
|
|
/* */
|
|
/* A.merge(B) */
|
|
AMfree(AMmerge(A, B));
|
|
/* */
|
|
/* const binary = A.save() */
|
|
AMbyteSpan const binary = AMpush(&stack,
|
|
AMsave(A),
|
|
AM_VALUE_BYTES,
|
|
cmocka_cb).bytes;
|
|
/* */
|
|
/* const C = load(binary) */
|
|
AMdoc* const C = AMpush(&stack,
|
|
AMload(binary.src, binary.count),
|
|
AM_VALUE_DOC,
|
|
cmocka_cb).doc;
|
|
/* */
|
|
/* assert.deepEqual(C.getWithType('_root', 'text'), ['text', '1@aabbcc'])*/
|
|
AMobjId const* const C_text = AMpush(&stack,
|
|
AMmapGet(C, AM_ROOT, "text", NULL),
|
|
AM_VALUE_OBJ_ID,
|
|
cmocka_cb).obj_id;
|
|
assert_int_equal(AMobjIdCounter(C_text), 1);
|
|
assert_string_equal(AMactorIdStr(AMobjIdActorId(C_text)), "aabbcc");
|
|
/* assert.deepEqual(C.text(At), 'hell! world') */
|
|
assert_string_equal(AMpush(&stack,
|
|
AMtext(C, At, NULL),
|
|
AM_VALUE_STR,
|
|
cmocka_cb).str, "hell! world");
|
|
}
|
|
|
|
int run_ported_wasm_basic_tests(void) {
|
|
const struct CMUnitTest tests[] = {
|
|
cmocka_unit_test_setup_teardown(test_create_clone_and_free, setup_stack, teardown_stack),
|
|
cmocka_unit_test_setup_teardown(test_start_and_commit, setup_stack, teardown_stack),
|
|
cmocka_unit_test_setup_teardown(test_getting_a_nonexistent_prop_does_not_throw_an_error, setup_stack, teardown_stack),
|
|
cmocka_unit_test_setup_teardown(test_should_be_able_to_set_and_get_a_simple_value, setup_stack, teardown_stack),
|
|
cmocka_unit_test_setup_teardown(test_should_be_able_to_use_bytes, setup_stack, teardown_stack),
|
|
cmocka_unit_test_setup_teardown(test_should_be_able_to_make_subobjects, setup_stack, teardown_stack),
|
|
cmocka_unit_test_setup_teardown(test_should_be_able_to_make_lists, setup_stack, teardown_stack),
|
|
cmocka_unit_test_setup_teardown(test_lists_have_insert_set_splice_and_push_ops, setup_stack, teardown_stack),
|
|
cmocka_unit_test_setup_teardown(test_should_be_able_to_delete_non_existent_props, setup_stack, teardown_stack),
|
|
cmocka_unit_test_setup_teardown(test_should_be_able_to_del, setup_stack, teardown_stack),
|
|
cmocka_unit_test_setup_teardown(test_should_be_able_to_use_counters, setup_stack, teardown_stack),
|
|
cmocka_unit_test_setup_teardown(test_should_be_able_to_splice_text, setup_stack, teardown_stack),
|
|
cmocka_unit_test_setup_teardown(test_should_be_able_to_insert_objects_into_text, setup_stack, teardown_stack),
|
|
cmocka_unit_test_setup_teardown(test_should_be_able_to_save_all_or_incrementally, setup_stack, teardown_stack),
|
|
cmocka_unit_test_setup_teardown(test_should_be_able_to_splice_text_2, setup_stack, teardown_stack),
|
|
cmocka_unit_test_setup_teardown(test_local_inc_increments_all_visible_counters_in_a_map, setup_stack, teardown_stack),
|
|
cmocka_unit_test_setup_teardown(test_local_inc_increments_all_visible_counters_in_a_sequence, setup_stack, teardown_stack),
|
|
cmocka_unit_test_setup_teardown(test_should_be_able_to_fetch_changes_by_hash, setup_stack, teardown_stack),
|
|
cmocka_unit_test_setup_teardown(test_recursive_sets_are_possible, setup_stack, teardown_stack),
|
|
cmocka_unit_test_setup_teardown(test_only_returns_an_object_id_when_objects_are_created, setup_stack, teardown_stack),
|
|
cmocka_unit_test_setup_teardown(test_objects_without_properties_are_preserved, setup_stack, teardown_stack),
|
|
cmocka_unit_test_setup_teardown(test_should_allow_you_to_forkAt_a_heads, setup_stack, teardown_stack),
|
|
cmocka_unit_test_setup_teardown(test_should_handle_merging_text_conflicts_then_saving_and_loading, setup_stack, teardown_stack)
|
|
};
|
|
|
|
return cmocka_run_group_tests(tests, NULL, NULL);
|
|
}
|