spec/robots_palette_spec.cr @ trunk
spec
scale = 0.75 fontscale = 1.500 box wid 1.380 ht 0.340 with .nw at (0.035, -0.035) fill 0xCBD5E1 color 0xCBD5E1 box wid 1.380 ht 0.340 with .nw at (0.000, 0.000) fill 0x7C83E6 color 0x7C83E6 box wid 0.300 ht 0.065 rad 0.018 at (0.370, -0.022) fill 0xffffff color 0xffffff line from (0.140, 0.000) to (0.140, -0.340) color white line from (1.240, 0.000) to (1.240, -0.340) color white text "require 'spec'" color white at (0.690, -0.170)→ /pikchrshow
crystal source
require "spec"
json block
{
"kind": "require",
"name": "spec",
"doc": null,
"children": []
}
json
scale = 0.75 fontscale = 1.500 box wid 1.330 ht 0.340 with .nw at (0.035, -0.455) fill 0xCBD5E1 color 0xCBD5E1 box wid 1.330 ht 0.340 with .nw at (0.000, -0.420) fill 0x7C83E6 color 0x7C83E6 box wid 0.300 ht 0.065 rad 0.018 at (0.370, -0.443) fill 0xffffff color 0xffffff line from (0.140, -0.420) to (0.140, -0.760) color white line from (1.190, -0.420) to (1.190, -0.760) color white text "require 'json'" color white at (0.665, -0.590)→ /pikchrshow
crystal source
require "json"
json block
{
"kind": "require",
"name": "json",
"doc": null,
"children": []
}
compiler/crystal/syntax
scale = 0.75 fontscale = 1.500 box wid 2.796 ht 0.340 with .nw at (0.035, -0.875) fill 0xCBD5E1 color 0xCBD5E1 box wid 2.796 ht 0.340 with .nw at (0.000, -0.840) fill 0x7C83E6 color 0x7C83E6 box wid 0.300 ht 0.065 rad 0.018 at (0.370, -0.863) fill 0xffffff color 0xffffff line from (0.140, -0.840) to (0.140, -1.180) color white line from (2.656, -0.840) to (2.656, -1.180) color white text "require 'compiler/crystal/syntax'" color white at (1.398, -1.010)→ /pikchrshow
crystal source
require "compiler/crystal/syntax"
json block
{
"kind": "require",
"name": "compiler/crystal/syntax",
"doc": null,
"children": []
}
../src/gpcr/render
scale = 0.75 fontscale = 1.500 box wid 2.343 ht 0.340 with .nw at (0.035, -1.295) fill 0xCBD5E1 color 0xCBD5E1 box wid 2.343 ht 0.340 with .nw at (0.000, -1.260) fill 0x7C83E6 color 0x7C83E6 box wid 0.300 ht 0.065 rad 0.018 at (0.370, -1.283) fill 0xffffff color 0xffffff line from (0.140, -1.260) to (0.140, -1.600) color white line from (2.203, -1.260) to (2.203, -1.600) color white text "require '../src/gpcr/render'" color white at (1.172, -1.430)→ /pikchrshow
crystal source
require "../src/gpcr/render"
json block
{
"kind": "require",
"name": "../src/gpcr/render",
"doc": null,
"children": []
}
../src/gpcr/roundtrip
scale = 0.75 fontscale = 1.500 box wid 2.530 ht 0.340 with .nw at (0.035, -1.715) fill 0xCBD5E1 color 0xCBD5E1 box wid 2.530 ht 0.340 with .nw at (0.000, -1.680) fill 0x7C83E6 color 0x7C83E6 box wid 0.300 ht 0.065 rad 0.018 at (0.370, -1.703) fill 0xffffff color 0xffffff line from (0.140, -1.680) to (0.140, -2.020) color white line from (2.390, -1.680) to (2.390, -2.020) color white text "require '../src/gpcr/roundtrip'" color white at (1.265, -1.850)→ /pikchrshow
crystal source
require "../src/gpcr/roundtrip"
json block
{
"kind": "require",
"name": "../src/gpcr/roundtrip",
"doc": null,
"children": []
}
../src/gpcr/robots_palette
scale = 0.75 fontscale = 1.500 box wid 2.954 ht 0.340 with .nw at (0.035, -2.135) fill 0xCBD5E1 color 0xCBD5E1 box wid 2.954 ht 0.340 with .nw at (0.000, -2.100) fill 0x7C83E6 color 0x7C83E6 box wid 0.300 ht 0.065 rad 0.018 at (0.370, -2.123) fill 0xffffff color 0xffffff line from (0.140, -2.100) to (0.140, -2.440) color white line from (2.814, -2.100) to (2.814, -2.440) color white text "require '../src/gpcr/robots_palette'" color white at (1.477, -2.270)→ /pikchrshow
crystal source
require "../src/gpcr/robots_palette"
json block
{
"kind": "require",
"name": "../src/gpcr/robots_palette",
"doc": null,
"children": []
}
ROBOTS_DEMO_SRC = File.read(File.join(__DIR__, "..", "examples", "robots_demo.cr"))
describe "robots_palette round-trip" do
it "GPCR.blocks_of reflects the robots faces" do
faces = GPCR.blocks_of
names = faces.map(&.name)
%w(scan cannon drive damage speed loc_x loc_y sqrt sin cos robot_rand main).each do |n|
names.should contain(n)
end
end
describe ":graph — demo_robot (c_call main, if/else, while, calls)" do
it "reconstructed source round-trips to the same JSON and compiles" do
original_json = gpcr_serialize_node(ROBOTS_DEMO_SRC, "demo_robot")
original_json.should_not be_empty
reconstructed = gpcr_block_json_to_crystal(original_json)
Crystal::Parser.new(reconstructed).parse
reconstructed.should contain("@[GPCR::Graph]")
reconstructed.should contain("def demo_robot")
reconstructed.should contain(%(main("Demo") do))
reconstructed.should contain("while angle < 360")
# "range = scan(angle, 0)" is a plain assignment — an :other node kind,
# rendered verbatim (ground truth), not reconstructed through rt_call.
reconstructed.should contain("range = scan(angle, 0)")
reconstructed.should contain("if range > 0")
reconstructed.should contain("cannon angle, range")
reconstructed.should contain("drive angle, 50")
# Compile check: the reconstructed graph must compile against the real
# palette, not just parse — catches anything Crystal::Parser can't see
# (e.g. the rt_c_call quoting gap this ticket found and fixed).
checkout = File.expand_path(File.join(__DIR__, ".."))
tmp_path = File.join(checkout, "spec", "fixtures", "_robots_demo_roundtrip_tmp.cr")
begin
File.write(tmp_path, %(require "../../src/gpcr/robots_palette"\n\n) + reconstructed)
stderr_io = IO::Memory.new
compile_result = Process.run(
"crystal", ["build", "--no-codegen", tmp_path],
chdir: checkout,
output: Process::Redirect::Close,
error: stderr_io
)
compile_result.success?.should be_true, "crystal build --no-codegen failed:\n#{stderr_io}"
ensure
File.delete(tmp_path) if File.exists?(tmp_path)
end
roundtrip_json = gpcr_serialize_node(reconstructed, "demo_robot")
original_json.should eq(roundtrip_json)
end
end
describe ":block — go_to_corner (case/when/else)" do
it "round-trips WITH its parameter (ticket a4942e5c88) and is actually callable" do
original_json = gpcr_serialize_node(ROBOTS_DEMO_SRC, "go_to_corner")
original_json.should_not be_empty
parsed_in = JSON.parse(original_json)
parsed_in["inputs"]?.should_not be_nil
parsed_in["inputs"].as_a.first["name"].as_s.should eq("corner")
parsed_in["inputs"].as_a.first["type"].as_s.should eq("Int32")
reconstructed = gpcr_block_json_to_crystal(original_json)
Crystal::Parser.new(reconstructed).parse
reconstructed.should contain("def go_to_corner(corner : Int32)")
reconstructed.should contain("case corner")
reconstructed.should contain("when 1")
reconstructed.should contain("drive 0, 50")
reconstructed.should contain("else")
reconstructed.should contain("drive 270, 50")
# Invoke check: a dropped parameter would compile cleanly as
# def go_to_corner() but fail here with "wrong number of arguments" —
# proving the signature, not just the declaration, survived
# reconstruction. go_to_corner needs the real palette's `drive` in
# scope, so require robots_palette rather than bare gpcr.
checkout = File.expand_path(File.join(__DIR__, ".."))
tmp_path = File.join(checkout, "spec", "fixtures", "_go_to_corner_invoke_tmp.cr")
begin
File.write(tmp_path, %(require "../../src/gpcr/robots_palette"\n\n) + reconstructed + "\ngo_to_corner(1)\n")
stdout_io = IO::Memory.new
stderr_io = IO::Memory.new
result = Process.run("crystal", ["run", tmp_path], chdir: checkout, output: stdout_io, error: stderr_io)
result.success?.should be_true, "crystal run failed:\n#{stderr_io}"
stdout_io.to_s.should contain("drive(0, 50)")
ensure
File.delete(tmp_path) if File.exists?(tmp_path)
end
roundtrip_json = gpcr_serialize_node(reconstructed, "go_to_corner")
original_json.should eq(roundtrip_json)
end
end
end