2026-07-14 19:14:49 -04:00
|
|
|
# mcrf: objects=[Caption,Color,Frame,Scene,Timer] verified=0.2.8-dev status=ok
|
test(snippets): the docs site's 130 code samples are now part of the test suite
The samples published on mcrogueface.github.io were executed by nothing at all.
Each carried a machine-readable header -- objects=[...] verified=0.2.8@b6720f6
status=ok -- and every one of those fields was hand-typed. 130 snippets asserted
"status=ok" while no run had ever confirmed it. This is the same failure mode as
the 82 rotted tests in 112f357, at a larger scale and with no gate whatsoever.
The samples now live here, in the engine repo, so that breaking the API breaks the
build. The site pulls them from tests/snippets/ rather than keeping a copy to rot
in parallel (build_library.py, site-side).
They are display scripts: each builds a scene and stops. They deliberately do NOT
call sys.exit(), because they must stay copy-pasteable -- a reader who pastes one
into their game should get a running scene, not an interpreter that quits. Since
#350 that also makes them un-runnable on their own, so _harness.py is chained as a
second --exec to supply the ending the snippet must not have. Chaining rather than
exec()-ing the source is deliberate: the snippet travels the same path a real
user's script travels, and nothing has to read the file -- which is how the first
draft of this harness tripped over #378 and mistook three healthy snippets for
broken ones.
A snippet passes only if it runs clean AND leaves a scene with something in it.
Not raising is not enough: a sample whose body silently no-ops still renders as
code on the docs site, and would still be wrong.
tools/stamp_snippets.py makes the header a measurement instead of a claim: status
comes from an actual run, verified from the engine that ran it, and objects is
derived from the source intersected with what the engine exports -- so a snippet
that starts using a new type gets tagged for it without anyone remembering to. It
immediately found tags the humans had missed (062 uses Transition, Key and
InputState; the hand-written header listed none of them). --check is the CI gate.
The gate earned its keep on the first run, catching #379 (fixed here): configuring
default_transition before the first scene activation made the engine transition
FROM the internal "uitest" bootstrap scene, which is not a PyScene and has no
Python wrapper -- so mcrfpy.current_scene reported None for the transition's full
duration, immediately after the user had assigned it. A transition with nothing
meaningful to transition from now changes scene immediately. Scene-to-scene
transitions still report the outgoing scene until they complete.
Suite: 468/468 (336 + 130 snippets + 2 regressions).
closes #379
2026-07-14 18:29:37 -04:00
|
|
|
# Timer Basic - Periodic callback
|
|
|
|
|
import mcrfpy
|
|
|
|
|
|
|
|
|
|
scene = mcrfpy.Scene("demo")
|
|
|
|
|
mcrfpy.current_scene = scene
|
|
|
|
|
|
|
|
|
|
scene.children.append(mcrfpy.Frame(
|
|
|
|
|
pos=(0, 0), size=(1024, 768),
|
|
|
|
|
fill_color=mcrfpy.Color(30, 30, 40)
|
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
counter = mcrfpy.Caption(
|
|
|
|
|
text="Ticks: 0",
|
|
|
|
|
pos=(420, 350))
|
|
|
|
|
counter.fill_color = mcrfpy.Color(255, 220, 100)
|
|
|
|
|
scene.children.append(counter)
|
|
|
|
|
|
|
|
|
|
count = 0
|
|
|
|
|
|
|
|
|
|
def on_tick(timer, runtime):
|
|
|
|
|
global count
|
|
|
|
|
count += 1
|
|
|
|
|
counter.text = f"Ticks: {count}"
|
|
|
|
|
|
|
|
|
|
# Timer fires every 500ms
|
|
|
|
|
timer = mcrfpy.Timer("ticker", on_tick, 500)
|
|
|
|
|
|
|
|
|
|
scene.children.append(mcrfpy.Caption(
|
|
|
|
|
text="Timer interval: 500ms",
|
|
|
|
|
pos=(410, 450)
|
|
|
|
|
))
|