Probably my favorite feature of the Dart Weekly newsletter is the package update section. Chris Buckett does a nice job of culling a few interesting little packages that I would otherwise overlook. It is fun to see some of the interesting, smaller projects that are going on in Dart-land. And every now and then I come across one that seems like it might be of use.
In this week's issue, the phrase “runtime scripts integrated with unit tests” caught my eye in the Hop project. I have been running an HTTP server to support some of my Dart tests. Maybe it will be of some help? Heck, even if I can't use it now, it's always fun to try something new. To the Dart cave!
I start with the usual update of Hipster MVC's
pubspec.yaml
to depend on the Hop package:name: hipster_mvc version: 0.2.6 description: Dart-based MVC framework author: Chris Strom <chris@eeecomputes.com> homepage: https://github.com/eee-c/hipster-mvc dependencies: unittest: any dirty: any uuid: any browser: any hop: anyFollowed by a quick
pub install
:➜ hipster-mvc git:(http-test-server) ✗ pub install Resolving dependencies............... Downloading bot 0.22.0 from hosted... Downloading bot_io 0.21.2 from hosted... Downloading hop 0.22.2 from hosted... Dependencies installed!The project wiki has some pretty nice documentation. Per the Setup section, I create
bin
sub-directory and copy Hop's hop
command into it:➜ hipster-mvc git:(http-test-server) ✗ mkdir bin ➜ hipster-mvc git:(http-test-server) ✗ cp /home/chris/.pub-cache/hosted/pub.dartlang.org/hop-0.22.2/bin/hop binI wonder if this might be better placed in
tool
as it is an internal script, but I stick with the convention for now.Next up, I convert my existing
test/test_server.dart
script to a library so that it can be imported and called from another script:library test_server; // import stuff here.. Future<HttpServer> run() { var port = 31337; return HttpServer.bind('127.0.0.1', port) ..then((app) { app.listen((HttpRequest req) { // process requests like normal }); print('Server started on port: ${port}'); }); }Starting a server is an asynchronous undertaking, so I return the
Future
from the HttpServer.bind()
call. Hopefully, that is all that I need to do in test_server.dart
. Now, I create
tool/hop_runner.dart
and define my start and stop server tasks:import 'package:hop/hop.dart'; import '../test/test_server.dart' as TestServer; import 'dart:async'; void main() { addAsyncTask('test_server:start', _startTestServer); addTask('test_server:stop', _stopTestServer); runHop(); }This should give me two hop tasks:
test_server:start
and test_server:stop
. But first, I need to define the two associated task functions.I am unsure how I will be able to stop the server, so I leave the
_stopTestServer()
function definition blank for now. In _startTestServer()
, I build a started
completer that I can use to mark the task completed once the server's future is marked complete:Future<bool> _startTestServer(TaskContext content) { var started = new Completer(); TestServer.run().then((_) { started.complete(true); }); return started.future; } bool _stopTestServer(TaskContext context) { // Not sure how this will work... return true; }When I ask Hop for a list of tasks, however, I am greeted with:
➜ hipster-mvc git:(http-test-server) ✗ ./bin/hop Unhandled exception: Illegal argument: "name" -- The value "test_server:start" does not contain the pattern "JSRegExp: pattern=^[a-z]([a-z0-9_\-]*[a-z0-9])?$ flags="Bother. I suppose colons are reserved for future nesting support. I prefer
test_server:start
mostly because I am used to that from the Rails world. A dash should suffice for now:void main() { addAsyncTask('test_server-start', _startTestServer); addTask('test_server-stop', _stopTestServer); runHop(); }With that, I am ready to try this from the command line:
➜ hipster-mvc git:(http-test-server) ✗ ./bin/hop test_server-start Unhandled exception: type '(TaskContext) => bool' is not a subtype of type 'Task' of 'task'. #0 addTask (package:hop/hop.dart:54:32) #1 main (file:///home/chris/repos/hipster-mvc/tool/hop_runner.dart:8:10)Weird! I had expected that I might get a problem from my async task with actual code, but it is no-op
addTask()
that is causing trouble. It turns out that this should be addSyncTask()
, not addTask()
. Both addAsyncTask()
and addSyncTask()
convert their callbacks into Task
objects that can be submitted to addTask()
. I make the change:void main() { addAsyncTask('test_server-start', _startTestServer); addSyncTask('test_server-stop', _stopTestServer); runHop(); }And then re-run
hop
to find:➜ hipster-mvc git:(http-test-server) ✗ ./bin/hop test_server-start Server started on port: 31337Yay! It worked. Except…
My server shuts down immediately. As soon as the Hop process exits, the Dart VM containing the server exits as well. In other words, running the server from inside Hop's hop_runner.dart ain't gonna work.
So instead, I revert all of my changes to my
test_server.dart
so that it needs to be run from the command line as dart test/test_server.dart
. Then, in Hop's hop_runner.dart
, I fork a shell process:import 'package:hop/hop.dart'; import '../test/test_server.dart' as TestServer; import 'dart:async'; import 'dart:io'; void main() { addAsyncTask('test_server-start', _startTestServer); addSyncTask('test_server-stop', _stopTestServer); runHop(); } Future<bool> _startTestServer(TaskContext content) { var started = new Completer(); Process.start('dart', ['test/test_server.dart'], runInShell: true).then((_) { started.complete(true); }); return started.future; }With that, I am able to start my test web server from the command line:
➜ hipster-mvc git:(http-test-server) ✗ ./bin/hop test_server-start ➜ hipster-mvc git:(http-test-server) ✗ ps -ef | grep test_server chris 783 1 0 23:56 pts/23 00:00:00 /bin/sh -c 'dart' 'test/test_server.dart' chris 784 783 3 23:56 pts/23 00:00:00 dart test/test_server.dart ➜ hipster-mvc git:(http-test-server) ✗ curl http://localhost:31337/test {"foo":1}%I am still forced to manually kill the server:
➜ hipster-mvc git:(http-test-server) ✗ kill 784But that is something that I ought to be able to improve upon another day.
For now, it seems that I can make use of
hop
in my test suite. I am unsure if it will prove a significant improvement on what I already have, but it ought to suffice as a suitable framework for building future improvements.Day #810
No comments:
Post a Comment