spec/gpcr_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": []
}
../src/gpcr
scale = 0.75 fontscale = 1.500 box wid 1.783 ht 0.340 with .nw at (0.035, -0.455) fill 0xCBD5E1 color 0xCBD5E1 box wid 1.783 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.643, -0.420) to (1.643, -0.760) color white text "require '../src/gpcr'" color white at (0.891, -0.590)→ /pikchrshow
crystal source
require "../src/gpcr"
json block
{
"kind": "require",
"name": "../src/gpcr",
"doc": null,
"children": []
}
GPCR_START = Channel(Nil).new(0)
class ExamplePrimitives
@[GPCR::Primitive(shape: :hat, label: "on start", category: "Events")]
def on_start
GPCR_START.receive
end
@[GPCR::Primitive(shape: :command, label: "set user LED #{state}")]
def set_user_led(state : Bool)
end
@[GPCR::Primitive(shape: :reporter, label: "join #{a} and #{b}", category: "Text", returns: :string)]
def join(a : String, b : String = "") : String
a + b
end
# not a block: no annotation, so nothing is derived for it
def helper(x : Int32)
end
end
BLOCKS = GPCR.blocks_of(ExamplePrimitives)
class NewShapePrimitives
@[GPCR::Primitive(shape: :predefined_process, label: "run subroutine #{name}", category: "Control")]
def run_subroutine(name : String)
end
@[GPCR::Primitive(shape: :document, label: "open file #{path}", category: "File")]
def open_file(path : String)
File.open(path)
end
end
NEW_BLOCKS = GPCR.blocks_of(NewShapePrimitives)
describe GPCR do
describe ".blocks_of" do
it "derives a block only for annotated defs" do
BLOCKS.size.should eq(3)
BLOCKS.map(&.label).should_not contain("helper")
end
it "derives shape, label, and category from the annotation" do
hat = BLOCKS[0]
hat.shape.should eq("hat")
hat.label.should eq("on start")
hat.category.should eq("Events")
hat.returns.should be_nil
hat.inputs.should be_empty
end
it "derives inputs from the parameter list" do
led = BLOCKS[1]
led.shape.should eq("command")
led.inputs.size.should eq(1)
led.inputs[0].name.should eq("state")
led.inputs[0].type.should eq("boolean")
led.inputs[0].default.should be_nil
end
it "derives returns and parameter defaults" do
join = BLOCKS[2]
join.shape.should eq("reporter")
join.returns.should eq("string")
join.inputs.map(&.type).should eq(["string", "string"])
join.inputs[0].default.should be_nil
join.inputs[1].default.should eq(JSON::Any.new(""))
end
end
describe "derived block form (JSON edit protocol)" do
it "serializes the derived form as the edit-protocol JSON" do
json = JSON.parse(BLOCKS[1].to_json)
json["shape"].should eq("command")
json["label"].should eq("set user LED \#{state}")
json["inputs"][0]["name"].should eq("state")
json["inputs"][0]["type"].should eq("boolean")
end
it "round-trips a derived block through JSON" do
restored = GPCR::Face.from_json(BLOCKS[2].to_json)
restored.shape.should eq("reporter")
restored.label.should eq("join \#{a} and \#{b}")
restored.returns.should eq("string")
restored.inputs.size.should eq(2)
end
end
describe "new primitive shapes" do
it "derives predefined_process shape and its category" do
req = NEW_BLOCKS.find { |b| b.label.includes?("run subroutine") }
req.should_not be_nil
req.not_nil!.shape.should eq("predefined_process")
req.not_nil!.category.should eq("Control")
req.not_nil!.inputs[0].name.should eq("name")
end
it "derives document shape and File category" do
file = NEW_BLOCKS.find { |b| b.label.includes?("open file") }
file.should_not be_nil
file.not_nil!.shape.should eq("document")
file.not_nil!.category.should eq("File")
file.not_nil!.inputs[0].name.should eq("path")
end
end
describe ".slot_type" do
it "maps Crystal restrictions to slot types" do
GPCR.slot_type("Bool").should eq("boolean")
GPCR.slot_type("Float64").should eq("number")
GPCR.slot_type("Int32").should eq("number")
GPCR.slot_type("String").should eq("string")
end
it "treats an untyped parameter as any" do
GPCR.slot_type(nil).should eq("any")
GPCR.slot_type("").should eq("any")
end
end
end