From: Serge Petrenko via Tarantool-patches <tarantool-patches@dev.tarantool.org> To: Vladislav Shpilevoy <v.shpilevoy@tarantool.org>, tarantool-patches@dev.tarantool.org, vdavydov@tarantool.org Subject: Re: [Tarantool-patches] [PATCH 2/9] error: introduce error_payload Date: Mon, 8 Nov 2021 18:14:11 +0300 [thread overview] Message-ID: <68ebfc9b-5e2c-91c0-106a-8c309a31c1b3@tarantool.org> (raw) In-Reply-To: <0fa9f99b412387269401bed410c480b456eeb604.1636156453.git.v.shpilevoy@tarantool.org> 06.11.2021 02:56, Vladislav Shpilevoy пишет: > 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 Hi! Thanks for the patch! Please, find 6 comments below. > --- > src/lib/core/CMakeLists.txt | 1 + > src/lib/core/error_payload.c | 282 +++++++++++++++++++++ > src/lib/core/error_payload.h | 166 +++++++++++++ > src/lib/uuid/mp_uuid.c | 26 ++ > src/lib/uuid/tt_uuid.h | 24 ++ > test/unit/CMakeLists.txt | 2 + > test/unit/error.c | 461 +++++++++++++++++++++++++++++++++++ > test/unit/error.result | 160 ++++++++++++ > 8 files changed, 1122 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 0cc742a1c..860758515 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..98ef08ada > --- /dev/null > +++ b/src/lib/core/error_payload.c > @@ -0,0 +1,282 @@ > +/* > + * SPDX-License-Identifier: BSD-2-Clause > + * > + * Copyright 2010-2021, Tarantool AUTHORS, please see AUTHORS file. > + */ > +#include "error_payload.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; > + /* > + * 0 is explicitly written after encoded string to be able to return > + * them without copying. > + */ > + data = mp_decode_str(&data, &len); > + assert(data[len] == 0); 1. Nit: `data[len] == '\0'`. Here and everywhere else. This is up to you, I'm just more used to '\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. > + */ 2. This comment is almost the same as the one in payload_get_str(). I propose to leave only one of them. Preferably the one in set_str(). > + *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; > +} > + 3. Consider this diff here. (Similar places below do need a label, but this one doesn't). ============================ diff --git a/src/lib/core/error_payload.c b/src/lib/core/error_payload.c index 98ef08ada..cfa30919e 100644 --- a/src/lib/core/error_payload.c +++ b/src/lib/core/error_payload.c @@ -62,17 +62,14 @@ 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); + *value = 0; if (f == NULL) - goto not_found; + return false; const char *data = f->data; if (mp_typeof(*data) != MP_UINT) - goto not_found; + return false; *value = mp_decode_uint(&data); return true; - -not_found: - *value = 0; - return false; } void ============================ > +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; > + } 4. Why do you check for both MP_DOUBLE and MP_FLOAT here? You only encode MP_DOUBLE. I think we might have set_float() and get_float() one day, but looks like they're not needed now. get_float() would only accept MP_FLOAT then, and get_double() should only accept MP_DOUBLE. > +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); > +} > + > +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; > + /* > + * Add the terminating zero after the buffer so as get_str() > + * would work without copying. > + */ 5. Maybe shorten this comment to "\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_unset(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 = name_size + sizeof(*f); > + 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..b6bf9ceee > --- /dev/null > +++ b/src/lib/core/error_payload.h > @@ -0,0 +1,166 @@ > +/* > + * 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) */ > + > +/** Single field of error payload. */ > +struct error_field { > + /** Data size. */ > + uint32_t size; > + /** MessagePack field value. */ > + char *data; > + /** Zero terminated field name. */ > + char name[0]; > +}; > + > +/** An array of key-value pairs. */ > +struct error_payload { > + /** Number of fields. */ > + int count; > + /** > + * Array of field pointers. Not very optimized, but the errors are > + * supposed to be rare. Also 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 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_unset(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/src/lib/uuid/mp_uuid.c b/src/lib/uuid/mp_uuid.c > index b2341ae36..a60716eb5 100644 > --- a/src/lib/uuid/mp_uuid.c > +++ b/src/lib/uuid/mp_uuid.c > @@ -32,6 +32,7 @@ > #include "mp_uuid.h" > #include "msgpuck.h" > #include "mp_extension_types.h" > +#include "error_payload.h" > > inline uint32_t > mp_sizeof_uuid(void) > @@ -113,3 +114,28 @@ mp_fprint_uuid(FILE *file, const char **data, uint32_t len) > return -1; > return fprintf(file, "%s", tt_uuid_str(&uuid)); > } > + > +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; > +} > + 6. I propose to get rid of the label here and set uuid to nil unconditionally. Would it be too expensive to do 2 struct assignments instead of one? (uuid = uuid_nil; uuid = decoded_value). > +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); > +} > diff --git a/src/lib/uuid/tt_uuid.h b/src/lib/uuid/tt_uuid.h > index 70c3b98b1..866764e77 100644 > --- a/src/lib/uuid/tt_uuid.h > +++ b/src/lib/uuid/tt_uuid.h > @@ -41,6 +41,8 @@ > extern "C" { > #endif > > +struct error_payload; > + > enum { UUID_LEN = 16, UUID_STR_LEN = 36 }; > > /** > @@ -182,6 +184,28 @@ tt_uuid_str(const struct tt_uuid *uu); > int > tt_uuid_from_strl(const char *in, size_t len, struct tt_uuid *uu); > > +/** > + * Error payload couldn't be implemented in libcore because requires UUID type > + * and its methods. That would be a cyclic dep. Thus in places where need to use > + * UUID fields in error payload also need to include libuuid. > + */ > + > +/** > + * 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); > + > #if defined(__cplusplus) > } /* extern "C" */ > #endif > diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt > index ae8b5b9ac..0bb71ccce 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 uuid) > > 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..ef17c56ec > --- /dev/null > +++ b/test/unit/error.c > @@ -0,0 +1,461 @@ > +/* > + * SPDX-License-Identifier: BSD-2-Clause > + * > + * Copyright 2010-2021, Tarantool AUTHORS, please see AUTHORS file. > + */ > +#include "error_payload.h" > +#include "msgpuck.h" > +#include "random.h" > +#include "unit.h" > +#include "uuid/mp_uuid.h" > +#include "uuid/tt_uuid.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_unset(&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_unset(&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_unset(&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(13); > + > + 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_unset(&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"); > + > + 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_unset(&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_unset(&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_unset(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_unset(&p, "key5"); > + is(p.count, 4, "unset last, count"); > + is(error_payload_find(&p, "key5"), NULL, "unset last, key"); > + > + error_payload_unset(&p, "key1"); > + is(p.count, 3, "unset first, count"); > + is(error_payload_find(&p, "key1"), NULL, "unset 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_unset(&p, "key2"); > + is(p.count, 2, "unset middle, count"); > + is(error_payload_find(&p, "key2"), NULL, "unset 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_unset(&p, "key3"); > + error_payload_unset(&p, "key4"); > + > + is(p.count, 0, "unset 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_unset(); > + 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..f0c4266e6 > --- /dev/null > +++ b/test/unit/error.result > @@ -0,0 +1,160 @@ > + *** 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..13 > + 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 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_unset *** > + 1..13 > + ok 1 - unset last, count > + ok 2 - unset last, key > + ok 3 - unset first, count > + ok 4 - unset first, key > + ok 5 - check key2 > + ok 6 - check key3 > + ok 7 - check key4 > + ok 8 - deletion is cyclic > + ok 9 - unset middle, count > + ok 10 - unset middle, key > + ok 11 - check key3 > + ok 12 - check key4 > + ok 13 - unset all > +ok 8 - subtests > + *** test_payload_unset: 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 *** -- Serge Petrenko
next prev parent reply other threads:[~2021-11-08 15:14 UTC|newest] Thread overview: 24+ messages / expand[flat|nested] mbox.gz Atom feed top 2021-11-05 23:56 [Tarantool-patches] [PATCH 0/9] ER_READONLY reason Vladislav Shpilevoy via Tarantool-patches 2021-11-05 23:56 ` [Tarantool-patches] [PATCH 1/9] diag: return created error from diag_set() Vladislav Shpilevoy via Tarantool-patches 2021-11-05 23:56 ` [Tarantool-patches] [PATCH 2/9] error: introduce error_payload Vladislav Shpilevoy via Tarantool-patches 2021-11-08 15:14 ` Serge Petrenko via Tarantool-patches [this message] 2021-11-11 23:50 ` Vladislav Shpilevoy via Tarantool-patches 2021-11-12 6:29 ` Serge Petrenko via Tarantool-patches 2021-11-05 23:56 ` [Tarantool-patches] [PATCH 3/9] error: move code to struct error from ClientError Vladislav Shpilevoy via Tarantool-patches 2021-11-08 15:15 ` Serge Petrenko via Tarantool-patches 2021-11-11 23:50 ` Vladislav Shpilevoy via Tarantool-patches 2021-11-12 6:31 ` Serge Petrenko via Tarantool-patches 2021-11-05 23:56 ` [Tarantool-patches] [PATCH 4/9] error: use error_payload to store optional members Vladislav Shpilevoy via Tarantool-patches 2021-11-05 23:56 ` [Tarantool-patches] [PATCH 5/9] error: use error_payload in MessagePack codecs Vladislav Shpilevoy via Tarantool-patches 2021-11-05 23:56 ` [Tarantool-patches] [PATCH 6/9] error: use error_payload in Lua Vladislav Shpilevoy via Tarantool-patches 2021-11-05 23:56 ` [Tarantool-patches] [PATCH 7/9] luatest: copy config in cluster:build_server() Vladislav Shpilevoy via Tarantool-patches 2021-11-05 23:56 ` [Tarantool-patches] [PATCH 8/9] luatest: add new helpers for 'server' object Vladislav Shpilevoy via Tarantool-patches 2021-11-08 15:16 ` Serge Petrenko via Tarantool-patches 2021-11-11 23:51 ` Vladislav Shpilevoy via Tarantool-patches 2021-11-05 23:56 ` [Tarantool-patches] [PATCH 9/9] box: enrich ER_READONLY with new details Vladislav Shpilevoy via Tarantool-patches 2021-11-06 19:30 ` Cyrill Gorcunov via Tarantool-patches 2021-11-07 16:45 ` Vladislav Shpilevoy via Tarantool-patches 2021-11-07 20:19 ` Cyrill Gorcunov via Tarantool-patches 2021-11-08 15:18 ` Serge Petrenko via Tarantool-patches 2021-11-11 23:52 ` Vladislav Shpilevoy via Tarantool-patches 2021-11-08 14:25 ` [Tarantool-patches] [PATCH 0/9] ER_READONLY reason Vladimir Davydov via Tarantool-patches
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=68ebfc9b-5e2c-91c0-106a-8c309a31c1b3@tarantool.org \ --to=tarantool-patches@dev.tarantool.org \ --cc=sergepetrenko@tarantool.org \ --cc=v.shpilevoy@tarantool.org \ --cc=vdavydov@tarantool.org \ --subject='Re: [Tarantool-patches] [PATCH 2/9] error: introduce error_payload' \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox