static void test_numbers(TestBatchRunner *runner) { Integer *i64 = Int_new(33); String *json = Json_to_json((Obj*)i64); String *trimmed = Str_Trim(json); TEST_TRUE(runner, Str_Equals_Utf8(trimmed, "33", 2), "Integer"); DECREF(json); DECREF(trimmed); Float *f64 = Float_new(33.33); json = Json_to_json((Obj*)f64); if (json) { double value = Str_To_F64(json); double diff = 33.33 - value; if (diff < 0.0) { diff = 0.0 - diff; } TEST_TRUE(runner, diff < 0.0001, "Float"); DECREF(json); } else { FAIL(runner, "Float conversion to json failed."); } DECREF(i64); DECREF(f64); }
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); const 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++) { const char *source = quote_escapes_source[i]; const 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_escapes(TestBatchRunner *runner) { for (int i = 0; control_escapes[i] != NULL; i++) { String *string = Str_new_from_char(i); const char *escaped = control_escapes[i]; String *json = Json_to_json((Obj*)string); String *trimmed = Str_Trim(json); String *decoded = (String*)Json_from_json(json); String *json_wanted = Str_newf("\"%s\"", escaped); TEST_TRUE(runner, Str_Equals(json_wanted, (Obj*)trimmed), "encode control escape: %s", escaped); TEST_TRUE(runner, decoded != NULL && Str_Equals(string, (Obj*)decoded), "decode control escape: %s", escaped); DECREF(string); DECREF(json); DECREF(trimmed); DECREF(decoded); DECREF(json_wanted); } for (int i = 0; quote_escapes_source[i] != NULL; i++) { const char *source = quote_escapes_source[i]; const char *escaped = quote_escapes_json[i]; String *string = Str_new_from_utf8(source, strlen(source)); String *json = Json_to_json((Obj*)string); String *trimmed = Str_Trim(json); String *decoded = (String*)Json_from_json(json); String *json_wanted = Str_newf("\"%s\"", escaped); TEST_TRUE(runner, Str_Equals(json_wanted, (Obj*)trimmed), "encode quote/backslash escapes: %s", source); TEST_TRUE(runner, decoded != NULL && Str_Equals(string, (Obj*)decoded), "decode quote/backslash escapes: %s", source); DECREF(string); DECREF(json); DECREF(trimmed); DECREF(decoded); DECREF(json_wanted); } }
static void test_tolerance(TestBatchRunner *runner) { String *foo = Str_newf("foo"); String *not_json = Json_to_json((Obj*)foo); TEST_TRUE(runner, not_json == NULL, "to_json returns NULL when fed invalid data type"); TEST_TRUE(runner, Err_get_error() != NULL, "to_json sets global error when fed invalid data type"); DECREF(foo); }
static void test_tolerance(TestBatch *batch) { CharBuf *foo = CB_newf("foo"); CharBuf *not_json = Json_to_json((Obj*)foo); TEST_TRUE(batch, not_json == NULL, "to_json returns NULL when fed invalid data type"); TEST_TRUE(batch, Err_get_error() != NULL, "to_json sets Err_error when fed invalid data type"); DECREF(foo); }
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_to_and_from(TestBatchRunner *runner) { Obj *dump = S_make_dump(); String *json = Json_to_json(dump); Obj *got = Json_from_json(json); TEST_TRUE(runner, got != NULL && Obj_Equals(dump, got), "Round trip through to_json and from_json"); DECREF(dump); DECREF(json); DECREF(got); }
static void test_max_depth(TestBatch *batch) { Hash *circular = Hash_new(0); Hash_Store_Str(circular, "circular", 8, INCREF(circular)); Err_set_error(NULL); CharBuf *not_json = Json_to_json((Obj*)circular); TEST_TRUE(batch, not_json == NULL, "to_json returns NULL when fed recursing data"); TEST_TRUE(batch, Err_get_error() != NULL, "to_json sets Err_error when fed recursing data"); DECREF(Hash_Delete_Str(circular, "circular", 8)); DECREF(circular); }
static void S_round_trip_integer(TestBatch *batch, int64_t value) { Integer64 *num = Int64_new(value); VArray *array = VA_new(1); VA_Store(array, 0, (Obj*)num); CharBuf *json = Json_to_json((Obj*)array); Obj *dump = Json_from_json(json); TEST_TRUE(batch, VA_Equals(array, dump), "Round trip integer %ld", (long)value); DECREF(dump); DECREF(json); DECREF(array); }
static void test_max_depth(TestBatchRunner *runner) { Hash *circular = Hash_new(0); Hash_Store_Utf8(circular, "circular", 8, INCREF(circular)); Err_set_error(NULL); String *not_json = Json_to_json((Obj*)circular); TEST_TRUE(runner, not_json == NULL, "to_json returns NULL when fed recursing data"); TEST_TRUE(runner, Err_get_error() != NULL, "to_json sets global error when fed recursing data"); DECREF(Hash_Delete_Utf8(circular, "circular", 8)); DECREF(circular); }
static void S_round_trip_integer(TestBatchRunner *runner, int64_t value) { Integer *num = Int_new(value); Vector *array = Vec_new(1); Vec_Store(array, 0, (Obj*)num); String *json = Json_to_json((Obj*)array); Obj *dump = Json_from_json(json); TEST_TRUE(runner, Vec_Equals(array, dump), "Round trip integer %ld", (long)value); DECREF(dump); DECREF(json); DECREF(array); }
static void test_illegal_keys(TestBatch *batch) { Hash *hash = Hash_new(0); Float64 *key = Float64_new(1.1); Hash_Store(hash, (Obj*)key, (Obj*)CB_newf("blah")); Err_set_error(NULL); CharBuf *not_json = Json_to_json((Obj*)hash); TEST_TRUE(batch, not_json == NULL, "to_json returns NULL when fed an illegal key"); TEST_TRUE(batch, Err_get_error() != NULL, "to_json sets Err_error when fed an illegal key"); DECREF(key); DECREF(hash); }
static void S_round_trip_float(TestBatch *batch, double value, double max_diff) { Float64 *num = Float64_new(value); VArray *array = VA_new(1); VA_Store(array, 0, (Obj*)num); CharBuf *json = Json_to_json((Obj*)array); Obj *dump = CERTIFY(Json_from_json(json), VARRAY); Float64 *got = (Float64*)CERTIFY(VA_Fetch((VArray*)dump, 0), FLOAT64); double diff = Float64_Get_Value(num) - Float64_Get_Value(got); if (diff < 0) { diff = 0 - diff; } TEST_TRUE(batch, diff <= max_diff, "Round trip float %f", value); DECREF(dump); DECREF(json); DECREF(array); }
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 S_round_trip_float(TestBatchRunner *runner, double value, double max_diff) { Float *num = Float_new(value); Vector *array = Vec_new(1); Vec_Store(array, 0, (Obj*)num); String *json = Json_to_json((Obj*)array); Obj *dump = CERTIFY(Json_from_json(json), VECTOR); Float *got = (Float*)CERTIFY(Vec_Fetch((Vector*)dump, 0), FLOAT); double diff = Float_Get_Value(num) - Float_Get_Value(got); if (diff < 0) { diff = 0 - diff; } TEST_TRUE(runner, diff <= max_diff, "Round trip float %f", value); DECREF(dump); DECREF(json); DECREF(array); }
bool_t Json_spew_json(Obj *dump, Folder *folder, const CharBuf *path) { CharBuf *json = Json_to_json(dump); if (!json) { ERR_ADD_FRAME(Err_get_error()); return false; } OutStream *outstream = Folder_Open_Out(folder, path); if (!outstream) { ERR_ADD_FRAME(Err_get_error()); DECREF(json); return false; } size_t size = CB_Get_Size(json); OutStream_Write_Bytes(outstream, CB_Get_Ptr8(json), size); OutStream_Close(outstream); DECREF(outstream); DECREF(json); return true; }
bool LFLock_Request_IMP(LockFileLock *self) { LockFileLockIVARS *const ivars = LFLock_IVARS(self); bool success = false; if (Folder_Exists(ivars->folder, ivars->lock_path)) { Err_set_error((Err*)LockErr_new(Str_newf("Can't obtain lock: '%o' exists", ivars->lock_path))); return false; } // Create the "locks" subdirectory if necessary. String *lock_dir_name = (String*)SSTR_WRAP_UTF8("locks", 5); if (!Folder_Exists(ivars->folder, lock_dir_name)) { if (!Folder_MkDir(ivars->folder, lock_dir_name)) { Err *mkdir_err = (Err*)CERTIFY(Err_get_error(), ERR); LockErr *err = LockErr_new(Str_newf("Can't create 'locks' directory: %o", Err_Get_Mess(mkdir_err))); // Maybe our attempt failed because another process succeeded. if (Folder_Find_Folder(ivars->folder, lock_dir_name)) { DECREF(err); } else { // Nope, everything failed, so bail out. Err_set_error((Err*)err); return false; } } } // Prepare to write pid, lock name, and host to the lock file as JSON. Hash *file_data = Hash_new(3); Hash_Store_Utf8(file_data, "pid", 3, (Obj*)Str_newf("%i32", (int32_t)PID_getpid())); Hash_Store_Utf8(file_data, "host", 4, INCREF(ivars->host)); Hash_Store_Utf8(file_data, "name", 4, INCREF(ivars->name)); String *json = Json_to_json((Obj*)file_data); DECREF(file_data); // Write to a temporary file, then use the creation of a hard link to // ensure atomic but non-destructive creation of the lockfile with its // complete contents. OutStream *outstream = Folder_Open_Out(ivars->folder, ivars->link_path); if (!outstream) { ERR_ADD_FRAME(Err_get_error()); DECREF(json); return false; } struct lockfile_context context; context.outstream = outstream; context.json = json; Err *json_error = Err_trap(S_write_lockfile_json, &context); bool wrote_json = !json_error; DECREF(outstream); DECREF(json); if (wrote_json) { success = Folder_Hard_Link(ivars->folder, ivars->link_path, ivars->lock_path); if (!success) { Err *hard_link_err = (Err*)CERTIFY(Err_get_error(), ERR); Err_set_error((Err*)LockErr_new(Str_newf("Failed to obtain lock at '%o': %o", ivars->lock_path, Err_Get_Mess(hard_link_err)))); } } else { Err_set_error((Err*)LockErr_new(Str_newf("Failed to obtain lock at '%o': %o", ivars->lock_path, Err_Get_Mess(json_error)))); DECREF(json_error); } // Verify that our temporary file got zapped. bool deletion_failed = !Folder_Delete(ivars->folder, ivars->link_path); if (deletion_failed) { String *mess = MAKE_MESS("Failed to delete '%o'", ivars->link_path); Err_throw_mess(ERR, mess); } return success; }