blob: 2a47d3af64a0e911e03c180b03f087c61d70cc28 [file] [log] [blame]
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "Lucy/Util/ToolSet.h"
#include "Lucy/Test.h"
#include "Lucy/Test/Util/TestJson.h"
#include "Lucy/Util/Json.h"
#include "Lucy/Store/FileHandle.h"
#include "Lucy/Store/RAMFolder.h"
// Create a test data structure including at least one each of Hash, VArray,
// and CharBuf.
static Obj*
S_make_dump() {
Hash *dump = Hash_new(0);
Hash_Store_Str(dump, "foo", 3, (Obj*)CB_newf("foo"));
Hash_Store_Str(dump, "stuff", 5, (Obj*)VA_new(0));
return (Obj*)dump;
}
// Test escapes for control characters ASCII 0-31.
static char* control_escapes[] = {
"\\u0000",
"\\u0001",
"\\u0002",
"\\u0003",
"\\u0004",
"\\u0005",
"\\u0006",
"\\u0007",
"\\b",
"\\t",
"\\n",
"\\u000b",
"\\f",
"\\r",
"\\u000e",
"\\u000f",
"\\u0010",
"\\u0011",
"\\u0012",
"\\u0013",
"\\u0014",
"\\u0015",
"\\u0016",
"\\u0017",
"\\u0018",
"\\u0019",
"\\u001a",
"\\u001b",
"\\u001c",
"\\u001d",
"\\u001e",
"\\u001f",
NULL
};
// Test quote and backslash escape in isolation, then in context.
static char* quote_escapes_source[] = {
"\"",
"\\",
"abc\"",
"abc\\",
"\"xyz",
"\\xyz",
"\\\"",
"\"\\",
NULL
};
static char* quote_escapes_json[] = {
"\\\"",
"\\\\",
"abc\\\"",
"abc\\\\",
"\\\"xyz",
"\\\\xyz",
"\\\\\\\"",
"\\\"\\\\",
NULL
};
static void
test_escapes(TestBatch *batch) {
CharBuf *string = CB_new(10);
CharBuf *json_wanted = CB_new(10);
for (int i = 0; control_escapes[i] != NULL; i++) {
CB_Truncate(string, 0);
CB_Cat_Char(string, i);
char *escaped = control_escapes[i];
CharBuf *json = Json_to_json((Obj*)string);
CharBuf *decoded = (CharBuf*)Json_from_json(json);
CB_setf(json_wanted, "\"%s\"", escaped);
CB_Trim(json);
TEST_TRUE(batch, json != NULL && CB_Equals(json_wanted, (Obj*)json),
"encode control escape: %s", escaped);
TEST_TRUE(batch, decoded != NULL && CB_Equals(string, (Obj*)decoded),
"decode control escape: %s", escaped);
DECREF(json);
DECREF(decoded);
}
for (int i = 0; quote_escapes_source[i] != NULL; i++) {
char *source = quote_escapes_source[i];
char *escaped = quote_escapes_json[i];
CB_setf(string, source, strlen(source));
CharBuf *json = Json_to_json((Obj*)string);
CharBuf *decoded = (CharBuf*)Json_from_json(json);
CB_setf(json_wanted, "\"%s\"", escaped);
CB_Trim(json);
TEST_TRUE(batch, json != NULL && CB_Equals(json_wanted, (Obj*)json),
"encode quote/backslash escapes: %s", source);
TEST_TRUE(batch, decoded != NULL && CB_Equals(string, (Obj*)decoded),
"decode quote/backslash escapes: %s", source);
DECREF(json);
DECREF(decoded);
}
DECREF(json_wanted);
DECREF(string);
}
static void
test_numbers(TestBatch *batch) {
Integer64 *i64 = Int64_new(33);
CharBuf *json = Json_to_json((Obj*)i64);
CB_Trim(json);
TEST_TRUE(batch, json && CB_Equals_Str(json, "33", 2), "Integer");
DECREF(json);
Float64 *f64 = Float64_new(33.33);
json = Json_to_json((Obj*)f64);
if (json) {
double value = CB_To_F64(json);
double diff = 33.33 - value;
if (diff < 0.0) { diff = 0.0 - diff; }
TEST_TRUE(batch, diff < 0.0001, "Float");
DECREF(json);
}
else {
FAIL(batch, "Float conversion to json failed.");
}
DECREF(i64);
DECREF(f64);
}
static void
test_to_and_from(TestBatch *batch) {
Obj *dump = S_make_dump();
CharBuf *json = Json_to_json(dump);
Obj *got = Json_from_json(json);
TEST_TRUE(batch, got != NULL && Obj_Equals(dump, got),
"Round trip through to_json and from_json");
DECREF(dump);
DECREF(json);
DECREF(got);
}
static void
test_spew_and_slurp(TestBatch *batch) {
Obj *dump = S_make_dump();
Folder *folder = (Folder*)RAMFolder_new(NULL);
CharBuf *foo = (CharBuf*)ZCB_WRAP_STR("foo", 3);
bool_t result = Json_spew_json(dump, folder, foo);
TEST_TRUE(batch, result, "spew_json returns true on success");
TEST_TRUE(batch, Folder_Exists(folder, foo),
"spew_json wrote file");
Obj *got = Json_slurp_json(folder, foo);
TEST_TRUE(batch, got && Obj_Equals(dump, got),
"Round trip through spew_json and slurp_json");
DECREF(got);
Err_set_error(NULL);
result = Json_spew_json(dump, folder, foo);
TEST_FALSE(batch, result, "Can't spew_json when file exists");
TEST_TRUE(batch, Err_get_error() != NULL,
"Failed spew_json sets Err_error");
Err_set_error(NULL);
CharBuf *bar = (CharBuf*)ZCB_WRAP_STR("bar", 3);
got = Json_slurp_json(folder, bar);
TEST_TRUE(batch, got == NULL,
"slurp_json returns NULL when file doesn't exist");
TEST_TRUE(batch, Err_get_error() != NULL,
"Failed slurp_json sets Err_error");
CharBuf *boffo = (CharBuf*)ZCB_WRAP_STR("boffo", 5);
{
FileHandle *fh
= Folder_Open_FileHandle(folder, boffo, FH_CREATE | FH_WRITE_ONLY);
FH_Write(fh, "garbage", 7);
DECREF(fh);
}
Err_set_error(NULL);
got = Json_slurp_json(folder, boffo);
TEST_TRUE(batch, got == NULL,
"slurp_json returns NULL when file doesn't contain valid JSON");
TEST_TRUE(batch, Err_get_error() != NULL,
"Failed slurp_json sets Err_error");
DECREF(got);
DECREF(dump);
DECREF(folder);
}
void
TestJson_run_tests() {
TestBatch *batch = TestBatch_new(92);
// Liberalize for testing.
Json_set_tolerant(true);
TestBatch_Plan(batch);
test_to_and_from(batch);
test_escapes(batch);
test_numbers(batch);
test_spew_and_slurp(batch);
DECREF(batch);
}