aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authoreunhhu <[email protected]>2026-10-13 07:40:30 +0100
committereunhhu <[email protected]>2026-10-13 07:40:30 +0100
commit010fa06adc3b05e3e164309ee34e200f5d858fa2 (patch)
tree4fafacc583806ae1426215c14757588a444ec761 /tests
parent8481b12fb794690cce0d9c54032ffba3de1fe31d (diff)
downloadpure-010fa06adc3b05e3e164309ee34e200f5d858fa2.tar.gz
pure-010fa06adc3b05e3e164309ee34e200f5d858fa2.zip
Research 140, 027's stage 2b measured: JSON read into a tree (aup's examples/json_tree.aup: a variant, lists in records, match, appends into a field's list, number(t)) and walked for a digest, against Python's reading: JSONTestSuite's 95 y_ taken alike, no n_ taken, 6 i_ taken; 300 random nested documents alike; in make check (module_specs, the suite and 100 random documents, three paths); 167 lines against the same design's 133 in Python (the bar missed: the type declarations), 40 times cJSON's time and 1.3 times plain Python's (556 ns a byte, 525 instructions); json_tree.py, json_tree_time.c (cJSON linked, digests compared first)
Diffstat (limited to 'tests')
-rw-r--r--tests/check.py53
1 files changed, 53 insertions, 0 deletions
diff --git a/tests/check.py b/tests/check.py
index bf77246..9386ced 100644
--- a/tests/check.py
+++ b/tests/check.py
@@ -1738,6 +1738,59 @@ def module_specs(aup, pure, work):
assert accepted == (name[0] == 'y') or name[0] == 'i', (name, accepted)
taken[name[0]] += accepted
runs['json'] = {'cases': len(cases), 'taken': taken}
+ # A JSON text read into a tree of values (its examples/json_tree.aup, 027's 2b: a variant, lists in records, match),
+ # walked breadth first for a digest, against Python's reading walked so: JSONTestSuite's cases and random documents.
+ class Pairs(list):
+ pass
+
+ def digest(data):
+ doc = json.loads(data.decode('utf-8'), object_pairs_hook=Pairs)
+ d = dict(nulls=0, trues=0, falses=0, numbers=0, strings=0, arrays=0, objects=0, sum=0.0, text_bytes=0, key_bytes=0)
+ q, at = [doc], 0
+ while at < len(q):
+ x = q[at]
+ if x is None: d['nulls'] += 1
+ elif x is True: d['trues'] += 1
+ elif x is False: d['falses'] += 1
+ elif isinstance(x, (int, float)): d['numbers'] += 1; d['sum'] += float(x)
+ elif isinstance(x, str): d['strings'] += 1; d['text_bytes'] += len(x.encode('utf-8'))
+ elif isinstance(x, Pairs):
+ d['objects'] += 1
+ for k, v in x: d['key_bytes'] += len(k.encode('utf-8')); q.append(v)
+ else: d['arrays'] += 1; q.extend(x)
+ at += 1
+ return d
+
+ def value(depth):
+ pick = chooser.random()
+ if depth > 4 or pick < 0.35:
+ return chooser.choice([None, True, False, chooser.randrange(-10 ** 6, 10 ** 6), chooser.uniform(-1e3, 1e3),
+ chooser.choice(['', 'a', 'é ü', 'line\nbreak', 'quote"back\\', '\U0001f600', '\u0001'])])
+ if pick < 0.7:
+ return [value(depth + 1) for _ in range(chooser.randrange(0, 6))]
+ return {f'k{chooser.randrange(50)}' if chooser.random() < 0.8 else '': value(depth + 1) for _ in range(chooser.randrange(0, 6))}
+
+ for k in range(100):
+ cases[f'r_{k}.json'] = json.dumps(value(0), ensure_ascii=chooser.random() < 0.5, indent=chooser.choice([None, 1, 2])).encode()
+ (work / f'r_{k}.json').write_bytes(cases[f'r_{k}.json'])
+ program = work / 'json_tree.pmod'
+ compile_module(aup, EDITOR.parent / 'json_tree.aup', program)
+ given = ''.join(f'message check @{work / name}\n' for name in cases)
+ outputs = {name: subprocess.run([str(pure / 'build/pure-run'), str(program)], input=given, capture_output=True, text=True,
+ timeout=600, env=dict(os.environ, **env)).stdout
+ for name, env in (('batches', {'PURE_JIT': '0'}), ('rounds', {'PURE_JIT': '0', 'PURE_BATCH': '0'}), ('all', {'PURE_JIT': 'all'}))}
+ for name in ('rounds', 'all'):
+ assert outputs[name] == outputs['batches'], ('json_tree', name)
+ keys = ('nulls', 'trues', 'falses', 'numbers', 'strings', 'arrays', 'objects', 'sum', 'text_bytes', 'key_bytes')
+ trees = {'y': 0, 'n': 0, 'i': 0, 'r': 0}
+ for name, line in zip(cases, outputs['batches'].splitlines()):
+ state = json.loads(line)
+ accepted = state['fault'] == 'none' and state['states']['valid'] is True
+ assert accepted == (name[0] in 'yr') or name[0] == 'i', (name, state['fault'])
+ if accepted:
+ assert {k: state['states'][k] for k in keys} == digest(cases[name]), name
+ trees[name[0]] += accepted
+ runs['json_tree'] = {'cases': len(cases), 'taken': trees}
return runs