spec/self_host_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": []
}
../src/gpcr/std
scale = 0.75 fontscale = 1.500 box wid 2.068 ht 0.340 with .nw at (0.035, -0.875) fill 0xCBD5E1 color 0xCBD5E1 box wid 2.068 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 (1.928, -0.840) to (1.928, -1.180) color white text "require '../src/gpcr/std'" color white at (1.034, -1.010)→ /pikchrshow
crystal source
require "../src/gpcr/std"
json block
{
"kind": "require",
"name": "../src/gpcr/std",
"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": []
}
double_say
a block whose body is calls-only: passes the contract
scale = 0.75 fontscale = 1.500 box wid 1.841 ht 0.340 rad 0.035 with .nw at (0.035, -1.715) fill 0xCBD5E1 color 0xCBD5E1 box wid 1.841 ht 0.340 rad 0.035 with .nw at (0.000, -1.680) fill 0x4C97FF color 0x4C97FF box wid 0.300 ht 0.065 rad 0.018 at (0.370, -1.703) fill 0xffffff color 0xffffff text "double say " color white at (0.593, -1.850) box wid 0.535 ht 0.205 rad 0.102 with .nw at (1.105, -1.748) fill 0xFFFFFF color 0xFFFFFF text "text" color 0x263238 at (1.373, -1.850) box wid 0.240 ht 0.220 rad 0.110 at (1.691, -1.820) fill 0x334155 color 0x334155 text "1" at (1.691, -1.820) color 0xf8fafc box wid 1.305 ht 0.340 rad 0.035 with .nw at (0.035, -2.135) fill 0xCBD5E1 color 0xCBD5E1 box wid 1.305 ht 0.340 rad 0.035 with .nw at (0.000, -2.100) fill 0x4C97FF color 0x4C97FF box wid 0.300 ht 0.065 rad 0.018 at (0.370, -2.123) fill 0xffffff color 0xffffff box wid 0.300 ht 0.065 rad 0.018 at (0.370, -2.472) fill 0x4C97FF color 0x4C97FF text "say " color white at (0.302, -2.270) box wid 0.535 ht 0.205 rad 0.102 with .nw at (0.570, -2.167) fill 0xFFFFFF color 0xFFFFFF text "text" color 0x263238 at (0.837, -2.270) box wid 1.305 ht 0.340 rad 0.035 with .nw at (0.035, -2.555) fill 0xCBD5E1 color 0xCBD5E1 box wid 1.305 ht 0.340 rad 0.035 with .nw at (0.000, -2.520) fill 0x4C97FF color 0x4C97FF box wid 0.300 ht 0.065 rad 0.018 at (0.370, -2.542) fill 0xffffff color 0xffffff box wid 0.300 ht 0.065 rad 0.018 at (0.370, -2.892) fill 0x4C97FF color 0x4C97FF text "say " color white at (0.302, -2.690) box wid 0.535 ht 0.205 rad 0.102 with .nw at (0.570, -2.587) fill 0xFFFFFF color 0xFFFFFF text "text" color 0x263238 at (0.837, -2.690) box wid 0.300 ht 0.065 rad 0.018 at (0.370, -2.472) fill 0x4C97FF color 0x4C97FF box wid 0.300 ht 0.065 rad 0.018 at (0.370, -2.892) fill 0x4C97FF color 0x4C97FF→ /pikchrshow
crystal source
@[GPCR::Block(shape: :command, label: "double say #{text}", category: "Output")]
def double_say(text : String)
say text
say text
end
FIXTURE_FACES = GPCR.blocks_of(SelfHostFixtures)
GPCR.check_blocks(SelfHostFixtures)
TOP_FACES = GPCR.blocks_of
private def build_bad_block_fixture : {Process::Status, String}
# Crystal requires are relative, so the fixture lives inside the tree.
tmp = File.tempfile("gpcr_bad_block", ".cr", dir: __DIR__)
begin
tmp.print <<-SRC
require "../src/gpcr"
require "../src/gpcr/std"
@[GPCR::Block(shape: :command, label: "leaky")]
def leaky
@@state = 1
end
GPCR.check_blocks
SRC
tmp.flush
err = IO::Memory.new
status = Process.run("crystal", ["build", "--no-codegen", tmp.path], error: err)
{status, err.to_s}
ensure
tmp.delete
end
end
describe "GPCR self-hosting" do
describe "face reflection (GPCR.blocks_of)" do
it "derives faces for @[GPCR::Block] defs, carrying the def name" do
face = FIXTURE_FACES.find { |f| f.name == "double_say" }.not_nil!
face.shape.should eq "command"
face.label.should eq "double say \#{text}"
face.inputs.map(&.name).should eq ["text"]
end
it "walks the top-level scope by default, seeing primitives and blocks alike" do
names = TOP_FACES.map(&.name)
names.should contain "receive_event" # primitive
names.should contain "on_start" # block defined in GPCR
names.should contain "say"
TOP_FACES.find { |f| f.name == "on_start" }.not_nil!.shape.should eq "hat"
end
end
describe "Renderer.face_to_prim" do
it "splits a reflected label into text and slot parts" do
face = TOP_FACES.find { |f| f.name == "wait" }.not_nil!
prim = GPCR::Renderer.face_to_prim(face)
prim.parts.should eq [{:text, "wait "}, {:slot, "secs"}, {:text, " seconds"}]
prim.params.should eq ["secs"]
end
end
describe "the @[GPCR::Block] body contract (GPCR.check_blocks)" do
it "rejects a non-renderable body at compile time, naming def and node kind" do
status, err = build_bad_block_fixture
status.success?.should be_false
err.should contain "non-renderable element"
err.should contain "leaky: ClassVar"
end
end
describe "the event bus (runtime primitives)" do
it "delivers a fired event to a waiting receive_event and say writes the stream" do
out_io = IO::Memory.new
GPCR::Runtime.output = out_io
begin
done = Channel(Nil).new
spawn do
on_start
say "hi"
done.send nil
end
Fiber.yield
send_event "start"
done.receive
out_io.to_s.should eq "hi\n"
ensure
GPCR::Runtime.output = STDOUT
end
end
it "fire-and-forget: an event with no waiting subscriber is dropped, not queued" do
send_event "nobody-listening" # must not raise or block
end
end
describe "the pure user program (examples/blink.cr)" do
example_bin = File.tempname("blink_bin")
it "compiles from nothing but a require and a graph" do
src = File.read(Path[__DIR__].join("..", "examples", "blink.cr"))
src.lines.reject(&.blank?).first.should eq %(require "../src/gpcr/main")
src.should_not contain "spawn" # no boilerplate: the runtime include covers it
src.should_not contain "ARGV"
example = Path[__DIR__].join("..", "examples", "blink.cr").normalize.to_s
err = IO::Memory.new
status = Process.run("crystal", ["build", example, "-o", example_bin], error: err)
status.success?.should be_true
end
it "runs its graphs for a bounded time via the standard entry point" do
out_io = IO::Memory.new
Process.run(example_bin, ["run", "0.6"], output: out_io).success?.should be_true
out_io.to_s.should contain "\e[93m*\e[0m" # simulated LED on
out_io.to_s.should contain "." # simulated LED off
end
it "renders its own embedded source: require as pikchr block, faces across files" do
out_io = IO::Memory.new
Process.run(example_bin, ["render"], output: out_io).success?.should be_true
md = out_io.to_s
md.should contain "## ../src/gpcr/main"
md.should contain "require '../src/gpcr/main'"
md.should contain "*blink: LED on/off loop after start*"
md.should contain %(text "on start")
md.should contain %(text "set user LED " color white)
md.should contain %(text "true" color 0x263238)
md.should contain %(text "wait " color white)
md.should contain %(text "0.5" color 0x263238)
md.should contain %(text " seconds" color white)
md.should contain %(text "forever")
end
it "reports its faces as JSON, palette included" do
out_io = IO::Memory.new
Process.run(example_bin, ["faces"], output: out_io).success?.should be_true
names = Array(GPCR::Face).from_json(out_io.to_s).map(&.name)
["on_start", "forever", "set_user_led", "wait", "argument", "receive_event"].each do |n|
names.should contain n
end
end
end
end