Skip to content

Commit

Permalink
Merge pull request #285 from PJK/describetest2
Browse files Browse the repository at this point in the history
Improve cbor_describe formatting and code quality
  • Loading branch information
PJK committed Jun 3, 2023
2 parents 219c2e7 + aa82657 commit 9ac3066
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 107 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Expand Up @@ -4,6 +4,9 @@ Template:
Next
---------------------
- [Updated documentation to refer to RFC 8949](https://github.com/PJK/libcbor/issues/269)
- Improvements to `cbor_describe`
- [Bytestring data will now be printed as well](https://github.com/PJK/libcbor/pull/281) by [akallabeth](https://github.com/akallabeth)
- [Formatting consistency and clarity improvements](https://github.com/PJK/libcbor/pull/285)

0.10.2 (2023-01-31)
---------------------
Expand Down
55 changes: 30 additions & 25 deletions src/cbor.c
Expand Up @@ -306,7 +306,7 @@ static void _cbor_type_marquee(FILE *out, char *label, int indent) {
}

static void _cbor_nested_describe(cbor_item_t *item, FILE *out, int indent) {
setlocale(LC_ALL, "");
const int indent_offset = 4;
switch (cbor_typeof(item)) {
case CBOR_TYPE_UINT: {
_cbor_type_marquee(out, "CBOR_TYPE_UINT", indent);
Expand All @@ -323,15 +323,16 @@ static void _cbor_nested_describe(cbor_item_t *item, FILE *out, int indent) {
case CBOR_TYPE_BYTESTRING: {
_cbor_type_marquee(out, "CBOR_TYPE_BYTESTRING", indent);
if (cbor_bytestring_is_indefinite(item)) {
fprintf(out, "Indefinite, with %zu chunks:\n",
fprintf(out, "Indefinite, Chunks: %zu, Chunk data:\n",
cbor_bytestring_chunk_count(item));
for (size_t i = 0; i < cbor_bytestring_chunk_count(item); i++)
_cbor_nested_describe(cbor_bytestring_chunks_handle(item)[i], out,
indent + 4);
indent + indent_offset);
} else {
const unsigned char* data = cbor_bytestring_handle(item);
fprintf(out, "Definite, length %zuB\n", cbor_bytestring_length(item));
fprintf(out, "%*s", indent + 4, " ");
const unsigned char *data = cbor_bytestring_handle(item);
fprintf(out, "Definite, Length: %zuB, Data:\n",
cbor_bytestring_length(item));
fprintf(out, "%*s", indent + indent_offset, " ");
for (size_t i = 0; i < cbor_bytestring_length(item); i++)
fprintf(out, "%02x", (int)(data[i] & 0xff));
fprintf(out, "\n");
Expand All @@ -341,56 +342,60 @@ static void _cbor_nested_describe(cbor_item_t *item, FILE *out, int indent) {
case CBOR_TYPE_STRING: {
_cbor_type_marquee(out, "CBOR_TYPE_STRING", indent);
if (cbor_string_is_indefinite(item)) {
fprintf(out, "Indefinite, with %zu chunks:\n",
fprintf(out, "Indefinite, Chunks: %zu, Chunk data:\n",
cbor_string_chunk_count(item));
for (size_t i = 0; i < cbor_string_chunk_count(item); i++)
_cbor_nested_describe(cbor_string_chunks_handle(item)[i], out,
indent + 4);
indent + indent_offset);
} else {
fprintf(out, "Definite, length %zuB, %zu codepoints\n",
fprintf(out, "Definite, Length: %zuB, Codepoints: %zu, Data:\n",
cbor_string_length(item), cbor_string_codepoint_count(item));
/* Careful - this doesn't support multibyte characters! */
/* Printing those is out of the scope of this demo :) */
/* libICU is your friend */
// TODO: Handle escaping
fprintf(out, "%*s", indent + 4, " ");
/* XXX: no null at the end -> confused vprintf */
fwrite(cbor_string_handle(item), (int)cbor_string_length(item), 1, out);
fprintf(out, "%*s", indent + indent_offset, " ");
// Note: The string is not escaped, whitespace and control character
// will be printed in verbatim and take effect.
fwrite(cbor_string_handle(item), sizeof(unsigned char),
cbor_string_length(item), out);
fprintf(out, "\n");
}
break;
}
case CBOR_TYPE_ARRAY: {
_cbor_type_marquee(out, "CBOR_TYPE_ARRAY", indent);
if (cbor_array_is_definite(item)) {
fprintf(out, "Definite, size: %zu\n", cbor_array_size(item));
fprintf(out, "Definite, Size: %zu, Contents:\n", cbor_array_size(item));
} else {
fprintf(out, "Indefinite, size: %zu\n", cbor_array_size(item));
fprintf(out, "Indefinite, Size: %zu, Contents:\n",
cbor_array_size(item));
}

for (size_t i = 0; i < cbor_array_size(item); i++)
_cbor_nested_describe(cbor_array_handle(item)[i], out, indent + 4);
_cbor_nested_describe(cbor_array_handle(item)[i], out,
indent + indent_offset);
break;
}
case CBOR_TYPE_MAP: {
_cbor_type_marquee(out, "CBOR_TYPE_MAP", indent);
if (cbor_map_is_definite(item)) {
fprintf(out, "Definite, size: %zu\n", cbor_map_size(item));
fprintf(out, "Definite, Size: %zu, Contents:\n", cbor_map_size(item));
} else {
fprintf(out, "Indefinite, size: %zu\n", cbor_map_size(item));
fprintf(out, "Indefinite, Size: %zu, Contents:\n", cbor_map_size(item));
}

// TODO: Label and group keys and values
for (size_t i = 0; i < cbor_map_size(item); i++) {
_cbor_nested_describe(cbor_map_handle(item)[i].key, out, indent + 4);
_cbor_nested_describe(cbor_map_handle(item)[i].value, out, indent + 4);
fprintf(out, "%*sMap entry %zu\n", indent + indent_offset, " ", i);
_cbor_nested_describe(cbor_map_handle(item)[i].key, out,
indent + 2 * indent_offset);
_cbor_nested_describe(cbor_map_handle(item)[i].value, out,
indent + 2 * indent_offset);
}
break;
}
case CBOR_TYPE_TAG: {
_cbor_type_marquee(out, "CBOR_TYPE_TAG", indent);
fprintf(out, "Value: %" PRIu64 "\n", cbor_tag_value(item));
_cbor_nested_describe(cbor_move(cbor_tag_item(item)), out, indent + 4);
_cbor_nested_describe(cbor_move(cbor_tag_item(item)), out,
indent + indent_offset);
break;
}
case CBOR_TYPE_FLOAT_CTRL: {
Expand All @@ -406,7 +411,7 @@ static void _cbor_nested_describe(cbor_item_t *item, FILE *out, int indent) {
fprintf(out, "Simple value: %d\n", cbor_ctrl_value(item));
} else {
fprintf(out, "Width: %dB, ", _pow(2, cbor_float_get_width(item)));
fprintf(out, "value: %lf\n", cbor_float_get_float(item));
fprintf(out, "Value: %lf\n", cbor_float_get_float(item));
}
break;
}
Expand Down
162 changes: 80 additions & 82 deletions test/pretty_printer_test.c
Expand Up @@ -11,52 +11,46 @@
#include "assertions.h"
#include "cbor.h"

void assert_describe_result(cbor_item_t *item, char *result[], size_t lines) {
void assert_describe_result(cbor_item_t *item, char *expected_result) {
#if CBOR_PRETTY_PRINTER
// We know the expected size based on `expected_result`, but read everything
// in order to get the full actual output in a useful error message.
const size_t buffer_size = 512;
FILE *outfile = tmpfile();
cbor_describe(item, outfile);
rewind(outfile);
for (size_t i = 0; i < lines; i++) {
// Expected line + linebreak + null character
size_t buffer_size = strlen(result[i]) + 2;
char *buffer = malloc(buffer_size);
char *result_with_newline = malloc(buffer_size);
assert_non_null(buffer);
assert_non_null(result_with_newline);
snprintf(result_with_newline, buffer_size, "%s\n", result[i]);
fgets(buffer, strlen(result[i]) + 2, outfile);
assert_int_equal(strlen(buffer), strlen(result_with_newline));
assert_string_equal(buffer, result_with_newline);
free(buffer);
free(result_with_newline);
}
fgetc(outfile);
// Treat string as null-terminated since cmocka doesn't have asserts
// for explicit length strings.
char *output = malloc(buffer_size);
assert_non_null(output);
size_t output_size = fread(output, sizeof(char), buffer_size, outfile);
output[output_size] = '\0';
assert_string_equal(output, expected_result);
assert_true(feof(outfile));
free(output);
fclose(outfile);
#endif
}

static void test_uint(void **_CBOR_UNUSED(_state)) {
cbor_item_t *item = cbor_build_uint8(42);
char *expected[] = {"[CBOR_TYPE_UINT] Width: 1B, Value: 42"};
assert_describe_result(item, expected, 1);
assert_describe_result(item, "[CBOR_TYPE_UINT] Width: 1B, Value: 42\n");
cbor_decref(&item);
}

static void test_negint(void **_CBOR_UNUSED(_state)) {
cbor_item_t *item = cbor_build_negint16(40);
char *expected[] = {"[CBOR_TYPE_NEGINT] Width: 2B, Value: -40 - 1"};
assert_describe_result(item, expected, 1);
assert_describe_result(item,
"[CBOR_TYPE_NEGINT] Width: 2B, Value: -40 - 1\n");
cbor_decref(&item);
}

static void test_definite_bytestring(void **_CBOR_UNUSED(_state)) {
unsigned char data[] = {0x01, 0x02, 0x03};
cbor_item_t *item = cbor_build_bytestring(data, 3);
char *expected[] = {
"[CBOR_TYPE_BYTESTRING] Definite, length 3B",
};
assert_describe_result(item, expected, 1);
assert_describe_result(item,
"[CBOR_TYPE_BYTESTRING] Definite, Length: 3B, Data:\n"
" 010203\n");
cbor_decref(&item);
}

Expand All @@ -67,24 +61,24 @@ static void test_indefinite_bytestring(void **_CBOR_UNUSED(_state)) {
item, cbor_move(cbor_build_bytestring(data, 3))));
assert_true(cbor_bytestring_add_chunk(
item, cbor_move(cbor_build_bytestring(data, 2))));
char *expected[] = {
"[CBOR_TYPE_BYTESTRING] Indefinite, with 2 chunks:",
" [CBOR_TYPE_BYTESTRING] Definite, length 3B",
" [CBOR_TYPE_BYTESTRING] Definite, length 2B",
};
assert_describe_result(item, expected, 3);
assert_describe_result(
item,
"[CBOR_TYPE_BYTESTRING] Indefinite, Chunks: 2, Chunk data:\n"
" [CBOR_TYPE_BYTESTRING] Definite, Length: 3B, Data:\n"
" 010203\n"
" [CBOR_TYPE_BYTESTRING] Definite, Length: 2B, Data:\n"
" 0102\n");
cbor_decref(&item);
}

static void test_definite_string(void **_CBOR_UNUSED(_state)) {
char *string = "Hello!";
cbor_item_t *item = cbor_build_string(string);
char *expected[] = {
// TODO: Codepoint metadata is not set by the builder, fix.
"[CBOR_TYPE_STRING] Definite, length 6B, 0 codepoints",
" Hello!",
};
assert_describe_result(item, expected, 2);
// TODO: Codepoint metadata is not set by the builder, fix.
assert_describe_result(
item,
"[CBOR_TYPE_STRING] Definite, Length: 6B, Codepoints: 0, Data:\n"
" Hello!\n");
cbor_decref(&item);
}

Expand All @@ -95,40 +89,47 @@ static void test_indefinite_string(void **_CBOR_UNUSED(_state)) {
cbor_string_add_chunk(item, cbor_move(cbor_build_string(string))));
assert_true(
cbor_string_add_chunk(item, cbor_move(cbor_build_string(string))));
char *expected[] = {
"[CBOR_TYPE_STRING] Indefinite, with 2 chunks:",
" [CBOR_TYPE_STRING] Definite, length 6B, 0 codepoints",
" Hello!",
" [CBOR_TYPE_STRING] Definite, length 6B, 0 codepoints",
" Hello!",
};
assert_describe_result(item, expected, 5);
assert_describe_result(
item,
"[CBOR_TYPE_STRING] Indefinite, Chunks: 2, Chunk data:\n"
" [CBOR_TYPE_STRING] Definite, Length: 6B, Codepoints: 0, Data:\n"
" Hello!\n"
" [CBOR_TYPE_STRING] Definite, Length: 6B, Codepoints: 0, Data:\n"
" Hello!\n");
cbor_decref(&item);
}

static void test_multibyte_string(void **_CBOR_UNUSED(_state)) {
// "Štěstíčko" in UTF-8
char *string = "\xc5\xa0t\xc4\x9bst\xc3\xad\xc4\x8dko";
cbor_item_t *item = cbor_build_string(string);
// TODO: Codepoint metadata is not set by the builder, fix.
assert_describe_result(
item,
"[CBOR_TYPE_STRING] Definite, Length: 13B, Codepoints: 0, Data:\n"
" \xc5\xa0t\xc4\x9bst\xc3\xad\xc4\x8dko\n");
cbor_decref(&item);
}

static void test_definite_array(void **_CBOR_UNUSED(_state)) {
cbor_item_t *item = cbor_new_definite_array(2);
assert_true(cbor_array_push(item, cbor_move(cbor_build_uint8(1))));
assert_true(cbor_array_push(item, cbor_move(cbor_build_uint8(2))));
char *expected[] = {
"[CBOR_TYPE_ARRAY] Definite, size: 2",
" [CBOR_TYPE_UINT] Width: 1B, Value: 1",
" [CBOR_TYPE_UINT] Width: 1B, Value: 2",
};
assert_describe_result(item, expected, 3);
assert_describe_result(item,
"[CBOR_TYPE_ARRAY] Definite, Size: 2, Contents:\n"
" [CBOR_TYPE_UINT] Width: 1B, Value: 1\n"
" [CBOR_TYPE_UINT] Width: 1B, Value: 2\n");
cbor_decref(&item);
}

static void test_indefinite_array(void **_CBOR_UNUSED(_state)) {
cbor_item_t *item = cbor_new_indefinite_array();
assert_true(cbor_array_push(item, cbor_move(cbor_build_uint8(1))));
assert_true(cbor_array_push(item, cbor_move(cbor_build_uint8(2))));
char *expected[] = {
"[CBOR_TYPE_ARRAY] Indefinite, size: 2",
" [CBOR_TYPE_UINT] Width: 1B, Value: 1",
" [CBOR_TYPE_UINT] Width: 1B, Value: 2",
};
assert_describe_result(item, expected, 3);
assert_describe_result(item,
"[CBOR_TYPE_ARRAY] Indefinite, Size: 2, Contents:\n"
" [CBOR_TYPE_UINT] Width: 1B, Value: 1\n"
" [CBOR_TYPE_UINT] Width: 1B, Value: 2\n");
cbor_decref(&item);
}

Expand All @@ -137,12 +138,12 @@ static void test_definite_map(void **_CBOR_UNUSED(_state)) {
assert_true(cbor_map_add(
item, (struct cbor_pair){.key = cbor_move(cbor_build_uint8(1)),
.value = cbor_move(cbor_build_uint8(2))}));
char *expected[] = {
"[CBOR_TYPE_MAP] Definite, size: 1",
" [CBOR_TYPE_UINT] Width: 1B, Value: 1",
" [CBOR_TYPE_UINT] Width: 1B, Value: 2",
};
assert_describe_result(item, expected, 3);
cbor_describe(item, stdout);
assert_describe_result(item,
"[CBOR_TYPE_MAP] Definite, Size: 1, Contents:\n"
" Map entry 0\n"
" [CBOR_TYPE_UINT] Width: 1B, Value: 1\n"
" [CBOR_TYPE_UINT] Width: 1B, Value: 2\n");
cbor_decref(&item);
}

Expand All @@ -151,22 +152,19 @@ static void test_indefinite_map(void **_CBOR_UNUSED(_state)) {
assert_true(cbor_map_add(
item, (struct cbor_pair){.key = cbor_move(cbor_build_uint8(1)),
.value = cbor_move(cbor_build_uint8(2))}));
char *expected[] = {
"[CBOR_TYPE_MAP] Indefinite, size: 1",
" [CBOR_TYPE_UINT] Width: 1B, Value: 1",
" [CBOR_TYPE_UINT] Width: 1B, Value: 2",
};
assert_describe_result(item, expected, 3);
assert_describe_result(item,
"[CBOR_TYPE_MAP] Indefinite, Size: 1, Contents:\n"
" Map entry 0\n"
" [CBOR_TYPE_UINT] Width: 1B, Value: 1\n"
" [CBOR_TYPE_UINT] Width: 1B, Value: 2\n");
cbor_decref(&item);
}

static void test_tag(void **_CBOR_UNUSED(_state)) {
cbor_item_t *item = cbor_build_tag(42, cbor_move(cbor_build_uint8(1)));
char *expected[] = {
"[CBOR_TYPE_TAG] Value: 42",
" [CBOR_TYPE_UINT] Width: 1B, Value: 1",
};
assert_describe_result(item, expected, 2);
assert_describe_result(item,
"[CBOR_TYPE_TAG] Value: 42\n"
" [CBOR_TYPE_UINT] Width: 1B, Value: 1\n");
cbor_decref(&item);
}

Expand All @@ -179,15 +177,14 @@ static void test_floats(void **_CBOR_UNUSED(_state)) {
cbor_array_push(item, cbor_move(cbor_build_ctrl(CBOR_CTRL_NULL))));
assert_true(cbor_array_push(item, cbor_move(cbor_build_ctrl(24))));
assert_true(cbor_array_push(item, cbor_move(cbor_build_float4(3.14f))));
char *expected[] = {
"[CBOR_TYPE_ARRAY] Indefinite, size: 5",
" [CBOR_TYPE_FLOAT_CTRL] Bool: true",
" [CBOR_TYPE_FLOAT_CTRL] Undefined",
" [CBOR_TYPE_FLOAT_CTRL] Null",
" [CBOR_TYPE_FLOAT_CTRL] Simple value: 24",
" [CBOR_TYPE_FLOAT_CTRL] Width: 4B, value: 3.140000",
};
assert_describe_result(item, expected, 6);
assert_describe_result(
item,
"[CBOR_TYPE_ARRAY] Indefinite, Size: 5, Contents:\n"
" [CBOR_TYPE_FLOAT_CTRL] Bool: true\n"
" [CBOR_TYPE_FLOAT_CTRL] Undefined\n"
" [CBOR_TYPE_FLOAT_CTRL] Null\n"
" [CBOR_TYPE_FLOAT_CTRL] Simple value: 24\n"
" [CBOR_TYPE_FLOAT_CTRL] Width: 4B, Value: 3.140000\n");
cbor_decref(&item);
}

Expand All @@ -199,6 +196,7 @@ int main(void) {
cmocka_unit_test(test_indefinite_bytestring),
cmocka_unit_test(test_definite_string),
cmocka_unit_test(test_indefinite_string),
cmocka_unit_test(test_multibyte_string),
cmocka_unit_test(test_definite_array),
cmocka_unit_test(test_indefinite_array),
cmocka_unit_test(test_definite_map),
Expand Down

0 comments on commit 9ac3066

Please sign in to comment.