test: every firework flight duration and long explosion runs

The firework tooltip corpus sampled flight durations 0-3, 7 and 255 and runs
of at most six equal explosions, so Fireworks.addToTooltip's run count above
six and the rest of the unsigned-byte flight range were never compared. A
deterministic edge corpus now adds all 256 flight durations, runs of 0..256
equal explosions in every shape, colour and fade lists up to 32 entries
(custom, negative and duplicate colours), equality-sensitive runs (same shape
with different colours), both components on one stack, firework components on
non-rocket/non-star items, and every combination of hidden components.
--no-edges writes the random corpus exactly as before.

3331/3331 cases match the pinned client.
This commit is contained in:
MSedgeMC committed 2026-11-06 20:30:47 +00:00
1 parent 41f822b659
commit 176caeb5fd
1 file changed
+96 -7
+96 -7
View File
@@ -3,10 +3,15 @@
Random rockets (flight durations 0 to 3 and higher, up to six explosions, runs of equal ones) and stars, with dye and
custom colours, fade colours, trails and twinkles, some without the component (a rocket's default) and some whose
tooltip_display hides it. Vanilla prints what `Fireworks.addToTooltip` and `FireworkExplosion.addToTooltip` add
tooltip_display hides it. A deterministic edge corpus adds every flight duration 0..255, runs of 0..256 equal
explosions, interleaved runs, colour lists up to 32 entries (including custom, negative and duplicate colours), both
components on one stack, firework components on non-rocket/non-star items and every combination of hidden components.
Vanilla prints what `Fireworks.addToTooltip` and `FireworkExplosion.addToTooltip` add
(VanillaFireworkTooltipOracle); Horizon what `item_tooltip::firework_lines` gives (examples/probe/firework_tooltip_fixture).
`--no-edges` leaves the edge corpus out and writes the random corpus exactly as before.
"""
import random
import sys
try:
from . import lab, oracle_harness as harness
except ImportError:
@@ -54,20 +59,104 @@ def stack(rng):
return f'{{id:"minecraft:{item}",count:1{body}}}'
def edge_stack(item, components):
return '{id:"minecraft:%s",count:1,components:{%s}}' % (item, ','.join(components))
def edge_explosion(shape='star', colors=(), fade=(), trail=False, twinkle=False):
fields = [f'shape:"{shape}"']
if colors:
fields.append('colors:[I;%s]' % ','.join(map(str, colors)))
if fade:
fields.append('fade_colors:[I;%s]' % ','.join(map(str, fade)))
if trail:
fields.append('has_trail:1b')
if twinkle:
fields.append('has_twinkle:1b')
return '{' + ','.join(fields) + '}'
def edge_fireworks(flight, explosions):
return '"minecraft:fireworks":{flight_duration:%d,explosions:[%s]}' % (
flight, ','.join(explosions))
def edge_hidden(*components):
return '"minecraft:tooltip_display":{hidden_components:[%s]}' % (
','.join('"%s"' % c for c in components))
def edge_cases():
"""The cases the random generator cannot produce: every flight duration,
long runs, long colour lists, both components and non-rocket carriers."""
out = []
for flight in range(256):
out.append(edge_stack('firework_rocket', [edge_fireworks(flight, [edge_explosion()])]))
star = edge_explosion(shape='star')
trail = edge_explosion(shape='small_ball', trail=True)
burst = edge_explosion(shape='burst', colors=[0x41CD34], twinkle=True)
for count in [0, 1, 2, 3, 4, 5, 6, 7, 8, 16, 32, 64, 128, 255, 256]:
out.append(edge_stack('firework_rocket', [edge_fireworks(1, [star] * count)]))
for shape in SHAPES:
for count in (7, 8, 16):
out.append(edge_stack('firework_rocket', [
edge_fireworks(1, [edge_explosion(shape=shape)] * count)]))
red = edge_explosion(shape='star', colors=[0xFF0000])
blue = edge_explosion(shape='star', colors=[0x0000FF])
faded = edge_explosion(shape='star', colors=[0xFF0000], fade=[0x00FF00])
for sequence in ([star, trail, star], [star, star, trail, trail, star],
[star] * 7 + [trail], [star] * 6 + [trail] * 7,
[star, trail, burst, star, trail, burst],
[red, blue, red], [red, faded, red], [red, red, blue, blue]):
out.append(edge_stack('firework_rocket', [edge_fireworks(3, sequence)]))
dyes = [0xF0F0F0, 0xEB8844, 0xC354CD, 0x6689D3, 0xDECF2A, 0x41CD34, 0xD88198, 0x434343,
0xABABAB, 0x287697, 0x7B2FBE, 0x253192, 0x51301A, 0x3B511A, 0xB3312C, 0x1E1B1B]
for count in [0, 1, 2, 3, 4, 8, 16, 32]:
colors = (dyes * 2)[:count]
out.append(edge_stack('firework_star', [
'"minecraft:firework_explosion":' + edge_explosion(colors=colors,
fade=colors[::-1])]))
out.append(edge_stack('firework_star', [
'"minecraft:firework_explosion":' + edge_explosion(
colors=[0x123456, -1, 0xFFFFFF, 0x123456],
fade=[-2147483648, 2147483647])]))
for shape in SHAPES:
for trail_flag in (False, True):
for twinkle_flag in (False, True):
out.append(edge_stack('firework_star', [
'"minecraft:firework_explosion":' + edge_explosion(
shape=shape, trail=trail_flag, twinkle=twinkle_flag)]))
both = [edge_fireworks(2, [star, trail]),
'"minecraft:firework_explosion":' + edge_explosion(shape='creeper',
colors=[0x41CD34], trail=True)]
out.append(edge_stack('firework_rocket', both))
out.append(edge_stack('stick', both))
out.append(edge_stack('diamond_sword', [both[0]]))
out.append(edge_stack('arrow', [both[1]]))
out.append(edge_stack('firework_star', both))
for hidden in [('minecraft:fireworks',), ('minecraft:firework_explosion',),
('minecraft:fireworks', 'minecraft:firework_explosion')]:
out.append(edge_stack('stick', both + [edge_hidden(*hidden)]))
return out
def cases(build):
rng = random.Random(0x46495245)
path = build / 'cases.txt'
resources = lab.LOCAL / 'horizon-client' / '26.3'
path.write_text(f'#resources {resources}\n' + ''.join(stack(rng) + '\n' for _ in range(COUNT)))
return path, COUNT
lines = [stack(rng) for _ in range(COUNT)]
if '--no-edges' not in sys.argv:
lines += edge_cases()
path.write_text(f'#resources {resources}\n' + ''.join(line + '\n' for line in lines))
return path, len(lines)
if __name__ == '__main__':
harness.differential(
'firework-tooltip-parity', 'VanillaFireworkTooltipOracle', 'firework_tooltip_fixture', cases,
'Firework rocket and star tooltip lines: flight duration, explosions (counted runs), colours, fade, trail, '
'twinkle, defaults and tooltip_display',
'Firework rocket and star tooltip lines: flight duration (every 0..255), explosions (counted runs up to 256), '
'colours and fade lists to 32 entries, trail, twinkle, defaults, tooltip_display, both components on one stack '
'and firework components on non-rocket/non-star items',
sources=[lab.ROOT / 'crates/horizon-items/src/enchant.rs',
lab.ROOT / 'crates/horizon-client/src/item_tooltip.rs', lab.ROOT / 'crates/horizon-items/src/item_data.rs',
lab.ROOT / 'crates/horizon-client-render/src/particles.rs'],
expected_lines=COUNT)
lab.ROOT / 'crates/horizon-client-render/src/particles.rs'])