271 lines
9.1 KiB
C
271 lines
9.1 KiB
C
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include "automerge.h"
|
|
|
|
#define BUFSIZE 4096
|
|
#define CMP_PATCH(x, y) \
|
|
do { \
|
|
char _buff[BUFSIZE]; \
|
|
char _buff2[BUFSIZE]; \
|
|
Buffer _rbuff = automerge_create_buff(); \
|
|
int ret = automerge_get_patch(x, &_rbuff); \
|
|
int len1 = _rbuff.len; \
|
|
ASSERT_RET(x, 0); \
|
|
ret = automerge_get_patch(y, &_rbuff); \
|
|
int len2 = _rbuff.len; \
|
|
ASSERT_RET(y, 0); \
|
|
printf("*** get_patch of " #x " & " #y " -- (likely) equal? *** --> %s\n\n", len1 == len2 ? "true": "false"); \
|
|
assert(len1 == len2); \
|
|
automerge_free_buff(&_rbuff); \
|
|
} while (0)
|
|
|
|
// Probably shouldn't use implicit declaration of `ret`...
|
|
#define ASSERT_RET(db, expected) \
|
|
do { \
|
|
if (ret != expected) { \
|
|
printf("LINE: %d, expected ret to be: %d, but it was: %d. Error: %s\n", __LINE__, expected, ret, automerge_error(db)); \
|
|
assert(ret == expected); \
|
|
} \
|
|
} while(0)
|
|
|
|
#define SEND_MSG(x, y) \
|
|
do { \
|
|
ret = automerge_generate_sync_message(db ## x, &rbuff, ss ## x); \
|
|
ASSERT_RET(db ## x, 0); \
|
|
ret = automerge_receive_sync_message(db ## y, &rbuff, ss ## y, rbuff.data, rbuff.len); \
|
|
ASSERT_RET(db ## y, 0); \
|
|
} while (0)
|
|
|
|
void test_sync_basic() {
|
|
printf("begin sync test - basic\n");
|
|
int ret;
|
|
|
|
Buffer rbuff = automerge_create_buff();
|
|
Backend * dbA = automerge_init();
|
|
Backend * dbB = automerge_init();
|
|
|
|
SyncState * ssA = automerge_sync_state_init();
|
|
SyncState * ssB = automerge_sync_state_init();
|
|
|
|
ret = automerge_generate_sync_message(dbA, &rbuff, ssA);
|
|
ASSERT_RET(dbA, 0);
|
|
ret = automerge_receive_sync_message(dbB, &rbuff, ssB, rbuff.data, rbuff.len);
|
|
ASSERT_RET(dbB, 0);
|
|
|
|
ret = automerge_generate_sync_message(dbB, &rbuff, ssB);
|
|
ASSERT_RET(dbB, 0);
|
|
assert(rbuff.len == 0);
|
|
|
|
automerge_sync_state_free(ssA);
|
|
automerge_sync_state_free(ssB);
|
|
automerge_free_buff(&rbuff);
|
|
}
|
|
|
|
void test_sync_encode_decode() {
|
|
printf("begin sync test - encode/decode\n");
|
|
int ret;
|
|
|
|
char buff[BUFSIZE];
|
|
char sync_state_buff[BUFSIZE];
|
|
|
|
Buffer rbuff = automerge_create_buff();
|
|
Backend * dbA = automerge_init();
|
|
Backend * dbB = automerge_init();
|
|
SyncState * ssA = automerge_sync_state_init();
|
|
SyncState * ssB = automerge_sync_state_init();
|
|
|
|
const char * requestA1 = "{\"actor\":\"111111\",\"seq\":1,\"time\":0,\"deps\":[],\"startOp\":1,\"ops\":[{\"action\":\"set\",\"obj\":\"_root\",\"key\":\"bird\",\"value\":\"magpie\",\"pred\":[]}]}";
|
|
const char * requestB1 = "{\"actor\":\"222222\",\"seq\":1,\"time\":0,\"deps\":[],\"startOp\":1,\"ops\":[{\"action\":\"set\",\"obj\":\"_root\",\"key\":\"bird\",\"value\":\"crow\",\"pred\":[]}]}";
|
|
|
|
unsigned char * A1msgpack = NULL;
|
|
unsigned char * B1msgpack = NULL;
|
|
uintptr_t A1msgpack_len = 0;
|
|
uintptr_t B1msgpack_len = 0;
|
|
|
|
debug_json_change_to_msgpack(requestA1, &A1msgpack, &A1msgpack_len);
|
|
debug_json_change_to_msgpack(requestB1, &B1msgpack, &B1msgpack_len);
|
|
|
|
ret = automerge_apply_local_change(dbA, &rbuff, A1msgpack, A1msgpack_len);
|
|
ASSERT_RET(dbA, 0);
|
|
ret = automerge_apply_local_change(dbB, &rbuff, B1msgpack, B1msgpack_len);
|
|
ASSERT_RET(dbB, 0);
|
|
|
|
// A -> B
|
|
SEND_MSG(A, B);
|
|
|
|
// B -> A
|
|
SEND_MSG(B, A);
|
|
|
|
// A -> B
|
|
SEND_MSG(A, B);
|
|
|
|
// B -> A
|
|
SEND_MSG(B, A);
|
|
|
|
ret = automerge_generate_sync_message(dbA, &rbuff, ssA);
|
|
ASSERT_RET(dbA, 0);
|
|
|
|
// Save the sync state
|
|
ret = automerge_encode_sync_state(dbB, &rbuff, ssB);
|
|
ASSERT_RET(dbB, 0);
|
|
// Read it back
|
|
ret = automerge_decode_sync_state(dbB, rbuff.data, rbuff.len, &ssB);
|
|
ASSERT_RET(dbB, 0);
|
|
|
|
// Redo B -> A
|
|
SEND_MSG(B, A);
|
|
|
|
ret = automerge_generate_sync_message(dbA, &rbuff, ssA);
|
|
ASSERT_RET(dbA, 0);
|
|
assert(rbuff.len == 0);
|
|
}
|
|
|
|
int main() {
|
|
int ret;
|
|
|
|
// In a real application you would need to check to make sure your buffer is large enough for any given read
|
|
char buff[BUFSIZE];
|
|
char buff2[BUFSIZE];
|
|
char buff3[BUFSIZE];
|
|
|
|
printf("begin\n");
|
|
|
|
Buffer rbuff = automerge_create_buff();
|
|
Backend * dbA = automerge_init();
|
|
Backend * dbB = automerge_init();
|
|
|
|
const char * requestA1 = "{\"actor\":\"111111\",\"seq\":1,\"time\":0,\"deps\":[],\"startOp\":1,\"ops\":[{\"action\":\"set\",\"obj\":\"_root\",\"key\":\"bird\",\"value\":\"magpie\",\"pred\":[]}]}";
|
|
const char * requestA2 = "{\"actor\":\"111111\",\"seq\":2,\"time\":0,\"deps\":[],\"startOp\":2,\"ops\":[{\"action\":\"set\",\"obj\":\"_root\",\"key\":\"dog\",\"value\":\"mastiff\",\"pred\":[]}]}";
|
|
const char * requestB1 = "{\"actor\":\"222222\",\"seq\":1,\"time\":0,\"deps\":[],\"startOp\":1,\"ops\":[{\"action\":\"set\",\"obj\":\"_root\",\"key\":\"bird\",\"value\":\"crow\",\"pred\":[]}]}";
|
|
const char * requestB2 = "{\"actor\":\"222222\",\"seq\":2,\"time\":0,\"deps\":[],\"startOp\":2,\"ops\":[{\"action\":\"set\",\"obj\":\"_root\",\"key\":\"cat\",\"value\":\"tabby\",\"pred\":[]}]}";
|
|
|
|
unsigned char * A1msgpack = NULL;
|
|
unsigned char * A2msgpack = NULL;
|
|
unsigned char * B1msgpack = NULL;
|
|
unsigned char * B2msgpack = NULL;
|
|
uintptr_t A1msgpack_len = 0;
|
|
uintptr_t A2msgpack_len = 0;
|
|
uintptr_t B1msgpack_len = 0;
|
|
uintptr_t B2msgpack_len = 0;
|
|
|
|
debug_json_change_to_msgpack(requestA1, &A1msgpack, &A1msgpack_len);
|
|
debug_json_change_to_msgpack(requestA2, &A2msgpack, &A2msgpack_len);
|
|
debug_json_change_to_msgpack(requestB1, &B1msgpack, &B1msgpack_len);
|
|
debug_json_change_to_msgpack(requestB2, &B2msgpack, &B2msgpack_len);
|
|
|
|
ret = automerge_apply_local_change(dbA, &rbuff, A1msgpack, A1msgpack_len);
|
|
ASSERT_RET(dbA, 0);
|
|
printf("cap: %ld, len: %ld, ptr: %p\n",rbuff.cap, rbuff.len, rbuff.data);
|
|
debug_print_msgpack_patch("*** patchA1 ***", rbuff.data, rbuff.len);
|
|
|
|
// TODO: Port this test to msgpack
|
|
// ret = automerge_apply_local_change(dbA, &rbuff, "{}");
|
|
// ASSERT_RET(dbA, 0);
|
|
// printf("*** patchA2 expected error string ** (%s)\n\n",automerge_error(dbA));
|
|
|
|
ret = automerge_apply_local_change(dbA, &rbuff, A2msgpack, A2msgpack_len);
|
|
ASSERT_RET(dbA, 0);
|
|
debug_print_msgpack_patch("*** patchA2 ***", rbuff.data, rbuff.len);
|
|
|
|
ret = automerge_apply_local_change(dbB, &rbuff, B1msgpack, B1msgpack_len);
|
|
ASSERT_RET(dbB, 0);
|
|
debug_print_msgpack_patch("*** patchB1 ***", rbuff.data, rbuff.len);
|
|
|
|
ret = automerge_apply_local_change(dbB, &rbuff, B2msgpack, B2msgpack_len);
|
|
ASSERT_RET(dbB, 0);
|
|
debug_print_msgpack_patch("*** patchB2 ***", rbuff.data, rbuff.len);
|
|
|
|
printf("*** clone dbA -> dbC ***\n\n");
|
|
Backend * dbC = NULL;
|
|
ret = automerge_clone(dbA, &dbC);
|
|
ASSERT_RET(dbA, 0);
|
|
|
|
CMP_PATCH(dbA, dbC);
|
|
|
|
ret = automerge_save(dbA, &rbuff);
|
|
ASSERT_RET(dbA, 0);
|
|
printf("*** save dbA - %ld bytes ***\n\n", rbuff.len);
|
|
|
|
printf("*** load the save into dbD ***\n\n");
|
|
Backend * dbD = automerge_load(rbuff.data, rbuff.len);
|
|
|
|
CMP_PATCH(dbA, dbD);
|
|
|
|
ret = automerge_get_changes_for_actor(dbA, &rbuff, "111111");
|
|
ASSERT_RET(dbA, 0);
|
|
|
|
// We are reading one return value (rbuff) while needing to return
|
|
// something else, so we need another `Buffers` struct
|
|
// Buffers rbuff2 = automerge_create_buffs();
|
|
// int start = 0;
|
|
// for(int i = 0; i < rbuff.lens_len; ++i) {
|
|
// int len = rbuff.lens[i];
|
|
// char * data_start = rbuff.data + start;
|
|
// automerge_decode_change(dbA, &rbuff2, data_start, len);
|
|
// util_read_buffs(&rbuff2, 0, buff2);
|
|
// printf("Change decoded to msgpack\n");
|
|
// start += len;
|
|
// automerge_encode_change(dbB, &rbuff2, buff2, rbuff2.lens[0]);
|
|
// assert(memcmp(data_start, rbuff2.data, len) == 0);
|
|
// }
|
|
// CBuffers cbuffs = { data: rbuff.data, data_len: rbuff.data_len, lens: rbuff.lens, lens_len: rbuff.lens_len };
|
|
// ret = automerge_apply_changes(dbB, &rbuff, cbuffs);
|
|
// ASSERT_RET(dbB, 0);
|
|
// automerge_free_buffs(&rbuff2);
|
|
|
|
ret = automerge_apply_changes(dbB, &rbuff, rbuff.data, rbuff.len);
|
|
ASSERT_RET(dbB, 0);
|
|
|
|
printf("*** get head from dbB ***\n\n");
|
|
ret = automerge_get_heads(dbB, &rbuff);
|
|
ASSERT_RET(dbB,0);
|
|
|
|
//int num_heads = 0;
|
|
//for (int i = 0; i < rbuff.lens_len; ++i) {
|
|
// assert(rbuff.lens[i] == 32);
|
|
// util_read_buffs(&rbuff, i, buff3 + (num_heads * 32));
|
|
// num_heads++;
|
|
//}
|
|
//assert(num_heads == 2);
|
|
ret = automerge_get_changes(dbB, &rbuff, rbuff.data, rbuff.len);
|
|
ASSERT_RET(dbB, 0);
|
|
|
|
printf("*** copy changes from dbB to A ***\n\n");
|
|
ret = automerge_get_changes_for_actor(dbB, &rbuff, "222222");
|
|
ASSERT_RET(dbB, 0);
|
|
|
|
ret = automerge_apply_changes(dbA, &rbuff, rbuff.data, rbuff.len);
|
|
ASSERT_RET(dbA, 0);
|
|
|
|
CMP_PATCH(dbA, dbB);
|
|
|
|
printf("*** copy changes from dbA to E using load ***\n\n");
|
|
Backend * dbE = automerge_init();
|
|
ret = automerge_get_changes(dbA, &rbuff, NULL, 0);
|
|
ASSERT_RET(dbA, 0);
|
|
ret = automerge_load_changes(dbE, rbuff.data, rbuff.len);
|
|
ASSERT_RET(dbE, 0);
|
|
|
|
CMP_PATCH(dbA, dbE);
|
|
CMP_PATCH(dbA, dbB);
|
|
|
|
//ret = automerge_get_missing_deps(dbE, &rbuff, buff3, num_heads);
|
|
//ASSERT_RET(dbE, 0);
|
|
//util_read_buffs(&rbuff, 0, buff);
|
|
//assert(strlen(buff) == 2); // [] - nothing missing
|
|
|
|
test_sync_basic();
|
|
test_sync_encode_decode();
|
|
|
|
printf("free resources\n");
|
|
automerge_free(dbA);
|
|
automerge_free(dbB);
|
|
automerge_free(dbC);
|
|
automerge_free(dbD);
|
|
automerge_free(dbE);
|
|
automerge_free_buff(&rbuff);
|
|
|
|
printf("end\n");
|
|
}
|