test(json): pin s64 MIN/MAX writer bytes; move scratch to .sx-tmp
Close the coverage gap from attempt 1: example 0713 now builds integer fields holding s64 MIN (-9223372036854775808) and s64 MAX (9223372036854775807) — plus zero, a small negative, and a small positive — and asserts the EXACT emitted bytes. This permanently pins the edge that write_int is specifically engineered for (folding positives into negative space so MIN's non-representable-positive magnitude serializes correctly). s64 MIN is expressed as (0 - 9223372036854775807 - 1) because its magnitude is not a representable positive s64 literal. Test hygiene: stream to a repo-local, gitignored .sx-tmp/ path (created if missing) instead of a fixed /tmp name, and unlink it right after read-back so nothing leaks. Writer/model logic and src/ are untouched.
This commit is contained in:
@@ -2,8 +2,10 @@
|
|||||||
//
|
//
|
||||||
// Builds a representative value — a nested object holding a string with
|
// Builds a representative value — a nested object holding a string with
|
||||||
// every escape kind (quote, newline, tab, backslash, a raw control byte),
|
// every escape kind (quote, newline, tab, backslash, a raw control byte),
|
||||||
// a negative integer, a bool, null, an array, and a nested object — then
|
// integers spanning zero / a small negative / a small positive / s64 MIN
|
||||||
// serializes it two ways and asserts the EXACT bytes:
|
// (-9223372036854775808) / s64 MAX (9223372036854775807), a bool, null, an
|
||||||
|
// array, and a nested object — then serializes it two ways and asserts the
|
||||||
|
// EXACT bytes:
|
||||||
//
|
//
|
||||||
// 1. into a caller-owned `[]u8` buffer (returns bytes written),
|
// 1. into a caller-owned `[]u8` buffer (returns bytes written),
|
||||||
// 2. streaming straight to a file through an 8-byte staging buffer
|
// 2. streaming straight to a file through an 8-byte staging buffer
|
||||||
@@ -20,7 +22,7 @@
|
|||||||
#import "modules/fs.sx";
|
#import "modules/fs.sx";
|
||||||
|
|
||||||
// The exact document the writer must produce (insertion order, escaping).
|
// The exact document the writer must produce (insertion order, escaping).
|
||||||
EXPECT :: "{\"name\":\"a\\\"b\\n\",\"tab\":\"x\\ty\",\"bs\":\"c\\\\d\",\"ctrl\":\"\\u0001\",\"n\":-7,\"ok\":true,\"nil\":null,\"xs\":[1,-2,3],\"nested\":{\"k\":\"v\"}}";
|
EXPECT :: "{\"name\":\"a\\\"b\\n\",\"tab\":\"x\\ty\",\"bs\":\"c\\\\d\",\"ctrl\":\"\\u0001\",\"n\":-7,\"zero\":0,\"pos\":7,\"min\":-9223372036854775808,\"max\":9223372036854775807,\"ok\":true,\"nil\":null,\"xs\":[1,-2,3],\"nested\":{\"k\":\"v\"}}";
|
||||||
|
|
||||||
report :: (label: string, ok: bool) {
|
report :: (label: string, ok: bool) {
|
||||||
if ok { print("{}: ok\n", label); } else { print("{}: FAIL\n", label); }
|
if ok { print("{}: ok\n", label); } else { print("{}: FAIL\n", label); }
|
||||||
@@ -48,7 +50,13 @@ build :: (alloc: Allocator) -> Value {
|
|||||||
obj.put("tab", .str("x\ty"), alloc); // tab
|
obj.put("tab", .str("x\ty"), alloc); // tab
|
||||||
obj.put("bs", .str("c\\d"), alloc); // backslash
|
obj.put("bs", .str("c\\d"), alloc); // backslash
|
||||||
obj.put("ctrl", .str(ctrl), alloc); // raw control byte ->
|
obj.put("ctrl", .str(ctrl), alloc); // raw control byte ->
|
||||||
obj.put("n", .int_(0 - 7), alloc); // negative int
|
obj.put("n", .int_(0 - 7), alloc); // small negative int
|
||||||
|
obj.put("zero", .int_(0), alloc); // zero
|
||||||
|
obj.put("pos", .int_(7), alloc); // small positive int
|
||||||
|
// s64 MIN: its magnitude (9223372036854775808) is not a representable
|
||||||
|
// positive s64 literal, so build it from MAX-positive minus one.
|
||||||
|
obj.put("min", .int_(0 - 9223372036854775807 - 1), alloc);
|
||||||
|
obj.put("max", .int_(9223372036854775807), alloc); // s64 MAX
|
||||||
obj.put("ok", .bool_(true), alloc);
|
obj.put("ok", .bool_(true), alloc);
|
||||||
obj.put("nil", .null_, alloc);
|
obj.put("nil", .null_, alloc);
|
||||||
obj.put("xs", .array(xs), alloc);
|
obj.put("xs", .array(xs), alloc);
|
||||||
@@ -78,8 +86,11 @@ main :: () -> ! {
|
|||||||
report("overflow-raised", oerr == error.Overflow);
|
report("overflow-raised", oerr == error.Overflow);
|
||||||
|
|
||||||
// 3. Stream to a file through a tiny staging buffer (forces flushes);
|
// 3. Stream to a file through a tiny staging buffer (forces flushes);
|
||||||
// read it back and assert it equals the same document.
|
// read it back and assert it equals the same document. Write into the
|
||||||
path := "/tmp/sx_0713_json.json";
|
// repo-local, gitignored scratch dir and unlink afterwards so nothing
|
||||||
|
// leaks and concurrent runs don't fight over a shared /tmp name.
|
||||||
|
if !create_dir_all(".sx-tmp") { print("mkdir: FAIL\n"); return; }
|
||||||
|
path := ".sx-tmp/sx_0713_json.json";
|
||||||
fh := open_file(path, .write);
|
fh := open_file(path, .write);
|
||||||
if fh == null { print("open: FAIL\n"); return; }
|
if fh == null { print("open: FAIL\n"); return; }
|
||||||
f := fh!;
|
f := fh!;
|
||||||
@@ -88,8 +99,8 @@ main :: () -> ! {
|
|||||||
f.close();
|
f.close();
|
||||||
|
|
||||||
back := read_file(path);
|
back := read_file(path);
|
||||||
|
delete_file(path);
|
||||||
if back == null { print("file-read: FAIL\n"); return; }
|
if back == null { print("file-read: FAIL\n"); return; }
|
||||||
report("file-exact", back! == EXPECT);
|
report("file-exact", back! == EXPECT);
|
||||||
delete_file(path);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
doc: {"name":"a\"b\n","tab":"x\ty","bs":"c\\d","ctrl":"\u0001","n":-7,"ok":true,"nil":null,"xs":[1,-2,3],"nested":{"k":"v"}}
|
doc: {"name":"a\"b\n","tab":"x\ty","bs":"c\\d","ctrl":"\u0001","n":-7,"zero":0,"pos":7,"min":-9223372036854775808,"max":9223372036854775807,"ok":true,"nil":null,"xs":[1,-2,3],"nested":{"k":"v"}}
|
||||||
buffer-exact: ok
|
buffer-exact: ok
|
||||||
buffer-len: ok
|
buffer-len: ok
|
||||||
overflow-raised: ok
|
overflow-raised: ok
|
||||||
|
|||||||
Reference in New Issue
Block a user