libvirt/tests/virjsontest.c
Michal Privoznik d07ce21610 tests: Always put a '\n' after each debug print
There is an inconsistency with VIR_TEST_DEBUG() calls. One half
(roughly) of calls does have the newline character the other one
doesn't. Well, it doesn't have it because it assumed blindly that
new line will be printed, which is not the case.

Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
Reviewed-by: Ján Tomko <jtomko@redhat.com>
2019-08-27 15:49:48 +02:00

620 lines
19 KiB
C

#include <config.h>
#include <time.h>
#include "internal.h"
#include "virjson.h"
#include "testutils.h"
#define VIR_FROM_THIS VIR_FROM_NONE
struct testInfo {
const char *name;
const char *doc;
const char *expect;
bool pass;
};
static int
testJSONFromFile(const void *data)
{
const struct testInfo *info = data;
VIR_AUTOPTR(virJSONValue) injson = NULL;
VIR_AUTOFREE(char *) infile = NULL;
VIR_AUTOFREE(char *) indata = NULL;
VIR_AUTOFREE(char *) outfile = NULL;
VIR_AUTOFREE(char *) actual = NULL;
if (virAsprintf(&infile, "%s/virjsondata/parse-%s-in.json",
abs_srcdir, info->name) < 0 ||
virAsprintf(&outfile, "%s/virjsondata/parse-%s-out.json",
abs_srcdir, info->name) < 0)
return -1;
if (virTestLoadFile(infile, &indata) < 0)
return -1;
injson = virJSONValueFromString(indata);
if (!injson) {
if (info->pass) {
VIR_TEST_VERBOSE("Failed to parse %s\n", info->doc);
return -1;
} else {
VIR_TEST_DEBUG("As expected, failed to parse %s", info->doc);
return 0;
}
} else {
if (!info->pass) {
VIR_TEST_VERBOSE("Unexpected success while parsing %s\n", info->doc);
return -1;
}
}
if (!(actual = virJSONValueToString(injson, false)))
return -1;
if (virTestCompareToFile(actual, outfile) < 0)
return -1;
return 0;
}
static int
testJSONFromString(const void *data)
{
const struct testInfo *info = data;
VIR_AUTOPTR(virJSONValue) json = NULL;
const char *expectstr = info->expect ? info->expect : info->doc;
VIR_AUTOFREE(char *) formatted = NULL;
json = virJSONValueFromString(info->doc);
if (!json) {
if (info->pass) {
VIR_TEST_VERBOSE("Failed to parse %s\n", info->doc);
return -1;
} else {
VIR_TEST_DEBUG("As expected, failed to parse %s", info->doc);
return 0;
}
} else {
if (!info->pass) {
VIR_TEST_VERBOSE("Unexpected success while parsing %s\n", info->doc);
return -1;
}
}
VIR_TEST_DEBUG("Parsed %s", info->doc);
if (!(formatted = virJSONValueToString(json, false))) {
VIR_TEST_VERBOSE("Failed to format json data\n");
return -1;
}
if (STRNEQ(expectstr, formatted)) {
virTestDifference(stderr, expectstr, formatted);
return -1;
}
return 0;
}
static int
testJSONAddRemove(const void *data)
{
const struct testInfo *info = data;
VIR_AUTOPTR(virJSONValue) json = NULL;
VIR_AUTOPTR(virJSONValue) name = NULL;
VIR_AUTOFREE(char *) infile = NULL;
VIR_AUTOFREE(char *) indata = NULL;
VIR_AUTOFREE(char *) outfile = NULL;
VIR_AUTOFREE(char *) actual = NULL;
if (virAsprintf(&infile, "%s/virjsondata/add-remove-%s-in.json",
abs_srcdir, info->name) < 0 ||
virAsprintf(&outfile, "%s/virjsondata/add-remove-%s-out.json",
abs_srcdir, info->name) < 0)
return -1;
if (virTestLoadFile(infile, &indata) < 0)
return -1;
json = virJSONValueFromString(indata);
if (!json) {
VIR_TEST_VERBOSE("Fail to parse %s\n", info->name);
return -1;
}
switch (virJSONValueObjectRemoveKey(json, "name", &name)) {
case 1:
if (!info->pass) {
VIR_TEST_VERBOSE("should not remove from non-object %s\n",
info->name);
return -1;
}
break;
case -1:
if (!info->pass)
return 0;
else
VIR_TEST_VERBOSE("Fail to recognize non-object %s\n", info->name);
return -1;
default:
VIR_TEST_VERBOSE("unexpected result when removing from %s\n",
info->name);
return -1;
}
if (STRNEQ_NULLABLE(virJSONValueGetString(name), "sample")) {
VIR_TEST_VERBOSE("unexpected value after removing name: %s\n",
NULLSTR(virJSONValueGetString(name)));
return -1;
}
if (virJSONValueObjectRemoveKey(json, "name", NULL)) {
VIR_TEST_VERBOSE("%s",
"unexpected success when removing missing key\n");
return -1;
}
if (virJSONValueObjectAppendString(json, "newname", "foo") < 0) {
VIR_TEST_VERBOSE("%s", "unexpected failure adding new key\n");
return -1;
}
if (!(actual = virJSONValueToString(json, false))) {
VIR_TEST_VERBOSE("%s", "failed to stringize result\n");
return -1;
}
if (virTestCompareToFile(actual, outfile) < 0)
return -1;
return 0;
}
static int
testJSONLookup(const void *data)
{
const struct testInfo *info = data;
VIR_AUTOPTR(virJSONValue) json = NULL;
virJSONValuePtr value = NULL;
VIR_AUTOFREE(char *) result = NULL;
int rc;
int number;
const char *str;
json = virJSONValueFromString(info->doc);
if (!json) {
VIR_TEST_VERBOSE("Fail to parse %s\n", info->doc);
return -1;
}
value = virJSONValueObjectGetObject(json, "a");
if (value) {
if (!info->pass) {
VIR_TEST_VERBOSE("lookup for 'a' in '%s' should have failed\n",
info->doc);
return -1;
} else {
result = virJSONValueToString(value, false);
if (STRNEQ_NULLABLE(result, "{}")) {
VIR_TEST_VERBOSE("lookup for 'a' in '%s' found '%s' but "
"should have found '{}'\n",
info->doc, NULLSTR(result));
return -1;
}
VIR_FREE(result);
}
} else if (info->pass) {
VIR_TEST_VERBOSE("lookup for 'a' in '%s' should have succeeded\n",
info->doc);
return -1;
}
number = 2;
rc = virJSONValueObjectGetNumberInt(json, "b", &number);
if (rc == 0) {
if (!info->pass) {
VIR_TEST_VERBOSE("lookup for 'b' in '%s' should have failed\n",
info->doc);
return -1;
} else if (number != 1) {
VIR_TEST_VERBOSE("lookup for 'b' in '%s' found %d but "
"should have found 1\n",
info->doc, number);
return -1;
}
} else if (info->pass) {
VIR_TEST_VERBOSE("lookup for 'b' in '%s' should have succeeded\n",
info->doc);
return -1;
}
str = virJSONValueObjectGetString(json, "c");
if (str) {
if (!info->pass) {
VIR_TEST_VERBOSE("lookup for 'c' in '%s' should have failed\n",
info->doc);
return -1;
} else if (STRNEQ(str, "str")) {
VIR_TEST_VERBOSE("lookup for 'c' in '%s' found '%s' but "
"should have found 'str'\n", info->doc, str);
return -1;
}
} else if (info->pass) {
VIR_TEST_VERBOSE("lookup for 'c' in '%s' should have succeeded\n",
info->doc);
return -1;
}
value = virJSONValueObjectGetArray(json, "d");
if (value) {
if (!info->pass) {
VIR_TEST_VERBOSE("lookup for 'd' in '%s' should have failed\n",
info->doc);
return -1;
} else {
result = virJSONValueToString(value, false);
if (STRNEQ_NULLABLE(result, "[]")) {
VIR_TEST_VERBOSE("lookup for 'd' in '%s' found '%s' but "
"should have found '[]'\n",
info->doc, NULLSTR(result));
return -1;
}
VIR_FREE(result);
}
} else if (info->pass) {
VIR_TEST_VERBOSE("lookup for 'd' in '%s' should have succeeded\n",
info->doc);
return -1;
}
return 0;
}
static int
testJSONCopy(const void *data)
{
const struct testInfo *info = data;
VIR_AUTOPTR(virJSONValue) json = NULL;
VIR_AUTOPTR(virJSONValue) jsonCopy = NULL;
VIR_AUTOFREE(char *) result = NULL;
VIR_AUTOFREE(char *) resultCopy = NULL;
json = virJSONValueFromString(info->doc);
if (!json) {
VIR_TEST_VERBOSE("Failed to parse %s\n", info->doc);
return -1;
}
jsonCopy = virJSONValueCopy(json);
if (!jsonCopy) {
VIR_TEST_VERBOSE("Failed to copy JSON data\n");
return -1;
}
result = virJSONValueToString(json, false);
if (!result) {
VIR_TEST_VERBOSE("Failed to format original JSON data\n");
return -1;
}
resultCopy = virJSONValueToString(json, false);
if (!resultCopy) {
VIR_TEST_VERBOSE("Failed to format copied JSON data\n");
return -1;
}
if (STRNEQ(result, resultCopy)) {
if (virTestGetVerbose())
virTestDifference(stderr, result, resultCopy);
return -1;
}
VIR_FREE(result);
VIR_FREE(resultCopy);
result = virJSONValueToString(json, true);
if (!result) {
VIR_TEST_VERBOSE("Failed to format original JSON data\n");
return -1;
}
resultCopy = virJSONValueToString(json, true);
if (!resultCopy) {
VIR_TEST_VERBOSE("Failed to format copied JSON data\n");
return -1;
}
if (STRNEQ(result, resultCopy)) {
if (virTestGetVerbose())
virTestDifference(stderr, result, resultCopy);
return -1;
}
return 0;
}
static int
testJSONDeflatten(const void *data)
{
const struct testInfo *info = data;
VIR_AUTOPTR(virJSONValue) injson = NULL;
VIR_AUTOPTR(virJSONValue) deflattened = NULL;
VIR_AUTOFREE(char *) infile = NULL;
VIR_AUTOFREE(char *) indata = NULL;
VIR_AUTOFREE(char *) outfile = NULL;
VIR_AUTOFREE(char *) actual = NULL;
if (virAsprintf(&infile, "%s/virjsondata/deflatten-%s-in.json",
abs_srcdir, info->name) < 0 ||
virAsprintf(&outfile, "%s/virjsondata/deflatten-%s-out.json",
abs_srcdir, info->name) < 0)
return -1;
if (virTestLoadFile(infile, &indata) < 0)
return -1;
if (!(injson = virJSONValueFromString(indata)))
return -1;
if ((deflattened = virJSONValueObjectDeflatten(injson))) {
if (!info->pass) {
VIR_TEST_VERBOSE("%s: deflattening should have failed\n", info->name);
return -1;
}
} else {
if (!info->pass)
return 0;
return -1;
}
if (!(actual = virJSONValueToString(deflattened, true)))
return -1;
if (virTestCompareToFile(actual, outfile) < 0)
return -1;
return 0;
}
static int
testJSONEscapeObj(const void *data ATTRIBUTE_UNUSED)
{
VIR_AUTOPTR(virJSONValue) json = NULL;
VIR_AUTOPTR(virJSONValue) nestjson = NULL;
VIR_AUTOPTR(virJSONValue) parsejson = NULL;
VIR_AUTOFREE(char *) neststr = NULL;
VIR_AUTOFREE(char *) result = NULL;
const char *parsednestedstr;
if (virJSONValueObjectCreate(&nestjson,
"s:stringkey", "stringvalue",
"i:numberkey", 1234,
"b:booleankey", false, NULL) < 0) {
VIR_TEST_VERBOSE("failed to create nested json object");
return -1;
}
if (!(neststr = virJSONValueToString(nestjson, false))) {
VIR_TEST_VERBOSE("failed to format nested json object");
return -1;
}
if (virJSONValueObjectCreate(&json, "s:test", neststr, NULL) < 0) {
VIR_TEST_VERBOSE("Failed to create json object");
return -1;
}
if (!(result = virJSONValueToString(json, false))) {
VIR_TEST_VERBOSE("Failed to format json object");
return -1;
}
if (!(parsejson = virJSONValueFromString(result))) {
VIR_TEST_VERBOSE("Failed to parse JSON with nested JSON in string");
return -1;
}
if (!(parsednestedstr = virJSONValueObjectGetString(parsejson, "test"))) {
VIR_TEST_VERBOSE("Failed to retrieve string containing nested json");
return -1;
}
if (STRNEQ(parsednestedstr, neststr)) {
virTestDifference(stderr, neststr, parsednestedstr);
return -1;
}
return 0;
}
static int
testJSONObjectFormatSteal(const void *opaque ATTRIBUTE_UNUSED)
{
VIR_AUTOPTR(virJSONValue) a1 = NULL;
VIR_AUTOPTR(virJSONValue) a2 = NULL;
VIR_AUTOPTR(virJSONValue) t1 = NULL;
VIR_AUTOPTR(virJSONValue) t2 = NULL;
if (!(a1 = virJSONValueNewString("test")) ||
!(a2 = virJSONValueNewString("test"))) {
VIR_TEST_VERBOSE("Failed to create json object");
}
if (virJSONValueObjectCreate(&t1, "a:t", &a1, "s:f", NULL, NULL) != -1) {
VIR_TEST_VERBOSE("virJSONValueObjectCreate(t1) should have failed\n");
return -1;
}
if (a1) {
VIR_TEST_VERBOSE("appended object a1 was not consumed\n");
return -1;
}
if (virJSONValueObjectCreate(&t2, "s:f", NULL, "a:t", &a1, NULL) != -1) {
VIR_TEST_VERBOSE("virJSONValueObjectCreate(t2) should have failed\n");
return -1;
}
if (!a2) {
VIR_TEST_VERBOSE("appended object a2 was consumed\n");
return -1;
}
return 0;
}
static int
mymain(void)
{
int ret = 0;
#define DO_TEST_FULL(name, cmd, doc, expect, pass) \
do { \
struct testInfo info = { name, doc, expect, pass }; \
if (virTestRun(name, testJSON ## cmd, &info) < 0) \
ret = -1; \
} while (0)
/**
* DO_TEST_PARSE:
* @name: test name
* @doc: source JSON string
* @expect: expected output JSON formatted from parsed @doc
*
* Parses @doc and formats it back. If @expect is NULL the result has to be
* identical to @doc.
*/
#define DO_TEST_PARSE(name, doc, expect) \
DO_TEST_FULL(name, FromString, doc, expect, true)
#define DO_TEST_PARSE_FAIL(name, doc) \
DO_TEST_FULL(name, FromString, doc, NULL, false)
#define DO_TEST_PARSE_FILE(name) \
DO_TEST_FULL(name, FromFile, NULL, NULL, true)
DO_TEST_PARSE_FILE("Simple");
DO_TEST_PARSE_FILE("NotSoSimple");
DO_TEST_PARSE_FILE("Harder");
DO_TEST_PARSE_FILE("VeryHard");
DO_TEST_FULL("success", AddRemove, NULL, NULL, true);
DO_TEST_FULL("failure", AddRemove, NULL, NULL, false);
DO_TEST_FULL("copy and free", Copy,
"{\"return\": [{\"name\": \"quit\"}, {\"name\": \"eject\"},"
"{\"name\": \"change\"}, {\"name\": \"screendump\"},"
"{\"name\": \"stop\"}, {\"name\": \"cont\"}, {\"name\": "
"\"system_reset\"}, {\"name\": \"system_powerdown\"}, "
"{\"name\": \"device_add\"}, {\"name\": \"device_del\"}, "
"{\"name\": \"cpu\"}, {\"name\": \"memsave\"}, {\"name\": "
"\"pmemsave\"}, {\"name\": \"migrate\"}, {\"name\": "
"\"migrate_cancel\"}, {\"name\": \"migrate_set_speed\"},"
"{\"name\": \"client_migrate_info\"}, {\"name\": "
"\"migrate_set_downtime\"}, {\"name\": \"netdev_add\"}, "
"{\"name\": \"netdev_del\"}, {\"name\": \"block_resize\"},"
"{\"name\": \"balloon\"}, {\"name\": \"set_link\"}, {\"name\":"
"\"getfd\"}, {\"name\": \"closefd\"}, {\"name\": \"block_passwd\"},"
"{\"name\": \"set_password\"}, {\"name\": \"expire_password\"},"
"{\"name\": \"qmp_capabilities\"}, {\"name\": "
"\"human-monitor-command\"}, {\"name\": \"query-version\"},"
"{\"name\": \"query-commands\"}, {\"name\": \"query-chardev\"},"
"{\"name\": \"query-block\"}, {\"name\": \"query-blockstats\"}, "
"{\"name\": \"query-cpus\"}, {\"name\": \"query-pci\"}, {\"name\":"
"\"query-kvm\"}, {\"name\": \"query-status\"}, {\"name\": "
"\"query-mice\"}, {\"name\": \"query-vnc\"}, {\"name\": "
"\"query-spice\"}, {\"name\": \"query-name\"}, {\"name\": "
"\"query-uuid\"}, {\"name\": \"query-migrate\"}, {\"name\": "
"\"query-balloon\"}], \"id\": \"libvirt-2\"}", NULL, true);
DO_TEST_PARSE("almost nothing", "[]", NULL);
DO_TEST_PARSE_FAIL("nothing", "");
DO_TEST_PARSE("number without garbage", "[ 234545 ]", "[234545]");
DO_TEST_PARSE_FAIL("number with garbage", "[ 2345b45 ]");
DO_TEST_PARSE("float without garbage", "[ 1.024e19 ]", "[1.024e19]");
DO_TEST_PARSE_FAIL("float with garbage", "[ 0.0314159ee+100 ]");
DO_TEST_PARSE("unsigned minus one", "[ 18446744073709551615 ]", "[18446744073709551615]");
DO_TEST_PARSE("another big number", "[ 9223372036854775808 ]", "[9223372036854775808]");
DO_TEST_PARSE("string", "[ \"The meaning of life\" ]",
"[\"The meaning of life\"]");
DO_TEST_PARSE_FAIL("unterminated string", "[ \"The meaning of lif ]");
DO_TEST_PARSE("integer", "1", NULL);
DO_TEST_PARSE("boolean", "true", NULL);
DO_TEST_PARSE("null", "null", NULL);
DO_TEST_PARSE("escaping symbols", "[\"\\\"\\t\\n\\\\\"]", NULL);
DO_TEST_PARSE("escaped strings", "[\"{\\\"blurb\\\":\\\"test\\\"}\"]", NULL);
DO_TEST_PARSE_FAIL("incomplete keyword", "tr");
DO_TEST_PARSE_FAIL("overdone keyword", "[ truest ]");
DO_TEST_PARSE_FAIL("unknown keyword", "huh");
DO_TEST_PARSE_FAIL("comments", "[ /* nope */\n1 // not this either\n]");
DO_TEST_PARSE_FAIL("trailing garbage", "[] []");
DO_TEST_PARSE_FAIL("list without array", "1, 1");
DO_TEST_PARSE_FAIL("parser abuse", "1] [2");
DO_TEST_PARSE_FAIL("invalid UTF-8", "\"\x80\"");
DO_TEST_PARSE_FAIL("object with numeric keys", "{ 1:1, 2:1, 3:2 }");
DO_TEST_PARSE_FAIL("unterminated object", "{ \"1\":1, \"2\":1, \"3\":2");
DO_TEST_PARSE_FAIL("unterminated array of objects",
"[ {\"name\": \"John\"}, {\"name\": \"Paul\"}, ");
DO_TEST_PARSE_FAIL("array of an object with an array as a key",
"[ {[\"key1\", \"key2\"]: \"value\"} ]");
DO_TEST_PARSE_FAIL("object with unterminated key", "{ \"key:7 }");
DO_TEST_PARSE_FAIL("duplicate key", "{ \"a\": 1, \"a\": 1 }");
DO_TEST_FULL("lookup on array", Lookup,
"[ 1 ]", NULL, false);
DO_TEST_FULL("lookup on string", Lookup,
"\"str\"", NULL, false);
DO_TEST_FULL("lookup on integer", Lookup,
"1", NULL, false);
DO_TEST_FULL("lookup with missing key", Lookup,
"{ }", NULL, false);
DO_TEST_FULL("lookup with wrong type", Lookup,
"{ \"a\": 1, \"b\": \"str\", \"c\": [], \"d\": {} }",
NULL, false);
DO_TEST_FULL("lookup with correct type", Lookup,
"{ \"a\": {}, \"b\": 1, \"c\": \"str\", \"d\": [] }",
NULL, true);
DO_TEST_FULL("create object with nested json in attribute", EscapeObj,
NULL, NULL, true);
DO_TEST_FULL("stealing of attributes while creating objects",
ObjectFormatSteal, NULL, NULL, true);
#define DO_TEST_DEFLATTEN(name, pass) \
DO_TEST_FULL(name, Deflatten, NULL, NULL, pass)
DO_TEST_DEFLATTEN("unflattened", true);
DO_TEST_DEFLATTEN("basic-file", true);
DO_TEST_DEFLATTEN("basic-generic", true);
DO_TEST_DEFLATTEN("deep-file", true);
DO_TEST_DEFLATTEN("deep-generic", true);
DO_TEST_DEFLATTEN("nested", true);
DO_TEST_DEFLATTEN("double-key", false);
DO_TEST_DEFLATTEN("concat", true);
DO_TEST_DEFLATTEN("concat-double-key", false);
DO_TEST_DEFLATTEN("qemu-sheepdog", true);
return (ret == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}
VIR_TEST_MAIN(mymain)