mirror of
https://github.com/horizon-rust/horizon.git
synced 2026-11-09 10:39:46 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ec08a90d43 |
No files matched your search
@@ -33,3 +33,28 @@ class CommandComparisonTests(unittest.TestCase):
|
||||
def test_unknown_comparison_fails_loudly(self):
|
||||
with self.assertRaises(ValueError):
|
||||
compare(self.record(1), self.record(1), 'typo')
|
||||
|
||||
|
||||
class UnorderedAttributesTests(unittest.TestCase):
|
||||
KEY, PLAIN, ID = [5636095] + [False] * 5, [None] + [False] * 5, [5635925] + [False] * 5
|
||||
|
||||
def body(self, *entries):
|
||||
"""`/data get entity` feedback holding an `attributes` list of (id, base) entries, as the printer styles it."""
|
||||
spans = [['Pig has the following entity data: {', self.PLAIN], ['attributes', self.KEY], [': [', self.PLAIN]]
|
||||
for n, (name, base) in enumerate(entries):
|
||||
spans += [['{' if n == 0 else ', {', self.PLAIN], ['id', self.KEY], [': "', self.PLAIN],
|
||||
[f'minecraft:{name}', self.ID], ['", ', self.PLAIN], ['base', self.KEY], [': ' + base, self.PLAIN],
|
||||
['}', self.PLAIN]]
|
||||
spans += [[']}', self.PLAIN]]
|
||||
return {'cmd': '/data get entity x', 'messages': [spans], 'results': [[True, 1]]}
|
||||
|
||||
def test_the_lists_order_is_ignored_and_its_entries_are_not(self):
|
||||
from tools.test_command_exec_parity import unordered_attributes
|
||||
speed_first = self.body(('movement_speed', '0.25d'), ('max_health', '20.0d'))
|
||||
health_first = self.body(('max_health', '20.0d'), ('movement_speed', '0.25d'))
|
||||
other_base = self.body(('max_health', '10.0d'), ('movement_speed', '0.25d'))
|
||||
self.assertNotEqual(speed_first, health_first)
|
||||
self.assertEqual(unordered_attributes(speed_first), unordered_attributes(health_first))
|
||||
self.assertNotEqual(unordered_attributes(other_base), unordered_attributes(health_first))
|
||||
plain = {'cmd': '/say', 'messages': [[['attributes', self.PLAIN]]], 'results': [[True, 1]]}
|
||||
self.assertEqual(unordered_attributes(plain), plain)
|
||||
@@ -728,6 +728,65 @@ MASKS = [
|
||||
GAMERULES = re.compile(r'/gamerule \(([^)]*)\)')
|
||||
|
||||
|
||||
ATTRIBUTES_LIST = re.compile(r'attributes: \[')
|
||||
|
||||
|
||||
def unordered_attributes(record):
|
||||
"""The record with every SNBT `attributes: [...]` list in its messages sorted by entry.
|
||||
|
||||
`AttributeMap.pack` lists the instances in the iteration order of an `Object2ObjectOpenHashMap` keyed by
|
||||
`Holder.Reference`, which keeps `Object.hashCode`: an identity hash, so the order changes with the JVM's
|
||||
startup (the same script gives another order under another main class). Each entry keeps its text and
|
||||
styles; only the entries' order is normalised. parity_fuzz compares these lists unordered too."""
|
||||
out = json.loads(json.dumps(record))
|
||||
for line in out.get('messages') or []:
|
||||
if not isinstance(line, list) or not all(isinstance(span, list) and len(span) == 2 for span in line):
|
||||
continue
|
||||
chars = [(ch, span[1]) for span in line for ch in span[0]]
|
||||
text = ''.join(ch for ch, _ in chars)
|
||||
for match in reversed(list(ATTRIBUTES_LIST.finditer(text))):
|
||||
start = i = match.end()
|
||||
depth, quoted, cuts = 0, False, [start]
|
||||
while i < len(text):
|
||||
ch = text[i]
|
||||
if quoted:
|
||||
if ch == '\\':
|
||||
i += 1
|
||||
elif ch == '"':
|
||||
quoted = False
|
||||
elif ch == '"':
|
||||
quoted = True
|
||||
elif ch in '[{':
|
||||
depth += 1
|
||||
elif ch in ']}':
|
||||
if depth == 0:
|
||||
break
|
||||
depth -= 1
|
||||
elif ch == ',' and depth == 0 and text[i + 1:i + 2] == ' ':
|
||||
cuts += [i, i + 2]
|
||||
i += 1
|
||||
i += 1
|
||||
if len(cuts) == 1:
|
||||
continue
|
||||
cuts.append(i)
|
||||
entries = [chars[a:b] for a, b in zip(cuts[0::2], cuts[1::2])]
|
||||
separator = chars[cuts[1]:cuts[2]]
|
||||
ordered = sorted(entries, key=lambda e: json.dumps(e))
|
||||
joined = ordered[0]
|
||||
for entry in ordered[1:]:
|
||||
joined = joined + separator + entry
|
||||
chars[start:i] = joined
|
||||
text = ''.join(ch for ch, _ in chars)
|
||||
spans = []
|
||||
for ch, style in chars:
|
||||
if spans and spans[-1][1] == style:
|
||||
spans[-1][0] += ch
|
||||
else:
|
||||
spans.append([ch, style])
|
||||
line[:] = spans
|
||||
return out
|
||||
|
||||
|
||||
def masked(record):
|
||||
text = json.dumps(record['messages'], ensure_ascii=False)
|
||||
# GameRuleCommand registers the rules in identity-hash order, different
|
||||
|
||||
@@ -34,8 +34,10 @@ if __name__ == '__main__':
|
||||
source = build / 'vanilla.jsonl' if full else (reference or build / 'vanilla.jsonl')
|
||||
if reference and not full:
|
||||
assert source.read_bytes() == (build / 'vanilla.jsonl').read_bytes(), 'fresh Java bytes changed'
|
||||
expected = [json.loads(s) for s in source.read_text().splitlines()]
|
||||
native = [json.loads(s) for s in (build / 'rust.jsonl').read_text().splitlines()]
|
||||
# A pig's saved `attributes` list is in identity-hash order (driver.unordered_attributes).
|
||||
expected = [driver.unordered_attributes(json.loads(s)) for s in source.read_text().splitlines()]
|
||||
native = [driver.unordered_attributes(json.loads(s))
|
||||
for s in (build / 'rust.jsonl').read_text().splitlines()]
|
||||
assert len(expected) == len(native) == len(recorded)
|
||||
for op, a, b in zip(recorded, expected, native):
|
||||
if op['admission'] and not full:
|
||||
|
||||
Reference in new issue
Block a user