Monday, September 16, 2013

An Unexpected Follow-Up to Testing Dart Isolates


As I work through the latest rewrite of Dart for Hipsters, I occasionally come across untested code. That's just silly.

The latest untested is in the relatively brief section on isolates in Dart. At this point, I have not run that code since the last edition earlier this year so I would be surprised if the code still runs…

Which it does.

It would seem that the isolate API is fairly stable in Dart. The example in the book is fairly contrived, though in my defense I could not bring myself to create one more Fibonnaci number calculator. My example calculates the Doomsday for a given year. As an aside, I had no idea that same person responsible for inflicting the Game of Life on unsuspecting unconferences everywhere also came up with the Doomsday rule. I can't escape him.

Anyhow, the code spawns an isolate, calls into it with a year, and then waits for the response in a future:
main() {
  SendPort sender = spawnFunction(findDoom);

  print('Certain doom awaits...');

  var year = 2013;
  sender.call(year).then((message) {
    print("Doom in $year is on a $message.");
  });
}
As mentioned, that just works:
➜  code git:(master) ✗ dart isolates/main.dart
Certain doom awaits...
Doom in 2013 is on a Thurs.
Happily, the only question left me—at least for this section—is how to test.

And wow. As I am getting ready to decide where to put my tests, I find that I have already done this. Good grief. You know that you blog too much when you don't realize that you have already covered a topic.

Anyhow, the reason that I thought this area was untested was because the tests for this chapter were not included in the book's test suite. I do so and wind up fixing a few of the future & completer tests. The isolate function, which tests spawning an isolate in the same way that my main() entry point does, still passes:
    test('can send back replies', (){
      SendPort sender = spawnFunction(Isolate.findDoom);
      expect(sender.call(2013), completion("Thurs"));
    });
That is a pretty test. I create an isolate with spawnFunction(), then I expect that calling it with 2013 will result in a future completion with the value of "Thurs." That's good stuff.

But what if I want to test the actual main() function that is included in the book? That I have not yet tested. To do so, I return the result of the then() method in main():
main() {
  SendPort sender = spawnFunction(findDoom);

  print('Certain doom awaits...');

  var year = 2013;
  return sender.call(year).then((message) {
    print("Doom in $year is on a $message.");
  });
}
I am not 100% sure that I will keep that because it might be a little awkward to explain the return statement in the narrative of the book. Well, that and it does not quite work. If I try to test it with:
    test('can perform calculations in an isolate', (){
      expect(Isolate.main(), completion("Thurs"));
    });
Then I get a null value failure:
FAIL: [isolates] can send back replies
  Expected: 'Thurs'
    Actual: <null>
     Which: <null>is not a string
I had expected that, since my then() does not return a value, that the completer would complete with the original value. It does not and I have to explicitly return a value:
main() {
  SendPort sender = spawnFunction(findDoom);

  print('Certain doom awaits...');

  var year = 2013;
  return sender.call(year).then((message) {
    print("Doom in $year is on a $message.");
    return message;
  });
}
I definitely will not be including two return statements in the narrative of the book just to satisfy my tests—even though my tests are satisfied:
PASS: [isolates] can send back replies
PASS: [isolates] can perform calculations in an isolate
Still, I would like to be able to test that main() function. I may pick back up with this tomorrow to see if I can get a more complete test covering this particular code. For now, I am happy to have my isolates and futures testing pulled into the main test suite of the book. Progress!





Day #876

No comments:

Post a Comment