Login
gpcr/spec/robots_palette_spec.cr
Login

[edit]

spec/robots_palette_spec.cr @ trunk

spec

require '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)

crystal source

require "spec"

json block

{
  "kind": "require",
  "name": "spec",
  "doc": null,
  "children": []
}

json

require '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)

crystal source

require "json"

json block

{
  "kind": "require",
  "name": "json",
  "doc": null,
  "children": []
}

compiler/crystal/syntax

require '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)

crystal source

require "compiler/crystal/syntax"

json block

{
  "kind": "require",
  "name": "compiler/crystal/syntax",
  "doc": null,
  "children": []
}

../src/gpcr/render

require '../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)

crystal source

require "../src/gpcr/render"

json block

{
  "kind": "require",
  "name": "../src/gpcr/render",
  "doc": null,
  "children": []
}

../src/gpcr/roundtrip

require '../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)

crystal source

require "../src/gpcr/roundtrip"

json block

{
  "kind": "require",
  "name": "../src/gpcr/roundtrip",
  "doc": null,
  "children": []
}

../src/gpcr/robots_palette

require '../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)

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