]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/test/test-json.c
license: LGPL-2.1+ -> LGPL-2.1-or-later
[thirdparty/systemd.git] / src / test / test-json.c
index fdf1b4f40c09c9476b79b1f4956f72af275fee0c..1d4b11945ec4c39de87150b8dd29b34e049b7f69 100644 (file)
@@ -1,13 +1,16 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 
 #include <math.h>
 
 #include "alloc-util.h"
+#include "escape.h"
 #include "fd-util.h"
+#include "fileio.h"
 #include "json-internal.h"
 #include "json.h"
 #include "string-util.h"
 #include "strv.h"
+#include "tests.h"
 #include "util.h"
 
 static void test_tokenizer(const char *data, ...) {
@@ -15,6 +18,10 @@ static void test_tokenizer(const char *data, ...) {
         void *state = NULL;
         va_list ap;
 
+        _cleanup_free_ char *cdata;
+        assert_se(cdata = cescape(data));
+        log_info("/* %s data=\"%s\" */", __func__, cdata);
+
         va_start(ap, data);
 
         for (;;) {
@@ -80,17 +87,22 @@ static void test_variant(const char *data, Test test) {
         _cleanup_free_ char *s = NULL;
         int r;
 
-        r = json_parse(data, &v, NULL, NULL);
+        _cleanup_free_ char *cdata;
+        assert_se(cdata = cescape(data));
+        log_info("/* %s data=\"%s\" */", __func__, cdata);
+
+        r = json_parse(data, 0, &v, NULL, NULL);
         assert_se(r == 0);
         assert_se(v);
 
         r = json_variant_format(v, 0, &s);
         assert_se(r >= 0);
         assert_se(s);
+        assert_se((size_t) r == strlen(s));
 
         log_info("formatted normally: %s\n", s);
 
-        r = json_parse(data, &w, NULL, NULL);
+        r = json_parse(data, JSON_PARSE_SENSITIVE, &w, NULL, NULL);
         assert_se(r == 0);
         assert_se(w);
         assert_se(json_variant_has_type(v, json_variant_type(w)));
@@ -103,10 +115,11 @@ static void test_variant(const char *data, Test test) {
         r = json_variant_format(v, JSON_FORMAT_PRETTY, &s);
         assert_se(r >= 0);
         assert_se(s);
+        assert_se((size_t) r == strlen(s));
 
         log_info("formatted prettily:\n%s", s);
 
-        r = json_parse(data, &w, NULL, NULL);
+        r = json_parse(data, 0, &w, NULL, NULL);
         assert_se(r == 0);
         assert_se(w);
 
@@ -118,12 +131,14 @@ static void test_variant(const char *data, Test test) {
         r = json_variant_format(v, JSON_FORMAT_COLOR, &s);
         assert_se(r >= 0);
         assert_se(s);
+        assert_se((size_t) r == strlen(s));
         printf("Normal with color: %s\n", s);
 
         s = mfree(s);
         r = json_variant_format(v, JSON_FORMAT_COLOR|JSON_FORMAT_PRETTY, &s);
         assert_se(r >= 0);
         assert_se(s);
+        assert_se((size_t) r == strlen(s));
         printf("Pretty with color:\n%s\n", s);
 
         if (test)
@@ -134,6 +149,8 @@ static void test_1(JsonVariant *v) {
         JsonVariant *p, *q;
         unsigned i;
 
+        log_info("/* %s */", __func__);
+
         /* 3 keys + 3 values */
         assert_se(json_variant_elements(v) == 6);
 
@@ -167,6 +184,8 @@ static void test_1(JsonVariant *v) {
 static void test_2(JsonVariant *v) {
         JsonVariant *p, *q;
 
+        log_info("/* %s */", __func__);
+
         /* 2 keys + 2 values */
         assert_se(json_variant_elements(v) == 4);
 
@@ -210,13 +229,12 @@ static void test_2(JsonVariant *v) {
 }
 
 static void test_zeroes(JsonVariant *v) {
-        size_t i;
-
         /* Make sure zero is how we expect it. */
+        log_info("/* %s */", __func__);
 
         assert_se(json_variant_elements(v) == 13);
 
-        for (i = 0; i < json_variant_elements(v); i++) {
+        for (size_t i = 0; i < json_variant_elements(v); i++) {
                 JsonVariant *w;
                 size_t j;
 
@@ -225,10 +243,9 @@ static void test_zeroes(JsonVariant *v) {
                 assert_se(json_variant_integer(w) == 0);
                 assert_se(json_variant_unsigned(w) == 0U);
 
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
+                DISABLE_WARNING_FLOAT_EQUAL;
                 assert_se(json_variant_real(w) == 0.0L);
-#pragma GCC diagnostic pop
+                REENABLE_WARNING;
 
                 assert_se(json_variant_is_integer(w));
                 assert_se(json_variant_is_unsigned(w));
@@ -250,6 +267,8 @@ static void test_zeroes(JsonVariant *v) {
 }
 
 static void test_build(void) {
+        log_info("/* %s */", __func__);
+
         _cleanup_(json_variant_unrefp) JsonVariant *a = NULL, *b = NULL;
         _cleanup_free_ char *s = NULL, *t = NULL;
 
@@ -296,7 +315,7 @@ static void test_build(void) {
 
         assert_se(json_variant_format(a, 0, &s) >= 0);
         log_info("GOT: %s\n", s);
-        assert_se(json_parse(s, &b, NULL, NULL) >= 0);
+        assert_se(json_parse(s, 0, &b, NULL, NULL) >= 0);
         assert_se(json_variant_equal(a, b));
 
         a = json_variant_unref(a);
@@ -307,7 +326,7 @@ static void test_build(void) {
         s = mfree(s);
         assert_se(json_variant_format(a, 0, &s) >= 0);
         log_info("GOT: %s\n", s);
-        assert_se(json_parse(s, &b, NULL, NULL) >= 0);
+        assert_se(json_parse(s, 0, &b, NULL, NULL) >= 0);
         assert_se(json_variant_format(b, 0, &t) >= 0);
         log_info("GOT: %s\n", t);
 
@@ -350,6 +369,8 @@ static void test_source(void) {
                 "false, 7.5, {} ]\n"
                 "}\n";
 
+        log_info("/* %s */", __func__);
+
         _cleanup_fclose_ FILE *f = NULL;
         _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
 
@@ -357,9 +378,9 @@ static void test_source(void) {
                "%s"
                "--- original end ---\n", data);
 
-        assert_se(f = fmemopen((void*) data, strlen(data), "r"));
+        assert_se(f = fmemopen_unlocked((void*) data, strlen(data), "r"));
 
-        assert_se(json_parse_file(f, "waldo", &v, NULL, NULL) >= 0);
+        assert_se(json_parse_file(f, "waldo", 0, &v, NULL, NULL) >= 0);
 
         printf("--- non-pretty begin ---\n");
         json_variant_dump(v, 0, stdout, NULL);
@@ -371,15 +392,16 @@ static void test_source(void) {
 }
 
 static void test_depth(void) {
+        log_info("/* %s */", __func__);
+
         _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
-        unsigned i;
         int r;
 
         v = JSON_VARIANT_STRING_CONST("start");
 
         /* Let's verify that the maximum depth checks work */
 
-        for (i = 0;; i++) {
+        for (unsigned i = 0;; i++) {
                 _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
 
                 assert_se(i <= UINT16_MAX);
@@ -391,6 +413,13 @@ static void test_depth(void) {
                         log_info("max depth at %u", i);
                         break;
                 }
+#if HAS_FEATURE_MEMORY_SANITIZER
+                /* msan doesn't like the stack nesting to be too deep. Let's quit early. */
+                if (i >= 128) {
+                        log_info("quitting early at depth %u", i);
+                        break;
+                }
+#endif
 
                 assert_se(r >= 0);
 
@@ -402,11 +431,98 @@ static void test_depth(void) {
         fputs("\n", stdout);
 }
 
-int main(int argc, char *argv[]) {
+static void test_normalize(void) {
+        log_info("/* %s */", __func__);
+
+        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL;
+        _cleanup_free_ char *t = NULL;
+
+        assert_se(json_build(&v, JSON_BUILD_OBJECT(
+                                             JSON_BUILD_PAIR("b", JSON_BUILD_STRING("x")),
+                                             JSON_BUILD_PAIR("c", JSON_BUILD_STRING("y")),
+                                             JSON_BUILD_PAIR("a", JSON_BUILD_STRING("z")))) >= 0);
+
+        assert_se(!json_variant_is_sorted(v));
+        assert_se(!json_variant_is_normalized(v));
+
+        assert_se(json_variant_format(v, 0, &t) >= 0);
+        assert_se(streq(t, "{\"b\":\"x\",\"c\":\"y\",\"a\":\"z\"}"));
+        t = mfree(t);
+
+        assert_se(json_build(&w, JSON_BUILD_OBJECT(
+                                             JSON_BUILD_PAIR("bar", JSON_BUILD_STRING("zzz")),
+                                             JSON_BUILD_PAIR("foo", JSON_BUILD_VARIANT(v)))) >= 0);
+
+        assert_se(json_variant_is_sorted(w));
+        assert_se(!json_variant_is_normalized(w));
+
+        assert_se(json_variant_format(w, 0, &t) >= 0);
+        assert_se(streq(t, "{\"bar\":\"zzz\",\"foo\":{\"b\":\"x\",\"c\":\"y\",\"a\":\"z\"}}"));
+        t = mfree(t);
+
+        assert_se(json_variant_sort(&v) >= 0);
+        assert_se(json_variant_is_sorted(v));
+        assert_se(json_variant_is_normalized(v));
+
+        assert_se(json_variant_format(v, 0, &t) >= 0);
+        assert_se(streq(t, "{\"a\":\"z\",\"b\":\"x\",\"c\":\"y\"}"));
+        t = mfree(t);
+
+        assert_se(json_variant_normalize(&w) >= 0);
+        assert_se(json_variant_is_sorted(w));
+        assert_se(json_variant_is_normalized(w));
+
+        assert_se(json_variant_format(w, 0, &t) >= 0);
+        assert_se(streq(t, "{\"bar\":\"zzz\",\"foo\":{\"a\":\"z\",\"b\":\"x\",\"c\":\"y\"}}"));
+        t = mfree(t);
+}
+
+static void test_bisect(void) {
+        log_info("/* %s */", __func__);
+
+        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+
+        /* Tests the bisection logic in json_variant_by_key() */
+
+        for (char c = 'z'; c >= 'a'; c--) {
+
+                if ((c % 3) == 0)
+                        continue;
+
+                _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
+                assert_se(json_variant_new_stringn(&w, (char[4]) { '<', c, c, '>' }, 4) >= 0);
+                assert_se(json_variant_set_field(&v, (char[2]) { c, 0 }, w) >= 0);
+        }
+
+        json_variant_dump(v, JSON_FORMAT_COLOR|JSON_FORMAT_PRETTY, NULL, NULL);
+
+        assert_se(!json_variant_is_sorted(v));
+        assert_se(!json_variant_is_normalized(v));
+        assert_se(json_variant_normalize(&v) >= 0);
+        assert_se(json_variant_is_sorted(v));
+        assert_se(json_variant_is_normalized(v));
 
-        log_set_max_level(LOG_DEBUG);
-        log_parse_environment();
-        log_open();
+        json_variant_dump(v, JSON_FORMAT_COLOR|JSON_FORMAT_PRETTY, NULL, NULL);
+
+        for (char c = 'a'; c <= 'z'; c++) {
+                JsonVariant *k;
+                const char *z;
+
+                k = json_variant_by_key(v, (char[2]) { c, 0 });
+                assert_se(!k == ((c % 3) == 0));
+
+                if (!k)
+                        continue;
+
+                assert_se(json_variant_is_string(k));
+
+                z = (char[5]){ '<', c, c, '>', 0};
+                assert_se(streq(json_variant_string(k), z));
+        }
+}
+
+int main(int argc, char *argv[]) {
+        test_setup_logging(LOG_DEBUG);
 
         test_tokenizer("x", -EINVAL);
         test_tokenizer("", JSON_TOKEN_END);
@@ -447,15 +563,16 @@ int main(int argc, char *argv[]) {
 
         test_variant("{\"k\": \"v\", \"foo\": [1, 2, 3], \"bar\": {\"zap\": null}}", test_1);
         test_variant("{\"mutant\": [1, null, \"1\", {\"1\": [1, \"1\"]}], \"thisisaverylongproperty\": 1.27}", test_2);
-        test_variant("{\"foo\" : \"\\uDBFF\\uDFFF\\\"\\uD9FF\\uDFFFFFF\\\"\\uDBFF\\uDFFF\\\"\\uD9FF\\uDFFF\\uDBFF\\uDFFFF\\uDBFF\\uDFFF\\uDBFF\\uDFFF\\uDBFF\\uDFFF\\uDBFF\\uDFFF\\\"\\uD9FF\\uDFFFFF\\\"\\uDBFF\\uDFFF\\\"\\uD9FF\\uDFFF\\uDBFF\\uDFFF\"}", NULL);
+        test_variant("{\"foo\" : \"\\u0935\\u093f\\u0935\\u0947\\u0915\\u0916\\u094d\\u092f\\u093e\\u0924\\u093f\\u0930\\u0935\\u093f\\u092a\\u094d\\u0932\\u0935\\u093e\\u0020\\u0939\\u093e\\u0928\\u094b\\u092a\\u093e\\u092f\\u0903\\u0964\"}", NULL);
 
         test_variant("[ 0, -0, 0.0, -0.0, 0.000, -0.000, 0e0, -0e0, 0e+0, -0e-0, 0e-0, -0e000, 0e+000 ]", test_zeroes);
 
         test_build();
-
         test_source();
-
         test_depth();
 
+        test_normalize();
+        test_bisect();
+
         return 0;
 }