Hello!
Tests are a key part of my workflow, generally I want my tests to run when I save a file so I can get immediate feedback. I'd be great if Crystal had this functionality built in :)
Thanks,
Louis
I could see this going horribly wrong if your tests eat up a significant portion of CPU or if you save frequently...
@kirbyfan64 i think it must be possible to get it right, because there's more than one library in ruby that solve the same problem.
How do you imagine it going wrong? I've written a test runner for Elixir and I'd be happy to share my experiences and help out.
Recently I have used test watchers with Elixir, Erlang, Haskell, Rust, Ruby, JS, Go, and Clojure. They're pretty commonplace :)
@r-obert Ruby supports live-reloading source codes and do dynamic patches, but with Crystal, the only way to do the tests now is to compile our codes and the spec codes together once we triggered the test, so this could eat a lot CPU (and even I/O) resources if we changed and triggered tests frequently...
That's normal. The Ruby watching tools (and most of them except some Erlang and Clojure ones) don't use any hot loading features, they just effectively shell out and run crystal spec (or rather the language's equivalent command).
High resource use isn't a problem as saving while tests are already running does not cause another set of tests to run. It's just the same as running them normally.
Hi Louis, it's an interesting idea! I'm sorry if this is a silly question but, is there any reason why this needs to start as a built-in tool? Do you think there's going to be a need for some sort of compiler support to get this to work? Please do share the experience you gained writing the test runner for Elixir, either writing about it here or providing any links to relevant resources.
I don't think there is any need for compiler integration. There might be some clever performance stuff we could do with the compiler, though that could come later if ever.
I thought it'd be a nice-to-have in the standard Crystal tool, but it would be fine as an independent tool.
Here's some pseudo-code detailing how I might try and implement this in Crystal with my few hours experience.
while true
# Block until there is a file system event in the current directory.
# Leverage the InotifyWait project for the hard bit of detecting FS events
event = InotifyWait.await_file_system_event("./")
# Filter out undesired events
next unless event.type == :close_write
next unless event.path.ends_with?(".cr")
# Shell out and run the tests.
# Run only one spec file if it was a spec file that changed.
if event.path.ends_with?("_spec.cr")
run_in_shell("crystal spec #{event.path}")
else
run_in_shell("crystal spec")
end
end
Is it possible to write plugins for the Crystal tool? My google fu is weak.
Did you check https://github.com/f/guardian ? I believe there are also many tools that monitor the file system and perform actions accordingly.
Thanks Ary, I was actually at crystalshards.xyz looking for that because I couldn't recall the name :P. @lpil Isn't guardian enough for your use cases?
I use http://entrproject.org/, and it works great for running specs. It's quite unixy, which I like. echo src spec | entr -d crystal spec should work.
Ah, guardian could be what I want here.
I personally use watchexec, portable and independent of Crystal/Ruby.
While not as flexible as Guard (Ruby), it gets the job done (changes in files, run X command).
Considering the amount it takes to run tests, the only thing that slows down is the compiler phase :wink:
I'm closing this issue as there are enough alternatives outside the core of the language, and there seems to be no need for now to integrate them. Feel free to reopen this issue once this situation changes; for instance, when we add incremental compilation :-)
You may want to try Guard Process. I have used it quite a bit and it is very flexible and easy to customize.
You can only run specs when spec file is updated, and compile + run specs when app code is updated.
Not sure when compile needs to happen. Just running spec picks up changes in the source code.
Here's my Guardfile:
guard 'process', name:'Crystal', command:'crystal spec' do
watch %r{spec/.*}
watch %r{src/.*}
end