[Tarantool-patches] [PATCH v2 03/11] error: introduce error_payload
Vladislav Shpilevoy
v.shpilevoy at tarantool.org
Fri Nov 12 02:54:24 MSK 2021
It is a dictionary-like struct which stores keys with binary data
values. The values are supposed to be arbitrary MessagePack blobs:
number, string, bool, UUID, array, map, anything.
The payload is an array inside instead of a hash table because
number of keys will be <= 3 in all cases. And even when it will be
public, it is very unlikely it will be bigger.
Object of error_payload in a future patch will be stored in struct
error and will allow to extend it dynamically with more members.
This in turn is needed to extend ER_READONLY error with more
details about why it happened.
Part of #5568
---
src/lib/core/CMakeLists.txt | 1 +
src/lib/core/error_payload.c | 301 ++++++++++++++++++++++
src/lib/core/error_payload.h | 190 ++++++++++++++
test/unit/CMakeLists.txt | 2 +
test/unit/error.c | 466 +++++++++++++++++++++++++++++++++++
test/unit/error.result | 161 ++++++++++++
6 files changed, 1121 insertions(+)
create mode 100644 src/lib/core/error_payload.c
create mode 100644 src/lib/core/error_payload.h
create mode 100644 test/unit/error.c
create mode 100644 test/unit/error.result
diff --git a/src/lib/core/CMakeLists.txt b/src/lib/core/CMakeLists.txt
index fb640461d..17197a371 100644
--- a/src/lib/core/CMakeLists.txt
+++ b/src/lib/core/CMakeLists.txt
@@ -21,6 +21,7 @@ set(core_sources
fio.c
exception.cc
errinj.c
+ error_payload.c
reflection.c
assoc.c
util.c
diff --git a/src/lib/core/error_payload.c b/src/lib/core/error_payload.c
new file mode 100644
index 000000000..e5c2efd1a
--- /dev/null
+++ b/src/lib/core/error_payload.c
@@ -0,0 +1,301 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 2010-2021, Tarantool AUTHORS, please see AUTHORS file.
+ */
+#include "error_payload.h"
+#include "mp_uuid.h"
+#include "msgpuck.h"
+
+void
+error_payload_create(struct error_payload *p)
+{
+ p->count = 0;
+ p->fields = NULL;
+}
+
+void
+error_payload_destroy(struct error_payload *p)
+{
+ for (int i = 0; i < p->count; ++i) {
+ TRASH(p->fields[i]);
+ free(p->fields[i]);
+ }
+ free(p->fields);
+ TRASH(p);
+}
+
+const char *
+error_payload_get_str(const struct error_payload *e, const char *name)
+{
+ const struct error_field *f = error_payload_find(e, name);
+ if (f == NULL)
+ return NULL;
+ const char *data = f->data;
+ if (mp_typeof(*data) != MP_STR)
+ return NULL;
+ uint32_t len;
+ data = mp_decode_str(&data, &len);
+ assert(data[len] == 0);
+ return data;
+}
+
+void
+error_payload_set_str(struct error_payload *e, const char *name,
+ const char *value)
+{
+ uint32_t len = strlen(value);
+ char *data = error_payload_prepare(
+ e, name, mp_sizeof_str(len), 1)->data;
+ /*
+ * 1 extra byte in the end is reserved to append 0 after the encoded
+ * string. To be able to return it from get() without copying.
+ */
+ *mp_encode_str(data, value, len) = 0;
+}
+
+bool
+error_payload_get_uint(const struct error_payload *e, const char *name,
+ uint64_t *value)
+{
+ const struct error_field *f = error_payload_find(e, name);
+ if (f == NULL)
+ goto not_found;
+ const char *data = f->data;
+ if (mp_typeof(*data) != MP_UINT)
+ goto not_found;
+ *value = mp_decode_uint(&data);
+ return true;
+
+not_found:
+ *value = 0;
+ return false;
+}
+
+void
+error_payload_set_uint(struct error_payload *e, const char *name,
+ uint64_t value)
+{
+ char *data = error_payload_prepare(
+ e, name, mp_sizeof_uint(value), 0)->data;
+ mp_encode_uint(data, value);
+}
+
+bool
+error_payload_get_int(const struct error_payload *e, const char *name,
+ int64_t *value)
+{
+ const struct error_field *f = error_payload_find(e, name);
+ if (f == NULL)
+ goto not_found;
+ const char *data = f->data;
+ if (mp_typeof(*data) == MP_UINT) {
+ uint64_t u = mp_decode_uint(&data);
+ if (u > INT64_MAX)
+ goto not_found;
+ *value = u;
+ return true;
+ } else if (mp_typeof(*data) == MP_INT) {
+ *value = mp_decode_int(&data);
+ return true;
+ }
+not_found:
+ *value = 0;
+ return false;
+}
+
+void
+error_payload_set_int(struct error_payload *e, const char *name, int64_t value)
+{
+ if (value >= 0)
+ return error_payload_set_uint(e, name, value);
+ char *data = error_payload_prepare(
+ e, name, mp_sizeof_int(value), 0)->data;
+ mp_encode_int(data, value);
+}
+
+bool
+error_payload_get_double(const struct error_payload *e, const char *name,
+ double *value)
+{
+ const struct error_field *f = error_payload_find(e, name);
+ if (f == NULL)
+ goto not_found;
+ const char *data = f->data;
+ if (mp_typeof(*data) == MP_DOUBLE) {
+ *value = mp_decode_double(&data);
+ return true;
+ } else if (mp_typeof(*data) == MP_FLOAT) {
+ *value = mp_decode_float(&data);
+ return true;
+ }
+not_found:
+ *value = 0;
+ return false;
+}
+
+void
+error_payload_set_double(struct error_payload *e, const char *name,
+ double value)
+{
+ char *data = error_payload_prepare(
+ e, name, mp_sizeof_double(value), 0)->data;
+ mp_encode_double(data, value);
+}
+
+bool
+error_payload_get_bool(const struct error_payload *e, const char *name,
+ bool *value)
+{
+ const struct error_field *f = error_payload_find(e, name);
+ if (f == NULL)
+ goto not_found;
+ const char *data = f->data;
+ if (mp_typeof(*data) != MP_BOOL)
+ goto not_found;
+ *value = mp_decode_bool(&data);
+ return true;
+
+not_found:
+ *value = false;
+ return false;
+}
+
+void
+error_payload_set_bool(struct error_payload *e, const char *name, bool value)
+{
+ char *data = error_payload_prepare(
+ e, name, mp_sizeof_bool(value), 0)->data;
+ mp_encode_bool(data, value);
+}
+
+bool
+error_payload_get_uuid(const struct error_payload *e, const char *name,
+ struct tt_uuid *uuid)
+{
+ const struct error_field *f = error_payload_find(e, name);
+ if (f == NULL)
+ goto not_found;
+ const char *data = f->data;
+ if (mp_decode_uuid(&data, uuid) == NULL)
+ goto not_found;
+ return true;
+
+not_found:
+ *uuid = uuid_nil;
+ return false;
+}
+
+void
+error_payload_set_uuid(struct error_payload *e, const char *name,
+ const struct tt_uuid *uuid)
+{
+ char *data = error_payload_prepare(e, name, mp_sizeof_uuid(), 0)->data;
+ mp_encode_uuid(data, uuid);
+}
+
+const char *
+error_payload_get_mp(const struct error_payload *e, const char *name,
+ uint32_t *size)
+{
+ const struct error_field *f = error_payload_find(e, name);
+ if (f == NULL) {
+ *size = 0;
+ return NULL;
+ }
+ *size = f->size;
+ return f->data;
+}
+
+void
+error_payload_set_mp(struct error_payload *e, const char *name,
+ const char *src, uint32_t size)
+{
+ char *data;
+ if (mp_typeof(*src) == MP_STR) {
+ data = error_payload_prepare(e, name, size, 1)->data;
+ /* @sa error_payload_set_str(). */
+ data[size] = 0;
+ } else {
+ data = error_payload_prepare(e, name, size, 0)->data;
+ }
+ memcpy(data, src, size);
+}
+
+void
+error_payload_clear(struct error_payload *e, const char *name)
+{
+ struct error_field **fields = e->fields;
+ for (int i = 0; i < e->count; ++i) {
+ struct error_field *f = fields[i];
+ if (strcmp(name, f->name) != 0)
+ continue;
+ TRASH(f);
+ free(f);
+ int count = --e->count;
+ if (count == 0) {
+ free(fields);
+ e->fields = NULL;
+ return;
+ }
+ /* Cyclic deletion. Order does not matter in a dictionary. */
+ fields[i] = fields[count];
+ return;
+ }
+}
+
+void
+error_payload_move(struct error_payload *dst, struct error_payload *src)
+{
+ for (int i = 0; i < dst->count; ++i) {
+ TRASH(dst->fields[i]);
+ free(dst->fields[i]);
+ }
+ free(dst->fields);
+ dst->fields = src->fields;
+ dst->count = src->count;
+ src->fields = NULL;
+ src->count = 0;
+}
+
+const struct error_field *
+error_payload_find(const struct error_payload *e, const char *name)
+{
+ for (int i = 0; i < e->count; ++i) {
+ const struct error_field *f = e->fields[i];
+ if (strcmp(name, f->name) == 0)
+ return f;
+ }
+ return NULL;
+}
+
+struct error_field *
+error_payload_prepare(struct error_payload *e, const char *name,
+ uint32_t value_size, uint32_t extra)
+{
+ struct error_field *f;
+ uint32_t name_size = strlen(name) + 1;
+ uint32_t data_offset = offsetof(struct error_field, name[name_size]);
+ uint32_t total = data_offset + value_size + extra;
+
+ struct error_field **fields = e->fields;
+ int count = e->count;
+ int i;
+ for (i = 0; i < count; ++i) {
+ f = fields[i];
+ if (strcmp(name, f->name) != 0)
+ continue;
+ f = xrealloc(f, total);
+ goto set;
+ }
+ e->count = ++count;
+ fields = xrealloc(fields, sizeof(fields[0]) * count);
+ e->fields = fields;
+ f = xmalloc(total);
+ memcpy(f->name, name, name_size);
+set:
+ f->data = (char *)f + data_offset;
+ f->size = value_size;
+ fields[i] = f;
+ return f;
+}
diff --git a/src/lib/core/error_payload.h b/src/lib/core/error_payload.h
new file mode 100644
index 000000000..23c75064b
--- /dev/null
+++ b/src/lib/core/error_payload.h
@@ -0,0 +1,190 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 2010-2021, Tarantool AUTHORS, please see AUTHORS file.
+ */
+#pragma once
+
+#include "trivia/util.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif /* defined(__cplusplus) */
+
+struct tt_uuid;
+
+/** Single field of error payload. */
+struct error_field {
+ /** MessagePack field value. */
+ char *data;
+ /** Data size. */
+ uint32_t size;
+ /** Zero terminated field name. */
+ char name[1];
+};
+
+/**
+ * Key-value pairs to store dynamic fields of an error object. The ones which
+ * are defined not for all error types and user-defined ones.
+ */
+struct error_payload {
+ /** Number of fields. */
+ int count;
+ /**
+ * Array of field pointers. Not very optimized, but
+ * - The errors are supposed to be rare;
+ * - Number of fields is around 3 tops - linear search can be even
+ * faster than a generic hash table then;
+ * - Not storing them by values simplifies addition of new fields and
+ * their removal.
+ */
+ struct error_field **fields;
+};
+
+/** Create error payload. */
+void
+error_payload_create(struct error_payload *p);
+
+/** Destroy error payload. */
+void
+error_payload_destroy(struct error_payload *p);
+
+/**
+ * Get value of a payload field as a string. If it is not string or is not
+ * found - return NULL.
+ */
+const char *
+error_payload_get_str(const struct error_payload *e, const char *name);
+
+/**
+ * Set value of a payload field to a string. If the field existed before, it is
+ * overwritten.
+ */
+void
+error_payload_set_str(struct error_payload *e, const char *name,
+ const char *value);
+
+/**
+ * Get value of a payload field as a uint. If it is not uint or is not found -
+ * return false and the out parameter is set to 0.
+ */
+bool
+error_payload_get_uint(const struct error_payload *e, const char *name,
+ uint64_t *value);
+
+/**
+ * Set value of a payload field to a uint. If the field existed before, it is
+ * overwritten.
+ */
+void
+error_payload_set_uint(struct error_payload *e, const char *name,
+ uint64_t value);
+
+/**
+ * Get value of a payload field as an int. If it is not int, or is not found, or
+ * does not fit int64_t - return false and the out parameter is set to 0.
+ */
+bool
+error_payload_get_int(const struct error_payload *e, const char *name,
+ int64_t *value);
+
+/**
+ * Set value of a payload field to an int. If the field existed before, it is
+ * overwritten.
+ */
+void
+error_payload_set_int(struct error_payload *e, const char *name, int64_t value);
+
+/**
+ * Get value of a payload field as a double. If it is not double or is not
+ * found - return false and the out parameter is set to 0.
+ */
+bool
+error_payload_get_double(const struct error_payload *e, const char *name,
+ double *value);
+
+/**
+ * Set value of a payload field to a double. If the field existed before, it is
+ * overwritten.
+ */
+void
+error_payload_set_double(struct error_payload *e, const char *name,
+ double value);
+
+/**
+ * Get value of a payload field as a bool. If it is not bool or is not found -
+ * return false and the out parameter is set to false.
+ */
+bool
+error_payload_get_bool(const struct error_payload *e, const char *name,
+ bool *value);
+
+/**
+ * Set value of a payload field to a bool. If the field existed before, it is
+ * overwritten.
+ */
+void
+error_payload_set_bool(struct error_payload *e, const char *name, bool value);
+
+/**
+ * Get value of a payload field as a UUID. If it is not UUID or is not found -
+ * return false and the out parameter is set to UUID with zeros.
+ */
+bool
+error_payload_get_uuid(const struct error_payload *e, const char *name,
+ struct tt_uuid *uuid);
+
+/**
+ * Set value of a payload field to a UUID. If the field existed before, it is
+ * overwritten.
+ */
+void
+error_payload_set_uuid(struct error_payload *e, const char *name,
+ const struct tt_uuid *uuid);
+
+/**
+ * Get MessagePack value of a payload field. If it is not found - return NULL
+ * and the out parameter is set to 0.
+ */
+const char *
+error_payload_get_mp(const struct error_payload *e, const char *name,
+ uint32_t *size);
+
+/**
+ * Set value of a payload field to a MessagePack buffer. If the field existed
+ * before, it is overwritten.
+ */
+void
+error_payload_set_mp(struct error_payload *e, const char *name,
+ const char *src, uint32_t size);
+
+/** Remove the given field from the payload. */
+void
+error_payload_clear(struct error_payload *e, const char *name);
+
+/**
+ * Move all fields of one payload into another. Old fields of the destination
+ * are all deleted. The source stays valid but empty.
+ */
+void
+error_payload_move(struct error_payload *dst, struct error_payload *src);
+
+/** Find a payload field by name. */
+const struct error_field *
+error_payload_find(const struct error_payload *e, const char *name);
+
+/**
+ * Prepare a payload field to get a new value. If the field didn't exist, it is
+ * added. If it existed then it is reallocated if was necessary and should be
+ * overwritten. Name is already filled in the field. Only need to fill the
+ * value.
+ * Extra parameter allows to allocate extra memory for arbitrary usage after the
+ * error field and its value.
+ */
+struct error_field *
+error_payload_prepare(struct error_payload *e, const char *name,
+ uint32_t value_size, uint32_t extra);
+
+#if defined(__cplusplus)
+} /* extern "C" */
+#endif /* defined(__cplusplus) */
diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt
index 10e1f4395..19674d3fe 100644
--- a/test/unit/CMakeLists.txt
+++ b/test/unit/CMakeLists.txt
@@ -60,6 +60,8 @@ add_executable(xmalloc.test xmalloc.c core_test_utils.c)
target_link_libraries(xmalloc.test unit)
add_executable(datetime.test datetime.c)
target_link_libraries(datetime.test tzcode core cdt unit)
+add_executable(error.test error.c core_test_utils.c)
+target_link_libraries(error.test unit core)
add_executable(bps_tree.test bps_tree.cc)
target_link_libraries(bps_tree.test small misc)
diff --git a/test/unit/error.c b/test/unit/error.c
new file mode 100644
index 000000000..55f8f6798
--- /dev/null
+++ b/test/unit/error.c
@@ -0,0 +1,466 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 2010-2021, Tarantool AUTHORS, please see AUTHORS file.
+ */
+#include "error_payload.h"
+#include "mp_uuid.h"
+#include "msgpuck.h"
+#include "random.h"
+#include "unit.h"
+
+#include <float.h>
+
+static void
+test_payload_field_str(void)
+{
+ header();
+ plan(15);
+
+ struct error_payload p;
+ error_payload_create(&p);
+ is(p.count, 0, "no fields in the beginning");
+ is(error_payload_get_str(&p, "key"), NULL, "get_str() empty");
+
+ error_payload_set_str(&p, "key1", "value1");
+ is(p.count, 1, "++count");
+ is(strcmp(error_payload_get_str(&p, "key1"), "value1"), 0,
+ "get_str() finds");
+
+ error_payload_set_str(&p, "key2", "value2");
+ is(p.count, 2, "++count");
+ is(strcmp(error_payload_get_str(&p, "key1"), "value1"), 0,
+ "get_str() finds old");
+ is(strcmp(error_payload_get_str(&p, "key2"), "value2"), 0,
+ "get_str() finds new");
+ is(error_payload_find(&p, "key1")->size,
+ mp_sizeof_str(strlen("value1")),
+ "size does not include terminating zero");
+
+ error_payload_set_str(&p, "key1", "new_value1");
+ is(p.count, 2, "field count is same");
+ is(strcmp(error_payload_get_str(&p, "key1"), "new_value1"), 0,
+ "get_str() finds new value");
+ is(strcmp(error_payload_get_str(&p, "key2"), "value2"), 0,
+ "get_str() finds other key old value");
+
+ error_payload_clear(&p, "key2");
+ is(p.count, 1, "--count");
+ is(strcmp(error_payload_get_str(&p, "key1"), "new_value1"), 0,
+ "get_str() finds new value");
+ is(error_payload_get_str(&p, "key2"), NULL,
+ "get_str() can't find deleted value");
+
+ error_payload_set_uint(&p, "key2", 1);
+ is(error_payload_get_str(&p, "key2"), NULL, "wrong type");
+
+ error_payload_destroy(&p);
+
+ check_plan();
+ footer();
+}
+
+static void
+test_payload_field_uint(void)
+{
+ header();
+ plan(17);
+
+ struct error_payload p;
+ error_payload_create(&p);
+ uint64_t val = 1;
+ ok(!error_payload_get_uint(&p, "key", &val) && val == 0,
+ "get_uint() empty");
+
+ error_payload_set_uint(&p, "key1", 1);
+ is(p.count, 1, "++count");
+ ok(error_payload_get_uint(&p, "key1", &val), "get_uint() finds");
+ is(val, 1, "value match");
+
+ val = 100;
+ ok(!error_payload_get_uint(&p, "key2", &val), "get_uint() fails");
+ is(val, 0, "value is default");
+
+ is(error_payload_find(&p, "key1")->size, mp_sizeof_uint(1),
+ "small number size");
+
+ error_payload_set_uint(&p, "key2", UINT32_MAX);
+ ok(error_payload_get_uint(&p, "key2", &val), "get_uint() 32 bit");
+ is(val, UINT32_MAX, "value match");
+ is(error_payload_find(&p, "key2")->size, mp_sizeof_uint(UINT32_MAX),
+ "middle number size");
+ is(p.count, 2, "field count is same");
+
+ error_payload_set_uint(&p, "key1", UINT64_MAX);
+ ok(error_payload_get_uint(&p, "key1", &val) && val == UINT64_MAX,
+ "value 1");
+ ok(error_payload_get_uint(&p, "key2", &val) && val == UINT32_MAX,
+ "value 2");
+
+ error_payload_clear(&p, "key2");
+ is(p.count, 1, "--count");
+ ok(error_payload_get_uint(&p, "key1", &val) && val == UINT64_MAX,
+ "remained value");
+ ok(!error_payload_get_uint(&p, "key2", &val) && val == 0,
+ "deleted value");
+
+ error_payload_set_str(&p, "key2", "1");
+ ok(!error_payload_get_uint(&p, "key2", &val) && val == 0, "wrong type");
+
+ error_payload_destroy(&p);
+
+ check_plan();
+ footer();
+}
+
+static void
+test_payload_field_int(void)
+{
+ header();
+ plan(20);
+
+ struct error_payload p;
+ error_payload_create(&p);
+ int64_t val = 1;
+ ok(!error_payload_get_int(&p, "key", &val) && val == 0,
+ "get_int() empty");
+
+ error_payload_set_int(&p, "key1", 1);
+ is(p.count, 1, "++count");
+ ok(error_payload_get_int(&p, "key1", &val), "get_int() finds");
+ is(val, 1, "value match");
+
+ val = 100;
+ ok(!error_payload_get_int(&p, "key2", &val), "get_int() fails");
+ is(val, 0, "value is default");
+
+ is(error_payload_find(&p, "key1")->size, mp_sizeof_uint(1),
+ "small number size");
+
+ error_payload_set_int(&p, "key2", UINT32_MAX);
+ ok(error_payload_get_int(&p, "key2", &val), "get_int() 32 bit");
+ is(val, UINT32_MAX, "value match");
+ is(error_payload_find(&p, "key2")->size, mp_sizeof_uint(UINT32_MAX),
+ "middle number size");
+ is(p.count, 2, "field count is same");
+
+ error_payload_set_int(&p, "key1", INT64_MAX);
+ ok(error_payload_get_int(&p, "key1", &val) && val == INT64_MAX,
+ "value 1");
+ ok(error_payload_get_int(&p, "key2", &val) && val == UINT32_MAX,
+ "value 2");
+
+ error_payload_clear(&p, "key2");
+ is(p.count, 1, "--count");
+ ok(error_payload_get_int(&p, "key1", &val) && val == INT64_MAX,
+ "remained value");
+ ok(!error_payload_get_int(&p, "key2", &val) && val == 0,
+ "deleted value");
+
+ error_payload_set_str(&p, "key2", "1");
+ ok(!error_payload_get_int(&p, "key2", &val) && val == 0, "wrong type");
+
+ error_payload_set_uint(&p, "key2", (uint64_t)INT64_MAX + 1);
+ ok(!error_payload_get_int(&p, "key2", &val) && val == 0, "overflow");
+
+ error_payload_set_uint(&p, "key2", 100);
+ ok(error_payload_get_int(&p, "key2", &val) && val == 100, "conversion");
+
+ error_payload_set_int(&p, "key2", INT64_MIN);
+ ok(error_payload_get_int(&p, "key2", &val) && val == INT64_MIN,
+ "min value");
+
+ error_payload_destroy(&p);
+
+ check_plan();
+ footer();
+}
+
+static void
+test_payload_field_double(void)
+{
+ header();
+ plan(14);
+
+ struct error_payload p;
+ error_payload_create(&p);
+ double val = 1;
+ ok(!error_payload_get_double(&p, "key", &val) && val == 0,
+ "get_double() empty");
+
+ error_payload_set_double(&p, "key1", 1.5);
+ is(p.count, 1, "++count");
+ ok(error_payload_get_double(&p, "key1", &val), "get_double() finds");
+ is(val, 1.5, "value match");
+
+ val = 1;
+ ok(!error_payload_get_double(&p, "key2", &val), "get_double() fails");
+ is(val, 0, "value is default");
+
+ is(error_payload_find(&p, "key1")->size, mp_sizeof_double(1.5), "size");
+
+ error_payload_set_double(&p, "key2", DBL_MAX);
+ ok(error_payload_get_double(&p, "key1", &val) && val == 1.5, "value 1");
+ ok(error_payload_get_double(&p, "key2", &val) && val == DBL_MAX,
+ "value 2");
+
+ error_payload_clear(&p, "key2");
+ is(p.count, 1, "--count");
+ ok(error_payload_get_double(&p, "key1", &val) && val == 1.5,
+ "remained value");
+ ok(!error_payload_get_double(&p, "key2", &val) && val == 0,
+ "deleted value");
+
+ error_payload_set_str(&p, "key2", "1");
+ ok(!error_payload_get_double(&p, "key2", &val) && val == 0,
+ "wrong type");
+
+ char buffer[16];
+ char *data = mp_encode_float(buffer, 0.5);
+ error_payload_set_mp(&p, "key2", buffer, data - buffer);
+ ok(error_payload_get_double(&p, "key2", &val) && val == 0.5,
+ "float 0.5");
+
+ error_payload_destroy(&p);
+
+ check_plan();
+ footer();
+}
+
+static void
+test_payload_field_bool(void)
+{
+ header();
+ plan(13);
+
+ struct error_payload p;
+ error_payload_create(&p);
+ bool val = true;
+ ok(!error_payload_get_bool(&p, "key", &val) && !val,
+ "get_bool() empty");
+
+ error_payload_set_bool(&p, "key1", true);
+ is(p.count, 1, "++count");
+ ok(error_payload_get_bool(&p, "key1", &val), "get_bool() finds");
+ ok(val, "value match");
+
+ val = true;
+ ok(!error_payload_get_bool(&p, "key2", &val), "get_bool() fails");
+ ok(!val, "value is default");
+
+ error_payload_set_bool(&p, "key2", false);
+ ok(error_payload_get_bool(&p, "key2", &val), "get_bool() finds");
+ ok(!val, "value match");
+
+ is(error_payload_find(&p, "key1")->size, mp_sizeof_bool(true), "size");
+
+ error_payload_clear(&p, "key2");
+ is(p.count, 1, "--count");
+ ok(error_payload_get_bool(&p, "key1", &val) && val, "remained value");
+ ok(!error_payload_get_bool(&p, "key2", &val) && !val, "deleted value");
+
+ error_payload_set_str(&p, "key2", "true");
+ ok(!error_payload_get_bool(&p, "key2", &val) && !val, "wrong type");
+
+ error_payload_destroy(&p);
+
+ check_plan();
+ footer();
+}
+
+static void
+test_payload_field_uuid(void)
+{
+ header();
+ plan(17);
+
+ struct error_payload p;
+ error_payload_create(&p);
+ struct tt_uuid val1;
+ tt_uuid_create(&val1);
+ ok(!error_payload_get_uuid(&p, "key", &val1), "get_uuid() empty");
+ ok(tt_uuid_is_nil(&val1), "default value");
+
+ tt_uuid_create(&val1);
+ error_payload_set_uuid(&p, "key1", &val1);
+ is(p.count, 1, "++count");
+ struct tt_uuid val2;
+ ok(error_payload_get_uuid(&p, "key1", &val2), "get_uuid() finds");
+ ok(tt_uuid_is_equal(&val1, &val2), "value match");
+
+ ok(!error_payload_get_uuid(&p, "key2", &val2), "get_uuid() fails");
+ ok(tt_uuid_is_nil(&val2), "value is default");
+
+ tt_uuid_create(&val2);
+ error_payload_set_uuid(&p, "key2", &val2);
+ struct tt_uuid val3;
+ ok(error_payload_get_uuid(&p, "key2", &val3), "get_uuid() finds");
+ ok(tt_uuid_is_equal(&val3, &val2), "value match");
+
+ is(error_payload_find(&p, "key1")->size, mp_sizeof_uuid(), "size");
+
+ error_payload_clear(&p, "key2");
+ is(p.count, 1, "--count");
+ ok(error_payload_get_uuid(&p, "key1", &val3), "remained value");
+ ok(tt_uuid_is_equal(&val1, &val3), "value match");
+ ok(!error_payload_get_uuid(&p, "key2", &val3), "deleted value");
+ ok(tt_uuid_is_nil(&val3), "value match");
+
+ error_payload_set_str(&p, "key2", "1");
+ ok(!error_payload_get_uuid(&p, "key2", &val3), "wrong type");
+ ok(tt_uuid_is_nil(&val3), "value match");
+
+ error_payload_destroy(&p);
+
+ check_plan();
+ footer();
+}
+
+static void
+test_payload_field_mp(void)
+{
+ header();
+ plan(6);
+ char buf[1024];
+ char *data;
+ const char *cdata;
+ uint32_t size;
+
+ struct error_payload p;
+ error_payload_create(&p);
+
+ data = mp_encode_str(buf, "value1", 6);
+ error_payload_set_mp(&p, "key1", buf, data - buf);
+ is(strcmp(error_payload_get_str(&p, "key1"), "value1"), 0, "mp str");
+
+ cdata = error_payload_get_mp(&p, "key1", &size);
+ is(memcmp(cdata, buf, size), 0, "mp str cmp");
+
+ data = mp_encode_uint(buf, 100);
+ error_payload_set_mp(&p, "key2", buf, data - buf);
+ uint64_t val;
+ ok(error_payload_get_uint(&p, "key2", &val) && val == 100, "mp uint");
+
+ cdata = error_payload_get_mp(&p, "key2", &size);
+ is(memcmp(cdata, buf, size), 0, "mp uint cmp");
+
+ data = mp_encode_array(buf, 1);
+ data = mp_encode_uint(data, 2);
+ error_payload_set_mp(&p, "key3", buf, data - buf);
+
+ cdata = error_payload_get_mp(&p, "key3", &size);
+ is(memcmp(cdata, buf, size), 0, "mp array");
+
+ ok(!error_payload_get_uint(&p, "key3", &val) && val == 0,
+ "mp uint from array");
+
+ error_payload_destroy(&p);
+
+ check_plan();
+ footer();
+}
+
+static void
+test_payload_clear(void)
+{
+ header();
+ plan(13);
+
+ struct error_payload p;
+ error_payload_create(&p);
+
+ error_payload_set_uint(&p, "key1", 1);
+ error_payload_set_uint(&p, "key2", 2);
+ error_payload_set_uint(&p, "key3", 3);
+ error_payload_set_uint(&p, "key4", 4);
+ error_payload_set_uint(&p, "key5", 5);
+
+ error_payload_clear(&p, "key5");
+ is(p.count, 4, "clear last, count");
+ is(error_payload_find(&p, "key5"), NULL, "clear last, key");
+
+ error_payload_clear(&p, "key1");
+ is(p.count, 3, "clear first, count");
+ is(error_payload_find(&p, "key1"), NULL, "clear first, key");
+
+ uint64_t val;
+ ok(error_payload_get_uint(&p, "key2", &val) && val == 2, "check key2");
+ ok(error_payload_get_uint(&p, "key3", &val) && val == 3, "check key3");
+ ok(error_payload_get_uint(&p, "key4", &val) && val == 4, "check key4");
+
+ is(strcmp(p.fields[0]->name, "key4"), 0, "deletion is cyclic");
+
+ error_payload_clear(&p, "key2");
+ is(p.count, 2, "clear middle, count");
+ is(error_payload_find(&p, "key2"), NULL, "clear middle, key");
+ ok(error_payload_get_uint(&p, "key3", &val) && val == 3, "check key3");
+ ok(error_payload_get_uint(&p, "key4", &val) && val == 4, "check key4");
+
+ error_payload_clear(&p, "key3");
+ error_payload_clear(&p, "key4");
+
+ is(p.count, 0, "clear all");
+
+ error_payload_destroy(&p);
+
+ check_plan();
+ footer();
+}
+
+static void
+test_payload_move(void)
+{
+ header();
+ plan(7);
+
+ struct error_payload p1, p2;
+ error_payload_create(&p1);
+ error_payload_create(&p2);
+
+ error_payload_move(&p1, &p2);
+ ok(p1.count == 0 && p1.fields == NULL, "empty");
+
+ error_payload_set_str(&p1, "key", "value");
+ error_payload_move(&p1, &p2);
+ ok(p1.count == 0 && p1.fields == NULL, "emptied on move");
+
+ error_payload_set_str(&p1, "key", "value");
+ error_payload_set_str(&p2, "key1", "value1");
+ error_payload_set_str(&p2, "key2", "value2");
+ error_payload_move(&p1, &p2);
+ is(p1.count, 2, "got 2 fields");
+ isnt(p1.fields, NULL, "got 2 fields");
+ is(strcmp(error_payload_get_str(&p1, "key1"), "value1"), 0, "key1");
+ is(strcmp(error_payload_get_str(&p1, "key2"), "value2"), 0, "key2");
+ is(error_payload_get_str(&p1, "key"), NULL, "key");
+
+ error_payload_destroy(&p2);
+ error_payload_destroy(&p1);
+
+ check_plan();
+ footer();
+}
+
+int
+main(void)
+{
+ header();
+ plan(9);
+
+ random_init();
+
+ test_payload_field_str();
+ test_payload_field_uint();
+ test_payload_field_int();
+ test_payload_field_double();
+ test_payload_field_bool();
+ test_payload_field_uuid();
+ test_payload_field_mp();
+ test_payload_clear();
+ test_payload_move();
+
+ random_free();
+
+ footer();
+ return check_plan();
+}
diff --git a/test/unit/error.result b/test/unit/error.result
new file mode 100644
index 000000000..daf4e6eb0
--- /dev/null
+++ b/test/unit/error.result
@@ -0,0 +1,161 @@
+ *** main ***
+1..9
+ *** test_payload_field_str ***
+ 1..15
+ ok 1 - no fields in the beginning
+ ok 2 - get_str() empty
+ ok 3 - ++count
+ ok 4 - get_str() finds
+ ok 5 - ++count
+ ok 6 - get_str() finds old
+ ok 7 - get_str() finds new
+ ok 8 - size does not include terminating zero
+ ok 9 - field count is same
+ ok 10 - get_str() finds new value
+ ok 11 - get_str() finds other key old value
+ ok 12 - --count
+ ok 13 - get_str() finds new value
+ ok 14 - get_str() can't find deleted value
+ ok 15 - wrong type
+ok 1 - subtests
+ *** test_payload_field_str: done ***
+ *** test_payload_field_uint ***
+ 1..17
+ ok 1 - get_uint() empty
+ ok 2 - ++count
+ ok 3 - get_uint() finds
+ ok 4 - value match
+ ok 5 - get_uint() fails
+ ok 6 - value is default
+ ok 7 - small number size
+ ok 8 - get_uint() 32 bit
+ ok 9 - value match
+ ok 10 - middle number size
+ ok 11 - field count is same
+ ok 12 - value 1
+ ok 13 - value 2
+ ok 14 - --count
+ ok 15 - remained value
+ ok 16 - deleted value
+ ok 17 - wrong type
+ok 2 - subtests
+ *** test_payload_field_uint: done ***
+ *** test_payload_field_int ***
+ 1..20
+ ok 1 - get_int() empty
+ ok 2 - ++count
+ ok 3 - get_int() finds
+ ok 4 - value match
+ ok 5 - get_int() fails
+ ok 6 - value is default
+ ok 7 - small number size
+ ok 8 - get_int() 32 bit
+ ok 9 - value match
+ ok 10 - middle number size
+ ok 11 - field count is same
+ ok 12 - value 1
+ ok 13 - value 2
+ ok 14 - --count
+ ok 15 - remained value
+ ok 16 - deleted value
+ ok 17 - wrong type
+ ok 18 - overflow
+ ok 19 - conversion
+ ok 20 - min value
+ok 3 - subtests
+ *** test_payload_field_int: done ***
+ *** test_payload_field_double ***
+ 1..14
+ ok 1 - get_double() empty
+ ok 2 - ++count
+ ok 3 - get_double() finds
+ ok 4 - value match
+ ok 5 - get_double() fails
+ ok 6 - value is default
+ ok 7 - size
+ ok 8 - value 1
+ ok 9 - value 2
+ ok 10 - --count
+ ok 11 - remained value
+ ok 12 - deleted value
+ ok 13 - wrong type
+ ok 14 - float 0.5
+ok 4 - subtests
+ *** test_payload_field_double: done ***
+ *** test_payload_field_bool ***
+ 1..13
+ ok 1 - get_bool() empty
+ ok 2 - ++count
+ ok 3 - get_bool() finds
+ ok 4 - value match
+ ok 5 - get_bool() fails
+ ok 6 - value is default
+ ok 7 - get_bool() finds
+ ok 8 - value match
+ ok 9 - size
+ ok 10 - --count
+ ok 11 - remained value
+ ok 12 - deleted value
+ ok 13 - wrong type
+ok 5 - subtests
+ *** test_payload_field_bool: done ***
+ *** test_payload_field_uuid ***
+ 1..17
+ ok 1 - get_uuid() empty
+ ok 2 - default value
+ ok 3 - ++count
+ ok 4 - get_uuid() finds
+ ok 5 - value match
+ ok 6 - get_uuid() fails
+ ok 7 - value is default
+ ok 8 - get_uuid() finds
+ ok 9 - value match
+ ok 10 - size
+ ok 11 - --count
+ ok 12 - remained value
+ ok 13 - value match
+ ok 14 - deleted value
+ ok 15 - value match
+ ok 16 - wrong type
+ ok 17 - value match
+ok 6 - subtests
+ *** test_payload_field_uuid: done ***
+ *** test_payload_field_mp ***
+ 1..6
+ ok 1 - mp str
+ ok 2 - mp str cmp
+ ok 3 - mp uint
+ ok 4 - mp uint cmp
+ ok 5 - mp array
+ ok 6 - mp uint from array
+ok 7 - subtests
+ *** test_payload_field_mp: done ***
+ *** test_payload_clear ***
+ 1..13
+ ok 1 - clear last, count
+ ok 2 - clear last, key
+ ok 3 - clear first, count
+ ok 4 - clear first, key
+ ok 5 - check key2
+ ok 6 - check key3
+ ok 7 - check key4
+ ok 8 - deletion is cyclic
+ ok 9 - clear middle, count
+ ok 10 - clear middle, key
+ ok 11 - check key3
+ ok 12 - check key4
+ ok 13 - clear all
+ok 8 - subtests
+ *** test_payload_clear: done ***
+ *** test_payload_move ***
+ 1..7
+ ok 1 - empty
+ ok 2 - emptied on move
+ ok 3 - got 2 fields
+ ok 4 - got 2 fields
+ ok 5 - key1
+ ok 6 - key2
+ ok 7 - key
+ok 9 - subtests
+ *** test_payload_move: done ***
+ *** main: done ***
--
2.24.3 (Apple Git-128)
More information about the Tarantool-patches
mailing list