Wednesday, July 31, 2013

Dart Scheduled Test and Browser Tests


After last night, I have the ability to run both dart:io (server side) and dart:html (browser) tests from the same script. This will be great for continuous integration—especially for Dart for Hipsters which has significant number of both kinds of tests and needs to run them often to prevent the kinds of language regressions that I now face in updating the book.

I have a good number of server-side tests in a fake test server package that I am developing to support the book. I would like to add a few browser tests as well—both to aid me in ensuring that my fake test server works and to give me a little more practice at writing tests with the Scheduled Test package.

Since my browser tests are going to hit a running HTTP server—my fake test server—my scheduled tests will actually be a little simpler than the server side tests. In the server-side counterparts, the tests needed to schedule starting the server in addition to requests, responses and actually setting test expectations. I still have a bit of test setup to do, but the setup now only concerns itself with clean-up after the test is run. In Scheduled Test, this is done with an onComplete() callback in the setUp() block:
  final String URL_ROOT = 'http://localhost:31337/widgets';

  group("/widgets", (){
    setUp(() {
      currentSchedule.
        onComplete.
        schedule(() {
          return HttpRequest.request('${URL_ROOT}/ALL', method: 'delete');
        });
    });
    // tests here...
  });
The currentSchedule top-level getter is available regardless of whether or not a setup schedule is created. I use it here to schedule a tear-down step: deleting all of the records in the database via a REST-like web interface.

Now I am ready to write an actual test. I schedule the request first. The response is the second scheduled action and, in there, I set the test expectation:
  group("/widgets", (){
    setUp(() { /* ... */ });

    test("POST new records", (){
      var responseReady = schedule(() {
        return HttpRequest.
          request(URL_ROOT, method: 'post', sendData: '{"test": 42}');
      });

      schedule(() {
        responseReady.then((req) {
          var rec = JSON.parse(req.response);
          expect(rec['test'], 42);
        });
      });
    });

  });
The results of HttpRequest.request() in the first schedule is a future that completes with a request instance. By returning it from the scheduled function, the schedule() function also returns the same future. This is then a quick, clean way to communicate the active request into the next schedule—without relying on local variables.

The request is a POST with JSON contents. The expectation is that the JSON contents will be returned from the server. There should also be a record ID returned from the server, but I will test that in a separate test. With that, I have my browser test passing against a real fake server:
➜  plummbur-kruk git:(scheduled_test) ✗ content_shell --dump-render-tree test/index.html
CONSOLE MESSAGE: unittest-suite-wait-for-done
CONSOLE MESSAGE: PASS: /widgets POST new records

CONSOLE MESSAGE: 
CONSOLE MESSAGE: All 1 tests passed.
CONSOLE MESSAGE: unittest-suite-success
The test passes, but content_shell never returns. I think it will time out after a few minutes, but that is not a recipe for a solid continuous integration setup. The problem is in the test/index.html page that provides the browser context for content_shell to run my tests.

In there, I have testing apparatus that instructs the browser virtual machine that it is, in fact, running tests. As such it needs to wait for a signal before it can consider the page rendered:
<html>
<head>
  <title>Plummbur Kruk Test Suite</title>
  <script type="application/dart" src="browser_test.dart"></script>
  <script type='text/javascript'>
    var testRunner = window.testRunner || window.layoutTestController;
    if (testRunner) {
      function handleMessage(m) {
        if (m.data == 'done') {
          testRunner.notifyDone();
        }
      }
      testRunner.waitUntilDone();
      window.addEventListener("message", handleMessage, false);
    }
  </script>
  <script src="packages/browser/dart.js"></script>
</head>
<body>
</body>
</html>
Until the testRunner object is notified that it is done, the page is not considered fully rendered and content_shell will not exit. Without that testing apparatus, the page will often render before a single test runs, causing content_shell to exit with no useful testing. So I really need something to notify the message event listener when all tests have completed.

With vanilla unit tests in Dart, I have been running a periodic check to see if all of the tests have completed:
pollForDone(List tests) {
  if (tests.every((t)=> t.isComplete)) {
    window.postMessage('done', window.location.href);
    return;
  }

  var wait = new Duration(milliseconds: 100);
  new Timer(wait, ()=> pollForDone(tests));
}
When every test is complete, then a “done“ message is posted to the window, which the testing apparatus in test/index.html then converts into the necessary notifyDone() call. In vanilla unittest, this works with:
pollForDone(testCases);
But in Scheduled Test, there is no testCases top-level getter.

After digging through Scheduled Test a bit, I have to admit that I am stumped. The best that I can come up with is to add one last schedule after every other group and test:
main() {
  group("/widgets", (){ /* ... */ });
  test("Complete", (){
    schedule((){
      window.postMessage('done', window.location.href);
    });
  });
}
That seems to do the trick, but it feels wrong. Writing a test that is not a test, but rather is just there to support the harness is awkward. This may be a function of using a testing framework—Scheduled Test—that is intended only for the server. At the risk of calling my grapes sour, the real test hardly needs scheduled test and I am unsure that it really benefits much from Scheduled Test. Then again, there is something to be said for the consistency of using the same test framework for both my server-side and browser tests.

I will call it a night here and hope that tomorrow brings some clarity on the issue.


Day #829

Tuesday, July 30, 2013

Running Browser and Server Test Together


I think my fake test server for functional testing Dart browser code is in pretty good shape. I also think I have a pretty good handle on the scheduled_test package that I have been exploring for the past few nights. But if I have learned anything over the past few years, it is that even when I think I know something, I don't. So tonight, I am going to write a few new tests for my fake test server.

I am extracting this out from Hipster MVC, so I already have a REST-like feature built into the server that allows tests to GET and POST to a /widgets resource, as well as PUT, DELETE, and GET to /widgets/:id resources. I hope to use Schedule Test to verify that it works.

To add to the degree of difficulty, I am going to test this with HttpRequest from the browser side of things. In Dart, there are two different HttpRequest classes: one for the server side and one for the browser. I have been testing from the server side, which means that I have been using the dart:io library. If there is a Node.js equivalent in Dart, then it is in the dart:io library.

In addition to making servers easy to write, it also makes testing servers nice as well. But in my case, most of the uses of this fake test server will come from browser code. Instead of dart:io, I will be using the HttpRequest from dart:html. This HttpRequest is a Dart version of the venerable XMLHttpRequest object from JavaScript.

Unfortunately, the two libraries cannot co-exist. It makes no sense to allow browser code to execute dart:io code like writing to the file system. Actually that would be a pretty horrendous security violation which is why dart:io classes are not in core Dart and not available in the browser. So the only way to have both dart:io and dart:html tests in the same test suite is to run two separate processes. Which is where Bash comes in...

My test/run.sh script will start by running the server code that I have been building over the past few days:
#!/bin/bash

###
# Test the server with server tools
echo "Running dart:io tests"
dart test/server_test.dart
That all works with no changes.

Next, I need to execute my client tests. This is slightly more involved in that I need to start a server, run content_shell against my browser test page (browser tests need a web page context), parse the output, and stop the server. Fortunately, I have gotten good at this:
#!/bin/bash

###
# Test the server with server tools
echo "Running dart:io tests"
dart test/server_test.dart

###
# Test the server from the browser
echo
echo "Running dart:html tests"

# Start the test server
dart test/dummy_server.dart &
server_pid=$!

# Run the actual tests
results=`content_shell --dump-render-tree test/index.html 2>&1`
echo $results
echo "$results" | grep CONSOLE

echo "$results" | grep 'unittest-suite-success' >/dev/null

echo "$results" | grep -v 'Exception: Some tests failed.' >/dev/null

# Stop the server
kill $server_pid
rm -f test.db test/test.db
I think that works. To be sure, I need to write some Scheduled Test tests. But I have a #pairwithme session tonight, so I think it makes sense to call it a night there. I will pick back up with the new Scheduled Tests tomorrow.

Day #828

Monday, July 29, 2013

Better Scheduled Tests in Dart


I got a start on scheduled_test last night. The scheduled test package is a replacement for the unittest library that is built into Dart. I have grown rather fond of unittest's ability to test asynchronous code, but the scheduled test package suggests that it may be even better, so I cannot resist.

One difference that I have found is that solo_group() does not work for running a single group of tests (as it does in unittest), though solo_test() does work like usual. Aside from that, I am mostly trying to wrap my brain around scheduling asynchronous tests. I think last night's scheduled test may not be the Scheduled Test Way™:
    test("POST /stub responds successfully", (){
      schedule((){
        new HttpClient().
          postUrl(Uri.parse("http://localhost:31337/stub")).
          then((request) {
            request.write('{"foo": 42}');
            return request.close();
          }).
          then(wrapAsync(
            (response) {
              expect(response.statusCode, 204);
            }
          ));
      });
    });
It works, but it is still not pretty. Once code is pretty, then I feel like I am doing it “right”—or at least the Scheduled Test Way™.

The first thing that I think to try is splitting the expectation out from the test execution code. Currently, it is a little hard to quickly scan to find the expectation, though at least the expectation is at the bottom of the code. In scheduled tests, different asynchronous code—like execution and response expectation—is split into different schedules:
    test("POST /stub responds successfully", (){
      var response;

      schedule(() {
        return new HttpClient().
          postUrl(Uri.parse("http://localhost:31337/stub")).
          then((request) {
            request.write('{"foo": 42}');
            return request.close();
          }).
          then((res) => response = res);
      });

      schedule(()=> expect(response.statusCode, 204));
    });
I quite like that. I have to introduce a test-scoped variable, response, but aside from that, it reads rather nicely.

The Scheduled Test documentation (which is excellent) suggests that improvement is still possible. Specifically, that test scoped variable is unlikely to scale well. Instead, the Scheduled Test Way™ is to obtain the result of a previous scheduled to be used in a subsequent schedule:
    test("POST /stub responds successfully", (){
      var responseReady = schedule(() {
        return new HttpClient().
          postUrl(Uri.parse("http://localhost:31337/stub")).
          then((request) {
            request.write('{"foo": 42}');
            return request.close();
          });
      });

      schedule(() {
        responseReady.then((response)=> expect(response.statusCode, 204));
      });
    });
The responseReady variable is a future. The Scheduled Test documentation says that the returned future will complete with the return value. Here, I do something a bit different, but it still works. I return the result of postUrl()'s then(). When you post a URL, then write and close the request, you get another future back that completes with the response. So the return value of the then() in the above code is a future of type HttpResponse (Future<HttpResponse>).

Even though it is not explicitly stated in the Scheduled Test documentation, returning this future will also work when used in the next scheduled test. And it reads quite nicely as well: when the response is ready, then I expect that the response status code will be 204.

I rather like that. I wonder if there are any other cool features to investigate?

Day #827

Sunday, July 28, 2013

Getting Started with Scheduled Tests in Dart


That Robert guy has loads of good ideas. One of his suggestions was to investigate the scheduled_test package for better async testing. I rather like the async testing that comes built into the unittest package in Dart, but if there is something better, then I would love to give it a whirl.

So I add scheduled_test to the list of packages my testing library is depending on:
name: plummbur_kruk
description: A real pretend server for testing.
dependencies:
  scheduled_test: any
  # ...
In the list of dependencies, scheduled_test replaces unittest. It is not a drop-in replacement for unittest. Rather, it is built on top of unittest (and replaces a few unittest methods). As such, scheduled_test depends on unittest so unittest will still be installed by Pub.

Then, I Pub install to get started:
➜  plummbur-kruk git:(master) ✗ pub install
Resolving dependencies.................
Downloading stack_trace 0.6.5 from hosted...
Downloading path 0.6.5 from hosted...
Downloading scheduled_test 0.6.5 from hosted...
Downloading http 0.6.5 from hosted...
Dependencies installed!
Now to kick the tires. I replace the import of unittest with the scheduled test equivalent:
import 'package:scheduled_test/scheduled_test.dart';
The two packages both define some of the same top-level methods so they cannot both be imported.

In the existing unittest tests, I have setup and teardown for some of my test server implementation that looks like the following in vanilla unittest:
  group("Running Server", (){
    var server;
    setUp((){
      return PlumburKruk.main()
        ..then((s){ server = s; });
    });

    tearDown(() => server.close());
    // tests here...
  });
Let's see what they might look in scheduled tests:
  group("Running Server", (){
    var server;
    setUp((){
      schedule(() {
        return PlumburKruk.main()
          .then((s){ server = s; });
      });

      currentSchedule.
        onComplete.
        schedule(() => server.close());
    });
    // tests here...
  });
That is not much of an improvement, but I am just getting familiar with things so I will defer judging for now. The big thing that has changed in the switch from a teardown function to defining the “tear down” as what happens when the scheduled tests complete.

As for the testing, I need to add something to the “schedule” in there as well. Currently, I have an asynchronous test that verifies that stub resources can be created on the server:
    test("POST /stub responds successfully", (){
      new HttpClient().
        postUrl(Uri.parse("http://localhost:31337/stub")).
        then((request) {
          request.write('{"foo": 42}');
          return request.close();
        }).
        then(expectAsync1(
           (response) {
             expect(response.statusCode, 204);
           }
        ));
    });
The asynchronous behavior occurs at the end of that test. The response from the server comes back out of band, which is what the expectAsync1() call does. It expects an asynchronous callback to be invoked at some point in the future with a single argument (the response).

There is no expectAsync in scheduled tests. Instead, I have to wrapAsync() in a schedule:
    test("POST /stub responds successfully", (){
      schedule(() {
        new HttpClient().
          postUrl(Uri.parse("http://localhost:31337/stub")).
          then((request) {
            request.write('{"foo": 42}');
            return request.close();
          }).
          then(wrapAsync(
            (response) {
              expect(response.statusCode, 204);
            }
          ));
      });
    });
I do like the wrapAsync() call instead of the expectAsyncN() methods. I am used to the variations of the latter by now, but still find it hard to explain. Conceptually, wrapAsync() is simpler.

I am not sure that I see too much value in the added ceremony of declaring new schedules. Then again I think this is probably too simple a case to see significant benefits of this package. I am mostly going through this exercise to see if I want to base significant development on scheduled tests or if the vanilla tests will suffice. So far, I like it enough to continue playing another night.


Day #826

Saturday, July 27, 2013

Dart in the ICE Code Editor


During a brief exchange with Seth Ladd, he suggested that the ICE Code Editor might be able to perform Dart coding similar to try.dartlang.org. I am not quite convinced that the additional compile time would make for a satisfactory experience (there is already a 2 second delay between the end of typing and an auto-update). But, it an intriguing enough idea that I am going to explore it.

First up, can I edit and run Dart code in ICE with the Dartium browser? Yes I can:



As an aside I would really like to suppress those Application Cache messages. They are really, really annoying. Anyhow…

I am going to get those editor warnings since the JavaScript syntax highlighter is in action. I will have to live with them for a bit. Next I add another warning by importing the dart:html library and doing something simple with it:
<body></body>
<script src="/ice/packages/browser/dart.js"></script>
<script type="application/dart">
import 'dart:html';
main() {
  print("Yay Dart!!!");
  document.body
    ..append(new Element.html('<h1>Howdy Space Cowboy!</h1>'))
    ..style.margin = '250px';
}
</script>
That works as well. The <h1> element is successfully appended to the preview frame behind the code:



I have no reason to think that Three.dart would not work in there as well.

At this point, I am sufficiently satisfied that I am ready to dive into a try.dartlang.org-like implementation for non-Dart browsers (i.e. all of them). This is likely a multi-night exploration, but I need to start somewhere. That somewhere is to pull in the compiled JavaScript that try.dartlang.org currently uses:
<!-- ... -->
<script src="http://try.dartlang.org/leap.dart.js"></script>
Unsurprisingly, that does not work, failing while trying to call appendChild() on null. In other words, that leap.dart.js expects the page in which it is embedded to contain a DOM structure nearly identical to that of try.dartlang.org.

So I spend some time digging through the leap.dart code used on try.dartlang.org. Happily, it looks like I will not need large sections of it (parsing syntax highlighting and communicating with iframes) as ICE already does that. I will need to be able to extract the Dart code between <script> tags, so I will pick up there tomorrow and see if I can pull enough of leap.dart into my own library to get it working on a local copy of ICE.

This should be fun, mostly because I have a built-in excuse to dig through someone else's code for a few days. I love reading other people's code!

Day #825

Friday, July 26, 2013

CORS on a Dart Server


There were a lot of really good questions after my OSCON talk on Dart. I think I was able to answer most of the questions statisfactorily and accurately, but to one I gave the wrong answer.

The question was how to make an HttpRequest to a different origin site. I thought that once the CORS headers were set properly on the server, there was a setting in HttpRequest that was all that was required. I followed up afterwards and found that the questioner had solved the problem in the meantime—in this case, it was hitting the wrong server (one without CORS headers set).

But it occurs to me that I have not tried this myself. In fact, I have specifically side-stepped the problem in the Hipster MVC test suite by only running my browser-based tests from content_shell. This utility which evaluates and analyzes a browser page also doubles as test harness by loading a test context page and sending the console output to STDOUT on the command line.

What is side-stepping my CORS question is that content_shell is able to make localhost connections to a test server. When I run the same test suite accessing the same test server from the Dartium browser, I get a bunch of errors like:
OPTIONS http://localhost:31337/widgets/ALL Origin null is not allowed by Access-Control-Allow-Origin. localhost:31337/widgets/ALL:1
XMLHttpRequest cannot load http://localhost:31337/widgets/ALL. Origin null is not allowed by Access-Control-Allow-Origin. index.html:1
OPTIONS http://localhost:31337/widgets/ALL Origin null is not allowed by Access-Control-Allow-Origin.
...
I am unsure how an OPTIONS request for cross origin request checking should respond in this case. Also, I am on a plane and too cheap to pay the $20 just to look up the answer, so I am going to start by replying with a simple 200 with no response body. In my Dart test server I do this as:
  HttpServer.bind('127.0.0.1', 31337).then((app) {
    app.listen((HttpRequest req) {
      if (req.method == 'OPTIONS') {
        HttpResponse res = req.response;
        res.statusCode = HttpStatus.OK;
        res.close();
        return;
      }
      // Handle other requests...
    });
  });
Nothing too fancy there—it reads quite well. If the current request is an OPTIONS, then I grab the response property from the incoming request object, set a 200/OK status code on it, and close the response, signalling that processing of this response is complete. I then return from the request stream so that no other handling is attemped.

But after restarting the server and re-running my tests, I still get the same errors in the Dartium console. So next I add the header that is being complained about, Access-Control-Allow-Origin to the response headers:

  HttpServer.bind('127.0.0.1', port).then((app) {
    app.listen((HttpRequest req) {
       HttpResponse res = req.response;
       res.headers
         ..add('Access-Control-Allow-Origin', 'null');

      if (req.method == 'OPTIONS') { /* ... */ }
      // Handle other requests...
    });
  });
I am fairly sure that a value of * would work here as well (confirmed later), but I start with the most restricted value that can possibly work—the “null” value currently supplied by Dartium when running a file:// test context page.

With that change applied to my test server, my tests still fail. But now with a new error message. Instead of complaing about the Access-Control-Allow-Origin header, Dart is complaining about Access-Control-Allow-Headers:
OPTIONS http://localhost:31337/widgets Request header field Content-type is not allowed by Access-Control-Allow-Headers. localhost:31337/widgets:1
Phew! So at least I am on the right track. It is never fun to try a theory and see no change at all.

To change this error, I add the apparently required header to my test server:
  HttpServer.bind('127.0.0.1', port).then((app) {
    app.listen((HttpRequest req) {
      HttpResponse res = req.response;
      res.headers
        ..add('Access-Control-Allow-Origin', 'null')
        ..add('Access-Control-Allow-Headers', 'Content-Type');

      if (req.method == 'OPTIONS') { /* ... */ }
      // Handle other requests...
    });
  });
I then get a similar (but different) warning:
OPTIONS http://localhost:31337/widgets/ALL Method DELETE is not allowed by Access-Control-Allow-Methods. 
I fix that with year another header:
  HttpServer.bind('127.0.0.1', port).then((app) {
    app.listen((HttpRequest req) {
      HttpResponse res = req.response;
      res.headers
        ..add('Access-Control-Allow-Origin', 'null')
        ..add('Access-Control-Allow-Headers', 'Content-Type')
        ..add('Access-Control-Allow-Methods', 'DELETE,PUT');

      if (req.method == 'OPTIONS') { /* ... */ }
      // Handle other requests...
    });
  });
(the PUT comes from another message that I see from one of the tests)

With that, I have eliminated all of my errors and have my test suite passing—now from both the browser and content shell:
➜  test git:(http-test-server) ✗ content_shell --dump-render-tree index.html
CONSOLE MESSAGE: unittest-suite-wait-for-done
CONSOLE MESSAGE: PASS: unsupported remove
CONSOLE MESSAGE: PASS: Hipster Sync can parse regular JSON
CONSOLE MESSAGE: PASS: Hipster Sync can parse empty responses
CONSOLE MESSAGE: PASS: Hipster Sync HTTP get it can parse responses
CONSOLE MESSAGE: PASS: Hipster Sync HTTP post it can POST new records
CONSOLE MESSAGE: PASS: Hipster Sync (w/ a pre-existing record) HTTP PUT: can update existing records
CONSOLE MESSAGE: PASS: Hipster Sync (w/ a pre-existing record) HTTP DELETE: can remove the record from the store
CONSOLE MESSAGE: PASS: Hipster Sync (w/ multiple pre-existing records) can retrieve a collection of records

CONSOLE MESSAGE:
CONSOLE MESSAGE: All 8 tests passed.
CONSOLE MESSAGE: unittest-suite-success
In the end, my response at the OSCON session was misleading (sorry about that). I had indicated that only a single parameter need be set in the HttpRequest constructor. I think I was likely thinking of the withCredentials option, which will send cookies and similar authorization data to the CORS server. The answer is that nothing else is needed. As long as the server sets the appropriate headers, which I can do now in my Dart server, the the HttpRequest object from dart:html will just work.


Day #824

Thursday, July 25, 2013

Another Dart Pub Deploy Attempt


One of the truly nice things about blogging every day is that often I find my next topic suggested and even outlined by folks that are crazy enough to be reading along. One such individual, Robert, suggested in the comments of last night's post that I try a different approach with Dart pub deploy command.

As and aside, comments and suggestions are especially welcome and appreciated when I have spent too much of the day enjoying a conference and do not have the proper amount of time to plan (or think) about a post. And yes, contrary to public perception, I do put some actual thought into these things beforehand. I wonder why I am rambling...

Anyhow, the goal remains the same: to see if I can get the pub deploy command to deploy my code from this state:
➜  ice-beta git:(gh-pages) ✗ tree . -P 'index*|*.dart' -l -L 2
.
├── index.html
├── main.dart
└── packages
    ├── browser -> /home/chris/.pub-cache/hosted/pub.dartlang.org/browser-0.6.5/lib
    ├── crypto -> /home/chris/.pub-cache/hosted/pub.dartlang.org/crypto-0.6.5/lib
    ├── ice_code_editor -> /home/chris/.pub-cache/hosted/pub.dartlang.org/ice_code_editor-0.0.9/lib
    ├── js -> /home/chris/.pub-cache/hosted/pub.dartlang.org/js-0.0.24/lib
    ├── meta -> /home/chris/.pub-cache/hosted/pub.dartlang.org/meta-0.6.5/lib
    └── unittest -> /home/chris/.pub-cache/hosted/pub.dartlang.org/unittest-0.6.5/lib

7 directories, 2 files
Into something that will work on GitHub pages. The primary obstacle to this is the symbolic links that Dart Pub normally uses. The other obstacle being that the pub deploy command seems to expect code to be in a web sub-directory.

Robert's suggestion was to place a main entry point in the web sub-directory that imports the real (i.e. the current) main.dart entry point. The hope being that this would let the code remain as-is, but still allow pub deploy to to the necessary compilation to JavaScript and removal of the packages symbolic links.

So I create web/main.dart as:
import '../main.dart' as Main;

main()=> Main.main();
Then try a pub deploy:
➜  ice-beta git:(gh-pages) ✗ pub deploy
Finding entrypoints...
Copying   web/          → deploy/
Compiling web/main.dart → deploy/main.dart.js
web/main.dart:1:8: Error: no library name found in file:///home/chris/repos/gamingjs/ice-beta/main.dart
import '../main.dart' as Main;
       ^^^^^^^^^^^^^^
Failed to compile "/home/chris/repos/gamingjs/ice-beta/web/main.dart".
So I add a library statement to the “real” main script:
library main;

import 'package:ice_code_editor/ice.dart' as ICE;

main()=> new ICE.Full();
With that pub deploy works:
➜  ice-beta git:(gh-pages) ✗ pub deploy
Finding entrypoints...
Copying   web/          → deploy/
Compiling web/main.dart → deploy/main.dart.js
But the layout still does not seem as though it is going to work. The index.html in my sub-directory points to the main.dart file in my sub-directory. For Dart-enabled browsers that will work just fine, except that the symbolic links will still not resolve once published to GitHub pages:
➜  ice-beta git:(gh-pages) ✗ tree . -P 'main.dart.js|index*|*.dart' -l -L 2
.
├── deploy
│   ├── main.dart
│   └── main.dart.js
├── index.html
├── main.dart
├── packages
│   ├── browser -> /home/chris/.pub-cache/hosted/pub.dartlang.org/browser-0.6.5/lib
│   ├── crypto -> /home/chris/.pub-cache/hosted/pub.dartlang.org/crypto-0.6.5/lib
│   ├── ice_code_editor -> /home/chris/.pub-cache/hosted/pub.dartlang.org/ice_code_editor-0.0.9/lib
│   ├── js -> /home/chris/.pub-cache/hosted/pub.dartlang.org/js-0.0.24/lib
│   ├── meta -> /home/chris/.pub-cache/hosted/pub.dartlang.org/meta-0.6.5/lib
│   └── unittest -> /home/chris/.pub-cache/hosted/pub.dartlang.org/unittest-0.6.5/lib
└── web
    ├── main.dart
    └── packages -> ../packages  [recursive, not followed]

10 directories, 5 files
Further, the index.html and main.dart in my web application directory will not work with non-Dart browsers since there is no main.dart.js file in there for legacy browser to fallback to.

I might be able to post-process this with another script—moving the deploy/main.dart.js into the application directory. But I already have a decache.sh script that solves my current use-case. So unless I am missing something (which is certainly possible in my current state—any ideas Robert?) it's back to OSCON partying. Woo hoo!


UPDATE: Per a suggestion from Paul Evans, I tried setting the HOME shell variable to the current working directory:
➜  ice-beta git:(gh-pages) ✗ HOME=`pwd` pub install
Resolving dependencies.....................
Downloading js 0.0.24 from hosted...
Downloading ice_code_editor 0.0.9 from hosted...
Downloading browser 0.6.5 from hosted...
Downloading meta 0.6.5 from hosted...
Downloading crypto 0.6.5 from hosted...
Downloading unittest 0.6.5 from hosted...
Dependencies installed!
(using HOME=. creates packages symlinks that do not resolve)

That looks promising in that all of my dependencies, which would otherwise be re-used from the system cache, are downloaded again. However, I am still left with a symbolic links to a package cache—only the package cache is now in the current directory:
➜  ice-beta git:(gh-pages) ✗ ls -l packages
total 4
lrwxrwxrwx 1 chris chris 54 Jul 26 11:16 browser -> /home/chris/repos/gamingjs/ice-beta/.pub-cache/hosted/pub.dartlang.org/browser-0.6.5/lib
lrwxrwxrwx 1 chris chris 53 Jul 26 11:17 crypto -> /home/chris/repos/gamingjs/ice-beta/.pub-cache/hosted/pub.dartlang.org/crypto-0.6.5/lib
lrwxrwxrwx 1 chris chris 62 Jul 26 11:16 ice_code_editor -> /home/chris/repos/gamingjs/ice-beta/.pub-cache/hosted/pub.dartlang.org/ice_code_editor-0.0.9/lib
lrwxrwxrwx 1 chris chris 50 Jul 26 11:17 js -> /home/chris/repos/gamingjs/ice-beta/.pub-cache/hosted/pub.dartlang.org/js-0.0.24/lib
lrwxrwxrwx 1 chris chris 51 Jul 26 11:16 meta -> /home/chris/repos/gamingjs/ice-beta/.pub-cache/hosted/pub.dartlang.org/meta-0.6.5/lib
lrwxrwxrwx 1 chris chris 55 Jul 26 11:16 unittest -> /home/chris/repos/gamingjs/ice-beta/.pub-cache/hosted/pub.dartlang.org/unittest-0.6.5/lib
That is not really an improvement. The /home/chris directory does not exist on GitHub. Even if it did, the symbolic links will not work in GitHub pages. Even if they did, “dot” files and directories like .pub-cache will not work with GitHub pages.

Really, this does not matter too much as publishing a Dart application to GitHub pages is something of a rarity (I would think) and I have a 14 line Bash script that takes care of it for me.

Day #823

Wednesday, July 24, 2013

Getting Started with Dart Pub Deploy for Applications


I was quite pleased with yesterday's simple Bash script to “de-cache” Dart packages for deployment to my simple GitHub pages site. It seemed a nice, concise solution to a problem in need of a bit of automation. I was almost proud. And then Kasper Lund told me about pub deploy.

In my defense, I did know of pub deploy, but after brief examination, had decided it was not likely a good candidate for my use-case. But I did not actually try it to make sure. Tonight I do so.

On the http://gamingjs.com GitHub pages web site, I have two sub-directories: ice and ice-beta. These contain different versions of the ICE Code Editor at http://gamingjs.com/ice/ and http://gamingjs.com/ice-beta/. Both of these sub-directories are nearly identical with an index.html page, a main.dart script that starts the application, and a bunch of Dart packages in the packages sub-directory created by the Dart Pub tool:
➜  ice-beta git:(gh-pages) ✗ tree . -P 'index*|*.dart' -l -L 2
.
├── index.html
├── main.dart
└── packages
    ├── browser -> /home/chris/.pub-cache/hosted/pub.dartlang.org/browser-0.6.5/lib
    ├── crypto -> /home/chris/.pub-cache/hosted/pub.dartlang.org/crypto-0.6.5/lib
    ├── ice_code_editor -> /home/chris/.pub-cache/hosted/pub.dartlang.org/ice_code_editor-0.0.9/lib
    ├── js -> /home/chris/.pub-cache/hosted/pub.dartlang.org/js-0.0.24/lib
    ├── meta -> /home/chris/.pub-cache/hosted/pub.dartlang.org/meta-0.6.5/lib
    └── unittest -> /home/chris/.pub-cache/hosted/pub.dartlang.org/unittest-0.6.5/lib

7 directories, 2 files
There is other stuff in there as well, but that (I think) is the most important bit for tonight. The index.html needs to reside there because it enables GitHub pages to serve up a page when the directory is accessed (e.g. http://gamingjs.com/ice-beta/). The main.dart is important because it imports the ICE package:
import 'package:ice_code_editor/ice.dart' as ICE;

main()=> new ICE.Full();
Last night's decache.sh converted the symbolic links in the packages sub-directory into local copies so that they will still work once pushed to GitHub (GitHub pages do not work with symbolic links).

So will pub deploy work with this?

As is, the answer would seem to be “no”:
➜  ice-beta git:(gh-pages) ✗ pub deploy
There is no '/home/chris/repos/gamingjs/ice-beta/web' directory.
This error would seem to refer to the pub layout convention for Dart web applications.

Perhaps if I copy the index.html file into a web sub-directory?
➜  ice-beta git:(gh-pages) ✗ mkdir web
➜  ice-beta git:(gh-pages) ✗ cp index.html web
➜  ice-beta git:(gh-pages) ✗ pub install
Resolving dependencies.........
Dependencies installed!
➜  ice-beta git:(gh-pages) ✗ pub deploy
Finding entrypoints...
Copying   web/ → deploy/
➜  ice-beta git:(gh-pages) ✗ ls deploy
index.html
That did not seem to do anything useful. Perhaps if I also place my main.dart entry point in there?
➜  ice-beta git:(gh-pages) ✗ cp main.dart web
➜  ice-beta git:(gh-pages) ✗ pub deploy
Finding entrypoints...
Copying   web/          → deploy/
Compiling web/main.dart → deploy/main.dart.js
➜  ice-beta git:(gh-pages) ✗ ls deploy
index.html  main.dart  main.dart.js
Ah, that is pretty nice. Compiling my application to JavaScript and moving it into the deploy directory is quite useful. But not in my particular use-case.

I need to have the main entry point for ICE directly in the ice sub-directory. I even need this locally so that I can test things out with a local Jekyll server. Placing my index.html application page and the associated main.dart entry point down a level are not going to serve the correct URLs. So I think I have a handle on pub deploy, but I think my custom built decache.sh will still be of use.


Day #822

Tuesday, July 23, 2013

Automated Deployment of Dart Packages on a Static Site


As of last night, I have one script to automate a portion of the deployment of the ICE Code Editor. Said script examines the contents of the dart2js-generated JavaScript for inclusion in an application cache manifest file. Tonight, I hope to automate another piece of the deployment—copying packages from the system cache into my GitHub pages repository.

Dart Pub is a pretty awesome tool. Among its qualities is that it will consolidate all installed packages in a single system (or user) directory, creating a symbolic link in the current application. This saves a bit of space and ensures that all package:XXX import statement will always resolve. The only downside is that symbolic links will not work on GitHub pages. Instead, I need the real packages.

As an Emacs user, I am quite good at copying the packages directory into Emacs for a quick conversion to a rm-the-symlinks and cp-the-system-packages. But I should be able to do better.

My first thought was to parse the output of ls -l packages/ with awk. The output is standard enough that I ought to be able to do it without too much work:
➜  ice-beta git:(gh-pages) ✗ ls -l packages 
total 24
lrwxrwxrwx 1 chris chris 64 Jul 23 18:47 browser -> /home/chris/.pub-cache/hosted/pub.dartlang.org/browser-0.6.5/lib
lrwxrwxrwx 1 chris chris 63 Jul 23 18:47 crypto -> /home/chris/.pub-cache/hosted/pub.dartlang.org/crypto-0.6.5/lib
lrwxrwxrwx 1 chris chris 72 Jul 23 18:47 ice_code_editor -> /home/chris/.pub-cache/hosted/pub.dartlang.org/ice_code_editor-0.0.9/lib
lrwxrwxrwx 1 chris chris 60 Jul 23 18:47 js -> /home/chris/.pub-cache/hosted/pub.dartlang.org/js-0.0.24/lib
lrwxrwxrwx 1 chris chris 61 Jul 23 18:47 meta -> /home/chris/.pub-cache/hosted/pub.dartlang.org/meta-0.6.5/lib
lrwxrwxrwx 1 chris chris 65 Jul 23 18:47 unittest -> /home/chris/.pub-cache/hosted/pub.dartlang.org/unittest-0.6.5/lib
But a bit of research turned up the readlink tool (I thought it was just a C command):
➜  ice-beta git:(gh-pages) ✗ readlink packages/browser 
/home/chris/.pub-cache/hosted/pub.dartlang.org/browser-0.6.5/lib
So my decache.sh script can iterate through each file in pub's packages directory, read the link, delete the exiting record, replacing it with the contents of the system cache:
#!/bin/sh

cd packages
for file in *; do
    link=`readlink $file`
    echo "$file ($link)"
    rm $file
    cp -r $link $file
done
And that works!

While I am at it, I ought to make the script idempotent. If I re-run it immediately after de-caching, it will copy the contents into the sub-directory, which is not quite right. All that is really needed is an if statement to exit with an error if the packages directory is already de-cached. Of course, if statements in Bash are a pain.

I think the only reason that Bash is not a more widely used language is because nobody can remember where to put the damn square brackets. It turns out that you put the damn square brackets around a boolean statement and before the semi-colon and then keyword:
#!/bin/sh

cd packages
for file in *; do
    if [ ! -L $file ]; then
        echo "Already decached."
        exit 1
    fi

    link=`readlink $file`
    echo "$file ($link)"
    rm $file
    cp -r $link $file
done
The -L test operator returns true if the argument is a symbolic link. If it is not—it the packages sub-directory is already de-cached, then I exit with non-zero status.

That does the trick:
➜  ice-beta git:(gh-pages) ✗ ./decache.sh             
browser (/home/chris/.pub-cache/hosted/pub.dartlang.org/browser-0.6.5/lib)
crypto (/home/chris/.pub-cache/hosted/pub.dartlang.org/crypto-0.6.5/lib)
ice_code_editor (/home/chris/.pub-cache/hosted/pub.dartlang.org/ice_code_editor-0.0.9/lib)
js (/home/chris/.pub-cache/hosted/pub.dartlang.org/js-0.0.24/lib)
meta (/home/chris/.pub-cache/hosted/pub.dartlang.org/meta-0.6.5/lib)
unittest (/home/chris/.pub-cache/hosted/pub.dartlang.org/unittest-0.6.5/lib)
➜  ice-beta git:(gh-pages) ✗ ./decache.sh             
Already decached.
➜  ice-beta git:(gh-pages) ✗ echo $?
1
That seems a good stopping point for tonight. Back to OSCON partying… er, preparation!


Day #821



Monday, July 22, 2013

Auto-Generating Appcache Manifests for Dart Applications


With travel and prep for OSCON, I am going to try to keep posts brief this week. Still, the gods of the chain must be appeased, so posts there must be. In today's, I hope to nail down one of the two manual processes in deployment of the ICE Code Editor: generating the application cache manifest file (the other is copying Dart packages from system cache).

Application cache is a newish feature of modern browsers that allow sites to cache files in browsers. This is done with a manifest attribute on the <html> tag of a page:
<!DOCTYPE html>
<html manifest="editor.appcache">
  <!-- ... -->
</html>
The format of the appcache file is a plain text file. Inside, it typically contains three sections: the manifest (opens the manifest and contains meta-information), the cache (contains a list of files to be cached) and a listing of files that should be fetched from the network rather than cached (typically a wildcard to denote everything not explicitly listed as being cached).

I am going to build this with a Bash script. Sometimes there is nothing better than a simple Bash script to get the job done. I toyed with the idea of a script to replace just a portion of the existing appcache file, but complete regeneration seems more robust. If I ever have to make a change to the structure of the appcache file, I will not have to worry about breaking this script by moving around important anchor text.

I start by declaring a variable to hold the name of the appcache file:
#!/bin/sh

APPCACHE=editor.appcache
I have no intention of changing the name of the file—I use a variable as a matter of taste to keep the rest of the code clean.

Next up, I wipe the old appcache file and write the manifest section of the file:
##
# Manifest
echo 'CACHE MANIFEST' > $APPCACHE
date +'# %Y-%m-%d %H:%M:%S' >> $APPCACHE
echo >> $APPCACHE
I start with a single file redirect (>) so that echo 'CACHE MANIFEST' overwrites the previous contents of the file. All subsequent operations will be done with the append redirect (>>). I am not worrying about making a backup because the generated code will be checked into source code—I already have a backup.

After the manifest directive, I add a comment containing the date to the appcache file—the date command's support for strftime-like formatting comes in handy here. Browser application cache does not use this information—the leading pound sign is a comment in appcache files. The purpose of the time stamp is solely to force the web server to respond with a 200 instead of 304. That is, a change in the content of the file—even a comment—will tell the server that this appcache file has changed and that it should not respond that it is NOT MODIFIED (304).

It is vitally important that this timestamp change whenever cached files change. If the server replies that the appcache file is unchanged, then no updated files will be loaded by the browser. I could change each and every file that is listed in the appcache manifest, but if the appcache file is unchanged, the browser will continue to use the old versions. If files are added or subtracted from the list of cached files in the appcache file, that would count as a change to the appcache file. More often than not, changes involve only modifying existing files. And a timestamp comment is the best way to communicate that some change awaits.

With the preliminary work out of the way, it is time for the cached content. There are three different kinds of content that I want cached for the ICE Code editor: libraries used while editing code in ICE (e.g. Three.js, Tween.js), files explicitly referenced in SCRIPT tags of the ICE application web page, and the JavaScript files that actually give ICE its functionality.

For the first section of cache—the files I want to make available to the programmers using ICE to make 3D animations—I list them manually:
##
# Cache

echo 'CACHE:' >> $APPCACHE

# Fixed files used within the editor for creating Three.js worlds:
cat <<EOF >> $APPCACHE
/favicon.ico
/Three.js
/Tween.js
/Detector.js
/physi.js
/Mouse.js
/Scoreboard.js
/ChromeFixes.js
/ammo.js
/physijs_worker.js

EOF
I am cating a HEREDOC to accomplish this. The cat command would normally echo the contents of a file. By supplying it with a HEREDOC, I tell cat to read from here.

Next, I have to echo the main.dart file into the list of cached files in editor.appcache:
# Dart script referenced in a SCRIPT tag:
echo main.dart >> $APPCACHE
echo >> $APPCACHE
No production browser in the world is going to do anything with that file, but it is explicitly referenced in a SCRIPT tag. For this reason, it must be in application cache to prevent a network request.

Last, I use my find + grep chain from the other day to find the JavaScript files—both generated by Dart and included from the ICE library—and place them in the list of cached files:
# Dart and JS code used to make the editor:
find | \
  grep -e \\.js$ -e \\.map$ -e \\.html$ -e \\.css$ | \
  grep -v -e unittest -e /lib/ | \
  sed 's/^\.\///' \
  >> $APPCACHE
I add a small sed script to the chain to remove leading ./ from the find output:
./part.js
./index.html
./main.dart.js.map
...
That does it for the CACHED section of my application cache file.

All that remains is a wildcard entry in the NETWORK section:
##
# Network
cat <<EOF >> $APPCACHE

NETWORK:
*
EOF
That is all there is to it. After running the script, the only difference in editor.appcache is the timestamp:
➜  ice-beta git:(gh-pages) ✗ git diff -u editor.appcache
diff --git a/ice-beta/editor.appcache b/ice-beta/editor.appcache
index 90d2ba8..ebaedef 100644
--- a/ice-beta/editor.appcache
+++ b/ice-beta/editor.appcache
@@ -1,5 +1,5 @@
 CACHE MANIFEST
-# 2013-07-20
+# 2013-07-22 10:26:40

 CACHE:
 /favicon.ico
So I have successfully written an automated script that ought to keep up with the changing files in ICE. The full, working script is:
#!/bin/sh

APPCACHE='editor.appcache'

##
# Manifest
echo 'CACHE MANIFEST' > $APPCACHE
date +'# %Y-%m-%d %H:%M:%S' >> $APPCACHE
echo >> $APPCACHE

##
# Cache

echo 'CACHE:' >> $APPCACHE

# Fixed files used within the editor for creating Three.js worlds:
cat <<EOF >> $APPCACHE
/favicon.ico
/Three.js
/Tween.js
/Detector.js
/physi.js
/Mouse.js
/Scoreboard.js
/ChromeFixes.js
/ammo.js
/physijs_worker.js

EOF

# Dart script referenced in a SCRIPT tag:
echo main.dart >> $APPCACHE
echo >> $APPCACHE

# Dart and JS code used to make the editor:
find | \
  grep -e \\.js$ -e \\.map$ -e \\.html$ -e \\.css$ | \
  grep -v -e unittest -e /lib/ | \
  sed 's/^\.\///' \
  >> $APPCACHE

##
# Network
cat <<EOF >> $APPCACHE

NETWORK:
*
EOF
That is a fine stopping point for today. Up tomorrow, I will try to automate the copying of system pub packages into a deployment.

Day #820

Sunday, July 21, 2013

Easy Sanity Checks with Early Alpha Dart Packages


I am beginning to feel a tad desperate to get an updated Dart for Hipsters out. The main blocker at this point is the ability to test and analyze some of the more HTTP intensive chapters. Some of the lessons on Dart testing that I have learned over the past few weeks would certainly help.

In the source code for the book, I wound up mocking my HTTP requests so that I could test things:
  void fetch() {
    var req = MaybeMockHttpRequest.httpRequest();
    req.on.load.add((event) {
      var list = JSON.parse(req.responseText);
      _handleOnLoad(list);
    });
    req.open('get', url, async: true);
    req.send();
  }
This has the advantage of being able to be run, but also being testable. The problem is, of course, that I cannot include this code in the book without some kind of pre-processing.

Rather than worry that my pre-processing might introduce bugs in the book copy as the language evolves, I would rather use the test server that I have been working up recently. Really, I'd just as soon use a stubbing strategy like Sinon.js but that is not an option because Dart does not support dynamic method redefinition. Dart boasts some dynamic language features, just nothing that will help me here.

I have a small repository that contains some of the testing work that I have been doing. It is local-only, but it is a git repository, which means that I can install it in other Dart applications using the Pub packaging tool that is built into Dart.

So, in my book's top-level directory, I add my test server repository as a dependency:
name: DartForHipsters
dependencies:
  unittest: any
  browser: any
  plummbur_kruk:
    git:
      /home/chris/repos/plummbur-kruk
A quick pub update and I am ready to go:
➜  code git:(master) ✗ pub update
Resolving dependencies............
Dependencies updated!

➜  mvc git:(master) ✗ ls -l packages 
...
lrwxrwxrwx 1 chris chris 85 Jul 21 22:10 plummbur_kruk -> /home/chris/.pub-cache/git/plummbur_kruk-4e23fa5c4423b6f8226821bd4cdda231ed633dec/lib
...
I have yet to settle on an code organization for the test code. I could put it in the test sub-directory, but there is already a testing sub-directory that supports the Testing chapter in the book. I defer organization for another day and simple create my “Plummbur Kruk” test server as test_server.dart right in the top-level code directory as:
import 'package:plummbur_kruk/server.dart' as PlummburKruk;

main() => PlummburKruk.main();
With that, I can start my test server:
➜  code git:(master) ✗ dart test_server.dart
Server started on port: 31337
Now, back in the code, I can switch to a real HttpRequest object:
  void fetch() {
    var req = new HttpRequest();

    req.on.load.add((event) {
      var list = JSON.parse(req.responseText);
      _handleOnLoad(list);
    });

    req.open('get', url, async: true);
    req.send();
  }
After updating the url getter to point to my test server, I have a passing test again, this time without any mock-post-processing malarkey. I will still have to add some kind of server stop/start/reset code. That could be done as a Bash script or as a Hop task. But the important thing is that I have been able to perform a quick sanity check of running a test server from a very alpha package repository. Hopefully I can build the two together.


Day #819

Saturday, July 20, 2013

Bare Minimum: File Size and Numbers for Dart Deployment (Application Cache)


Following up on last night's deployment improvements in the Dart version of the ICE Code Editor, I hope to be able to finish up today. Whereas yesterday I shrunk the application payload with the --minify option in dart2js, today I would like to shrink the payload by being a bit more judicious with what needs to be downloaded.

Taking a look at http://gamingjs.com/ice-beta/ (which currently holds the Dart version of ICE) in an incognito browser session, I find:


18 requests  ❘  190 KB transferred  ❘  1.66 s (load: 1.64 s, DOMContentLoaded: 139 ms)
In some respects, that is not bad. This is the non-minified version of ICE, meaning that the compiled JavaScript is 912 KB alone. That, plus the 17 other requests that are needed to download ICE should push the payload up to well over 1 MB. Since the actual data transferred is only 190 KB, it would seem that gzip encoding of the resources is producing some nice bandwidth savings. Combined that with the 60% decrease in the compiled JavaScript from the --minify option, and ICE should be in fine shape.

And yet…

There is an obvious gap in the transfer of files. What gives? And if I can eliminate it, will the download go even faster?

The answer to the first question—the cause of the gap in transferring files—lives in the JavaScript console. There are many lines of output (around 100, in fact) ending with:
...
Application Cache Progress event (95 of 100) http://gamingjs.com/ice-beta/part.js.map
Application Cache Progress event (96 of 100) http://gamingjs.com/ice-beta/packages/unittest/src/utils.dart
Application Cache Progress event (97 of 100) http://gamingjs.com/ice-beta/packages/ice_code_editor/full/rename_dialog.dart
Application Cache Progress event (98 of 100) http://gamingjs.com/ice-beta/packages/unittest/src/test_case.dart
Application Cache Progress event (99 of 100) http://gamingjs.com/ice-beta/packages/js/src/wrapping/typed_proxy.dart
Application Cache Progress event (100 of 100) 
Application Cache Cached event
Document was loaded from Application Cache with manifest http://gamingjs.com/ice-beta/editor.appcache
So it seems that I may be putting just a little too much into application cache. Well, maybe a lot. Considering that loading the editor and the default project only required 18 requests, I am almost certainly loading way too much.

A closer inspection shows that #96 and #98 of the application cache files are unit test files—those certainly are not needed in application cache. They probably do not need to exist on the server at all. But even more so, there is absolutely no reason to jam a single Dart file into application cache. There is not a single production browser that supports raw Dart, so I am forcing ICE users on http://gamingjs.com/ice-beta to download a bunch of files for no reason whatsoever. Sorry about that.

Application cache beasts, like the ICE Code Editor, need a manifest file on the document's <html> tag:
<!DOCTYPE html>
<html manifest="editor.appcache">
  <head>
    <title>code editor</title>
    <meta charset="utf-8">
    <script src="appcache.js"></script>
    <script src="packages/browser/dart.js"></script>
    <script src="packages/browser/interop.js"></script>
    <script src="main.dart" type="application/dart"></script>
  </head>
  <body>
  </body>
</html>
This manifest file informs browsers what they need to down for the application to function properly:
CACHE MANIFEST
# 2013-07-10

CACHE:
/favicon.ico
/Three.js
/Tween.js
/Detector.js
/physi.js
/Mouse.js
/Scoreboard.js
/ChromeFixes.js
/ammo.js
/physijs_worker.js

part.js
main.dart
index.html
main.dart.js.map
main.dart.js
packages/js/js.dart
packages/js/src/wrapping/js/object_to_map_adapter.dart
# Many, many, many more files...

NETWORK:
*
The asterisk in the NETWORK section of my manifest says that anything not explicitly listed in the CACHE session should downloaded over the network. I really ought to allow all of the Dart code, along with any supporting resources, to get served up over the network. I can still leave them on the server, in case a Dartium browser comes along, but not place it in application cache.

After excluding a bunch of files, I get my list down from 100 to 33:
➜  ice-beta git:(gh-pages) ✗ cat editor.appcache | grep -v \\.dart$ | \
  grep -v pubspec\\. | \
  grep -v unittest | \
  grep -v inconsolata | \
  grep \\. | \
  wc -l
33
That is much, much better because, as everyone knows, individual files have a much greater impact on download speeds than do large files.

I find that I can generate the same list of worthy application files with a grep statement that whitelists HTML, JavaScript, and source map files:
➜  ice-beta git:(gh-pages) ✗ find | \
  grep -e \\.js$ -e \\.map$ -e \\.html$ -e \\.css$ | \
  grep -v -e unittest -e /lib/ | \
  wc -l           
22
There is some duplication in the lib/ sub-directory that I do not fully understand (it may just be how I coped it last night). That is something for another day. For now I ignore them in the grep -v (ignore) statement.

I am explicitly ignoring the .deps file that dart2js generates. I should probably dot-gitignore that file—it includes local file URLs:
file:///home/chris/local/dart/dart-sdk/lib/_collection_dev/arrays.dart
file:///home/chris/local/dart/dart-sdk/lib/_collection_dev/collection_dev.dart
...

I do need to manually add the main.dart script to the application cache manifest—the browser does request it even if it does not know how to process it (because it is in a <script> tag in the HTML). With those changes, my entire manifest file contains:
CACHE MANIFEST
# 2013-07-20

CACHE:
/favicon.ico
/Three.js
/Tween.js
/Detector.js
/physi.js
/Mouse.js
/Scoreboard.js
/ChromeFixes.js
/ammo.js
/physijs_worker.js

main.dart

part.js
index.html
main.dart.js.map
main.dart.js
packages/js/dart_interop.js
packages/browser/interop.js
packages/browser/dart.js
packages/ice_code_editor/js/deflate/rawinflate.js
packages/ice_code_editor/js/deflate/rawdeflate.js
packages/ice_code_editor/js/ace/mode-css.js
packages/ice_code_editor/js/ace/ace.js
packages/ice_code_editor/js/ace/theme-textmate.js
packages/ice_code_editor/js/ace/theme-chrome.js
packages/ice_code_editor/js/ace/ext-searchbox.js
packages/ice_code_editor/js/ace/mode-html.js
packages/ice_code_editor/js/ace/worker-javascript.js
packages/ice_code_editor/js/ace/keybinding-emacs.js
packages/ice_code_editor/js/ace/mode-javascript.js
packages/ice_code_editor/css/ice.css
packages/ice_code_editor/html/preview_frame.html
part.js.map
appcache.js

NETWORK:
*
After pushing that to http://gamingjs.com/ice-beta and firing up a new incognito window, I find:


18 requests  ❘  103 KB transferred  ❘  1.39 s (load: 1.37 s, DOMContentLoaded: 187 ms)

That is a 16% decrease in load time when compared to the previous Dart version of ICE, but it is still 30% slower than the old pure JavaScript version of ICE.

Ultimately it does not matter too much. I am still looking at a load time of less than 1.5 seconds for the first access, which is pretty darn good. Subsequent access will be even better since it never even touches the the network—the benefits of being stored in application cache. The dart2js tool is only going to make smaller compiled JavaScript, though I would guess that the changes from here on out will be smaller—that we will not see compiled Dart smaller by many orders of magnitude.

And if I find that I need much better, there is always SPDY!


Day #818

Friday, July 19, 2013

Compiling Dart for GitHub Pages: The Revenge


Thanks to Jon Kirkman, who took last night's #pairwithme slot, we have the first keyboard shortcuts in the ICE Code Editor.

As the last feature that was slated for the Dart version of ICE, I think it is time to promote it to production at http://gamingjs.com/ice. For the past month, the beta version has resided at http://gamingjs.com/ice-beta and performed quite well by all accounts. I would like to push the keyboard shortcut commit to beta and will move to production tomorrow. But first…

I am going to revisit how I deploy the application. Nothing major here, there are two nagging problems that I hope to address. First, when I first pushed the Dart version out, I found that the minify option on the dart2js tool did not work with my code (I suspect it could not work with the js-interop code). The other problem is that I may be packing too much in my application cache manifest file.

I start with dart2js. I grab the most recent SDK from the Dart homepage:
➜  ice-code-editor git:(master) dart --version
Dart VM version: 0.6.5.0_r25017 (Mon Jul 15 15:01:03 2013) on "linux_x64"
In my gamingjs.com github repository, which is a GitHub pages repository, I update the package dependencies with Pub:
➜  ice-beta git:(gh-pages) ✗ pub update
Resolving dependencies...........
Downloading ice_code_editor 0.0.9 from hosted...
Dependencies updated!
Since this is going to be installed on GitHub pages, the normal symbolic links in Pub's packages directory are not going to work:
➜  ice-beta git:(gh-pages) ✗ cd packages
➜  packages git:(gh-pages) ✗ ls -l
total 24
lrwxrwxrwx 1 chris chris 64 Jul 19 22:01 browser -> /home/chris/.pub-cache/hosted/pub.dartlang.org/browser-0.6.5/lib
lrwxrwxrwx 1 chris chris 63 Jul 19 22:01 crypto -> /home/chris/.pub-cache/hosted/pub.dartlang.org/crypto-0.6.5/lib
lrwxrwxrwx 1 chris chris 72 Jul 19 22:01 ice_code_editor -> /home/chris/.pub-cache/hosted/pub.dartlang.org/ice_code_editor-0.0.9/lib
lrwxrwxrwx 1 chris chris 60 Jul 19 22:01 js -> /home/chris/.pub-cache/hosted/pub.dartlang.org/js-0.0.24/lib
lrwxrwxrwx 1 chris chris 61 Jul 19 22:01 meta -> /home/chris/.pub-cache/hosted/pub.dartlang.org/meta-0.6.5/lib
lrwxrwxrwx 1 chris chris 65 Jul 19 22:01 unittest -> /home/chris/.pub-cache/hosted/pub.dartlang.org/unittest-0.6.5/lib
I use my editor (Emacs) to convert that listing to a list of cp commands needed to convert then to real files:
➜  packages git:(gh-pages) ✗ rm *
➜  packages git:(gh-pages) ✗ cp -r /home/chris/.pub-cache/hosted/pub.dartlang.org/browser-0.6.5/lib browser
➜  packages git:(gh-pages) ✗ cp -r /home/chris/.pub-cache/hosted/pub.dartlang.org/crypto-0.6.5/lib crypto
➜  packages git:(gh-pages) ✗ cp -r /home/chris/.pub-cache/hosted/pub.dartlang.org/ice_code_editor-0.0.9/lib ice_code_editor
➜  packages git:(gh-pages) ✗ cp -r /home/chris/.pub-cache/hosted/pub.dartlang.org/js-0.0.24/lib js
➜  packages git:(gh-pages) ✗ cp -r /home/chris/.pub-cache/hosted/pub.dartlang.org/meta-0.6.5/lib meta
➜  packages git:(gh-pages) ✗ cp -r /home/chris/.pub-cache/hosted/pub.dartlang.org/unittest-0.6.5/lib unittest
I really need to write a tool to do this, but I will leave that for another day.

For now, I compile the ICE application down into JavaScript using the --minify option:
➜  ice-beta git:(gh-pages) ✗ dart2js --minify -omain.dart.js main.dart
I fire up a Jekyll server in my GitHub pages repository root:
➜  gamingjs git:(gh-pages) ✗ jekyll --auto --server
Configuration from /home/chris/repos/gamingjs/_config.yml
Auto-regenerating enabled: /home/chris/repos/gamingjs -> /home/chris/repos/gamingjs/_site
[2013-07-19 22:14:41] regeneration: 474 files changed
[2013-07-19 22:14:41] INFO  WEBrick 1.3.1
[2013-07-19 22:14:41] INFO  ruby 1.9.3 (2012-04-20) [x86_64-linux]
[2013-07-19 22:14:41] WARN  TCPServer Error: Address already in use - bind(2)
[2013-07-19 22:14:41] INFO  WEBrick::HTTPServer#start: pid=18249 port=4000
Then I fire up a non-Dart enabled browser (Chrome) to find...

It works!



There are no errors and the editor is working (keyboard shortcuts and all). It really does seem that, if something in the Dart world is not working properly, the best strategy is to wait a day or two.

Not only is it working, but the compiled main.dart.js file has gone from 910 KB to 312 KB. Obviously, that could be better, but it is much better than before. Plus it will be served with gzip encoding and will be app-cached so this is really exciting.

This seems a good stopping point for tonight. I will pick back up with improving my application cache deployment strategy (and possibly start tackling the automation of these things) tomorrow. For now, I can go to bed happy. Yay!


Day #817

Thursday, July 18, 2013

HttpClient for Testing HTTP Services in Dart


I have gotten reasonably adept at using HttpRequest in Dart unit tests to make actual connections to test servers. But that is the HttpRequest class from the dart:html library, not in dart:io.

The HttpRequest class in dart:io is very different from the class of the same name in dart:html—the primary difference being that it is impossible to directly instantiate an HttpRequest object in dart:io. The server-side HttpRequest, which lives in the dart:io library, is only for encapsulating server requests. As such, only an HttpServer can create a dart:io HttpRequest.

Since the dart:io HttpRequest encapsulates the request that a server sees, it cannot be used to establish a new connection to a server like its dart:html counterpart (which replaces the venerable XMLHttpRequest in Dart). So the question is, how do I write a unit test in dart:io (i.e. doesn't use a web browser context)?

I believe that the answer is with the HttpClient class.

What I hope to do is start the server that I am trying to test in a setup block. Once that is ready, then I would like to test the /stub resource with HttpRequest.

To get the setup working, I need a Future to tell me when the server is ready. For that, I need my library's main() method to return the result of the server's bind() method, which is a future that completes when the server is ready:
library plumbur_kruk;
// imports and variable declaration...
main() {
  return HttpServer.bind('127.0.0.1', 31337)..then((app) {
    // do server stuff here...
  });
}
I use the method cascade operator (the “..”) to invoke then() on the future returned from the bind() call. Instead of returning the result of the then() call, the method cascade returns the object whose method is being invoked. In other words, I return the same Future on which this then() is being invoked.

Back in my test, I create a test group that runs this main() method:
  group("Running Server", (){
    var server;
    setUp((){
      return PlumburKruk.main()
        ..then((s){ server = s; });
    });
    // tests go here...
  });
I use the same method cascade technique here that I used in the actual server. Once the main() function's server is running, then I assign a local variable server to the server passed from the bind() method's future. More importantly, I return the same future from the setUp() block. In Dart unit tests, returning a future from setUp() blocks the tests from running until the future completes. In this case, my tests will block until the server is ready, which is exactly what I want.

Once the server is up and running, I can write my test. I create a POST request from the HttpClient. To create the POST's body content, I have to use the future returned from the client to write() the body:
  group("Running Server", (){
    // setup...
    test("POST /stub responds successfully", (){
      new HttpClient().
        postUrl(Uri.parse("http://localhost:31337/stub")).
        then((request) {
          request.write('{"foo": 42}');
          return request.close();
        });
    });
  });
I am still not testing anything here, but I have sent the request to the server and am ready to set the expectation that will test things.

And here, I find something new—that a then() call on a future returns another future. It makes sense, but I had never really given it much thought. I have always performed some asynchronous action, then performed a single action when it was done.

In this case, I need to wait for the HttpClient() to open a connection at the specified URL, then post some data, and once that is complete, then I do what I want with the response:
  group("Running Server", (){
    var server;
    setUp((){
      return PlumburKruk.main()
        ..then((s){ server = s; });
    });

    tearDown(() => server.close());

    test("POST /stub responds successfully", (){
      new HttpClient().
        postUrl(Uri.parse("http://localhost:31337/stub")).
        then((request) {
          request.write('{"foo": 42}');
          return request.close();
        }).
        then(expectAsync1(
           (response) {
             expect(response.statusCode, 204);
           }
        ));
    });
  });
I use the normal expectAsync1() in the final then() to block the test until the expected asynchronous call with one argument (the response in this case) fires.

Just like that, I have another passing test against my test server:
➜  plumpbur-kruk git:(master) ✗ dart test/server_test.dart
unittest-suite-wait-for-done
PASS: Core Server non-existent resource results in a 404
PASS: Running Server POST /stub responds successfully

All 2 tests passed.
unittest-suite-success
Yay!

I like HttpClient, though I do wish that I could specify the POST body in the constructor or in the post() method. I still dislike the two different HttpRequest classes as it is easy to find myself looking at the wrong documentation. Nevertheless, I think that I am getting the hang of all this.


Day #816

Wednesday, July 17, 2013

Mock HttpRequests for Testing Dart Servers


I have not done much server-side testing in Dart yet. Tonight seems like as good a time as any to kick the tires.

Unlike client-side testing in Dart, there are a number of solid testing options available to me. The most obvious is to actually run the server and verify that the responses returned are what I expect. The other option is to convert the server.dart file into a Dart library so that the individual functions and classes can be probed directly. I will try the latter tonight since it just seems more fun.

To import a library for test, the thing being imported needs to be a… library. To convert test_server.dart into a library, I need only add the library statement to the top of the file:
library plumbur_kruk;
// imports and variable declarations...
main() {
  HttpServer.bind('127.0.0.1', 31337).then((app) {
    app.listen((HttpRequest req) {
      log(req);
      // handle requests here...
      notFoundResponse(req);
    });
  });
}
The library name does not matter as long as it is alphanumeric and underscores.

With that, I write a test that imports my new library:
library plumbur_kruk_test;

import 'package:unittest/unittest.dart';
import 'dart:io';

import 'test_server.dart' as PlumburKruk;

main(){
  group("Core Server", (){
    test("non-existent resource results in a 404", (){
      var req = new HttpRequest();
    });
  });
}
In the main() entry point, I have written my first test that verifies that I can get a 404 response back from the non-existent request handling code. Actually, there is no real test there—I am just wondering if I can create a new HttpRequest object.

In fact, I cannot:
➜  plumpbur-kruk git:(master) ✗ dart test/server_test.dart
unittest-suite-wait-for-done
ERROR: Core Server non-existent resource results in a 404
  Test failed: Caught Cannot instantiate abstract class HttpRequest: url 'file:///home/chris/repos/plumpbur-kruk/test/server_test.dart' line 15
  core_server_tests.<anonymous closure>.<anonymous closure>   file:///home/chris/repos/plumpbur-kruk/test/server_test.dart 15:21
...
Bah! I am on the fence, but I do not think that I like this. It bothers me a little that I cannot directly create this object. It bothers me more that I can create an HttpRequest instance on the client side.

In this case, I am getting the HttpRequest class from the dart:io library. When I code Dart in the browser, a different HttpRequest class comes from the dart:html library. HttpRequest from dart:io is meant to handle HTTP requests in a server environment. HttpRequest from dart:html is a replacement for the venerable XMLHttpRequest object for performing so-called AJAX operations.

I cannot create instances of HttpRequest from dart:io—only a server's listen stream can do that. It is still possible to make HTTP requests from the command-line in Dart—the HttpClient class does that. Naturally, it does not have an HttpRequest object for me to use, preferring to expose them in futures.

Basically, I am stuck. I cannot create an HttpRequest object for my server test. But I can create something the quacks like an HttpRequest duck. Rather than building a fake class manually, I am going to give the mock library a go. I may be trying one too many new things at this point, but what the hey?

The mock library is already included in the unittest library, so there is no need to update my pubspec.yaml. It already depends on unittest:
name: plumbur_kruk
#...
dependencies:
  unittest: any
Since I already have unittest there is also no need to install with Dart Pub.

I should be good to create a mock instance of HttpRequest. I update my test to import the mock library, then I create a MockHttpRequest as implementing HttpRequest, and finally create an instance of this mock class:
library plumbur_kruk_test;

import 'package:unittest/unittest.dart';
import 'package:unittest/mock.dart';
import 'dart:io';

import 'package:plumbur_kruk/server.dart' as PlumburKruk;

class MockHttpRequest extends Mock implements HttpRequest {}

main(){
  group("Core Server", (){
    test("non-existent resource results in a 404", (){
      var req = new MockHttpRequest();
    });
  });
}
And that works. Or at least it compiles and successfully creates an instance of MockHttpRequest:
➜  plumpbur-kruk git:(master) ✗ dart test/server_test.dart
unittest-suite-wait-for-done
PASS: Core Server non-existent resource results in a 404

All 1 tests passed.
unittest-suite-success
Of course, I am not really testing anything. I am going to need a second mock class to test the response—a MockHttpResponse class:
class MockHttpRequest extends Mock implements HttpRequest {}
class MockHttpResponse extends Mock implements HttpResponse {}
To make use of this, my test gets a little more complicated. I start by creating a mock HttpResponse instance and a mock HttpRequest instance:
      var response = new MockHttpResponse();
      var req = new MockHttpRequest()
        ..when(callsTo('get response')).alwaysReturn(response);
In addition to creating the mock request object, I give it some behavior. I tell it that, when it sees calls to the response getter, it should always return my mock HTTP response object. This syntax feels a little awkward, but it is fairly readable.

Next, I call the noResponseFound() method in my actual test server with the mock request object:
      PlumburKruk.notFoundResponse(req);
Last, I need to check the mock request object's “logs” to see how many times the statusCode setter was told to assign a 404 value:
      response.
        getLogs(callsTo('set statusCode', 404)).
        verify(happenedOnce);
Here, I am verifying that, according the logs that the mock hold, the setter was called once with a value of 404.

In the actual server method, it really is only set once:
notFoundResponse(req) {
  HttpResponse res = req.response;
  res.statusCode = HttpStatus.NOT_FOUND;
  res.close();
}
So, if I have done this correctly, my test should pass:
➜  plumpbur-kruk git:(master) ✗ dart test/server_test.dart
unittest-suite-wait-for-done
PASS: Core Server non-existent resource results in a 404

All 1 tests passed.
unittest-suite-success
Yay! That seems legit. To be sure, I temporarily break the notFoundResponse() function to set a 200 status code:
notFoundResponse(req) {
  HttpResponse res = req.response;
  res.statusCode = HttpStatus.OK;
  res.close();
}
And, that does indeed cause a test failure:
➜  plumpbur-kruk git:(master) ✗ dart test/server_test.dart
unittest-suite-wait-for-done
FAIL: Core Server non-existent resource results in a 404
  Expected set statusCode(<404>) to be called 1 times
       but: was called 0 times. 
  LogEntryList.verify                               package:unittest/mock.dart 568:11
  main.<anonymous closure>.<anonymous closure>      file:///home/chris/repos/plumpbur-kruk/test/server_test.dart 23:15
...
I am pretty excited to finally have put the mock library in Dart to some good use—all the more so because the resultant test is pretty solid. It would still make some sense to contrast this approach with a real HTTP client and server, which I will likely tackle tomorrow.


Day #815

Tuesday, July 16, 2013

TearDown Asynchronous Tests in Dart


Bah! I almost had my ridiculous, pseudo-stubbing solution working last night. And then I tried it one too many times.

Dart is a fantastic language that mixes aspects of both dynamic languages and static languages. One of the places that static wins is the ability to replace methods at runtime—it won't work in Dart. It is still possible to vary runtime behavior (with noSuchMethod() or assigning a property to a function). But once a method is declared, there is nothing that can change it.

What this means for me in my recent quest is that there is no way to stub HttpRequest actions in my tests. I either need to use something that is HttpRequest-like (e.g. a subclass) or use a real server. Lately, I have been focusing on the latter. I even got the bright idea to run a test server that accepted POSTs to the /stub resource so that subsequent requests would return a specified response. In effect I would be stubbing HttpRequest in the most roundabout way possible.

And it worked. Kind of.

With a bit of browser and server code, I was able to write a “stub” test as:
    group("HTTP get", (){
      setUp((){
        return HttpRequestX.respondWith('{"foo": 42}');
      });

      test("it can parse responses", (){
        var model = new FakeModel();
        HipsterSync.
          call('get', model).
          then(
            expectAsync1((response) {
              expect(response, {'foo': 42});
            })
          );
      });
    });
Most of the time, that passes. But every now and then I will get a 404/Not Found response in the actual test.

To track this down, I add some logging to my Dart server:
main() {
  HttpServer.bind('127.0.0.1', 31337).then((app) {
    app.listen((HttpRequest req) {
      log(req);
      // handle actual requests below...
    });
  });
}
I will not be interfering with the response in the log() function. Instead, I wait for the response to be done, at which point I log a some pertinent information:
log(req) {
  req.response.done.then((res){
    var now = new DateTime.now();
    print('[${now}] "${req.method} ${req.uri.path}" ${res.statusCode}');
  });
}
I love me some good Dart code. In there, I get to make use of a Future, sane date objects, and string interpolation. That's just pretty. The done property on the response is a future—the callback that I supply in then() is invoked when the response is… done. The now() factory constructor gives me the current time in an object that supports a sane toString(). That comes in handy when I interpolate a bunch of values into the logged string.

With that, I see that my normal activity looks like:
[2013-07-16 22:55:02.292] "DELETE /widgets/ALL" 204
[2013-07-16 22:55:02.294] "POST /stub" 204
[2013-07-16 22:55:02.301] "GET /test" 200
[2013-07-16 22:55:02.305] "DELETE /widgets/ALL" 204
The POST to /stub specifies the stubbed response that I am going to get back on the next request. The subsequent GET of /test returns the data POSTed to /stub. The DELETEs before and after are teardown functions being executed in between tests (they clear the DB for tests that use them).

One thing that strikes me in the log output is that the first DELETE and the POST to /stub are only 2 milliseconds apart. My tests are asynchronous. If there is a tiny delay when connecting for the first DELETE, but no delay when connecting for the POST to /stub, what happens?

It turns out that I can make that happen on the very next test run:
[2013-07-16 22:55:06.017] "POST /stub" 204
[2013-07-16 22:55:06.020] "DELETE /widgets/ALL" 200
[2013-07-16 22:55:06.034] "GET /test" 404
Since the POST to /stub arrives before the DELETE teardown, the response to the DELETE request is the stubbed data. When the test that expects a stubbed response finally hits the server, the stub is gone (currently, it is a one-time only stub), resulting in a 404 and a failing test.

So culprit identified, but how do I fix it? The fix is something that I have been wanting to do for a while: return a future from a test's teardown function. I already figured out the benefit of returning futures in setup blocks—the test will block until the setup future completes. I knew that I could return a Future from unittest tearDown() functions, but until tonight, I lacked a good use-case.

The tearDown() function in this case is “fixed” by simply adding a return statement to my Hipster MVC test. HipsterSync.call() returns a future, so returning it will return a future from tearDown():
    tearDown(() {
      // ...
      return HipsterSync.call('delete', model);
    });
The use-case seems to be that other tests in the same group will not start running until the previous teardown is complete. And this seems to be the case. Despite repeated efforts, I cannot get the timing issue to reappear even though the first DELETE and the stub are often a single millisecond apart:
[2013-07-16 23:47:47.178] "DELETE /widgets/ALL" 204
[2013-07-16 23:47:47.179] "POST /stub" 204
[2013-07-16 23:47:47.187] "GET /test" 200
[2013-07-16 23:47:47.193] "DELETE /widgets/ALL" 204
This even seems to work cross-test groups. I make a duplicate of the test that had been failing—the stubbing test—in a separate group. And it still works despite repeated test runs. The second stub always occurs at the very end—after the previous group has run.
[2013-07-16 23:57:50.904] "DELETE /widgets/ALL" 204
[2013-07-16 23:57:50.905] "POST /stub" 204
[2013-07-16 23:57:50.911] "GET /test" 200
[2013-07-16 23:57:50.916] "DELETE /widgets/ALL" 204
... other test activity ...
[2013-07-16 23:57:50.972] "DELETE /widgets/ALL" 204
[2013-07-16 23:57:50.975] "POST /stub" 204
[2013-07-16 23:57:50.978] "GET /test" 200
[2013-07-16 23:57:50.981] "DELETE /widgets/ALL" 204
So maybe this will work after all. This whole pseudo-stubbing exercise still feels a bit daft. Still, it may prove useful in certain circumstances so I will probably explore it a bit more. But it just might work.


Day #814