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