Sunday, May 4, 2014

Thankful for a Large Test Suite (Returning to a Large Project)


I have 12 errata reported on Dart for Hipsters. That needs to get fixed darn near immediately.

Most of the errata appear on the face of it to be mistakes or suggestions for improvement on the narrative rather than technical problems, but I will start with a quick review of the code nevertheless. More specifically, the tests. There are 160+ unit tests (which seems rather low) against code in the book. Happily, they all still pass—even with the 1.3 version of Dart:
CONSOLE MESSAGE: PASS: [skeleton] the skeleton app returns OK
CONSOLE MESSAGE: PASS: [main] the app returns OK
CONSOLE MESSAGE: PASS: [main] populates the list
CONSOLE MESSAGE: PASS: [numbers] 2 + 2 = 4
...
CONSOLE MESSAGE: PASS: [canvas] code runs
CONSOLE MESSAGE: PASS: [canvas] player is drawn
CONSOLE MESSAGE: 
CONSOLE MESSAGE: All 163 tests passed.
As I mentioned, I will likely need to write a few more tests before this next edition of the book, but before I get to that, not all is sunshine and roses with my test suite: the Dart analyzer is flagging some unexpected warnings:
Static type analysis (expecting 5 warnings)...
Analyzing test.dart...
[warning] There is no such getter 'function' in 'InstanceMirror' (/home/chris/repos/csdart/Book/code/functional_programming/first_order.dart, line 69, col 28)
[warning] The method 'bar' is not defined for the class 'Foo' (/home/chris/repos/csdart/Book/code/functional_programming/first_order.dart, line 120, col 19)
[warning] The method 'bar' is not defined for the class 'Foo' (/home/chris/repos/csdart/Book/code/functional_programming/first_order.dart, line 126, col 29)
[warning] A value of type 'int' cannot be assigned to a variable of type 'String' (/home/chris/repos/csdart/Book/code/primitives/types.dart, line 34, col 16)
[warning] The argument type 'int' cannot be assigned to the parameter type 'String' (/home/chris/repos/csdart/Book/code/primitives/types.dart, line 42, col 20)
[warning] 0 positional arguments expected, but 1 found (/home/chris/repos/csdart/Book/code/varying_the_behavior/test/calling_methods_from_no_such_method.dart, line 22, col 17)
[warning] 0 positional arguments expected, but 1 found (/home/chris/repos/csdart/Book/code/varying_the_behavior/test/calling_methods_from_no_such_method.dart, line 26, col 16)
7 warnings found.

Unexpected warnings found. There should be 5 and only 5.
As the last line indicates, 5 of those warnings are expected. There are a few intentional errors in the text to support some of the narrative. I have yet to find a way to indicate that type mistakes are intentional (or at least not an error), so I have to rely on my test_runner.sh to count the warnings from dartanalyzer:
# 2 in primitives/types.dart
# 2 in varying_the_behavior/test/calling_methods_from_no_such_method.dart
# 1 in varying_the_behavior/public/scripts/HipsterSync.dart (TODO: bug?)
if [[ "$results" != *"5 warnings found."* ]]
then
    echo
    echo "Unexpected warnings found. There should be 5 and only 5."
    exit 1
fi
I am encoding the known errors in comments above. I may switch that over to grep-remove known warnings on specific line numbers, but for now I note that something curious has happened. There is no longer a “HipsterSync” warning so that bug looks to have been addressed by the language. That leaves 3, not 2, extra warnings that I need to investigate—all in my functional_programming/first_order.dart Dart file that supports the discussion of functional programming in Dart.

So what then heck did I do in there? It turns out that the code generating these warnings is not used in the actual book. It was done back when I was exploring currying functions some time ago. I thought to include some of that in the book, but thought better of it (the approach is a little awkward). Still, the book mentions the ability to curry, which is probably why I included the code in the book's test suite.

Anyhow, the first error is a simple type error. I reflect on a value, but am not explicit about the expected return value:
  _curry(args) {
    var fn = args[0];
    var bound = args.sublist(1);
    var size = reflect(fn).function.parameters.length;
    // ...
 }
Since this code supports currying, I am reflecting on the values submitted to the curry function trying to determine the number of functions still outstanding. To fix, the particulars of the function do not matter much, the problem is that reflect(fn) returns a generic InstanceMirror. Since I am reflecting on a function, I will really get back a ClosureMirror, but dartanalyzer has no way to know that—unless I tell it:
  _curry(args) {
    var fn = args[0];
    var bound = args.sublist(1);
    ClosureMirror cm = reflect(fn);
    var size = cm.function.parameters.length;
    // ...
  }
That takes care of one of my currying warning, but leaves me with two others—both related. I have to confess, I am not sure what I was thinking with these two. My best guess is that it was a work-in-progress approach to either a full currying solution or something I thought worth presenting in the book:
    test("Curry3", (){
      expect(
        new Foo().bar(1)(2)(3),
        6
      );
    });
Looking at the Foo class, which is defined in the test, it seems like I was, perhaps, manually trying to build a curried method as a proof of concept:
class Foo {
  noSuchMethod(args) {
    if (args.positionalArguments.length == 1)
      return (x) {
        return (y) {
          var _args = [x, y]..addAll(args.positionalArguments);
          return Function.apply(add, _args);
        };
      };
  }
}
I am using Dart's noSuchMethod() to return a function that takes one argument and returns another function that takes one argument and itself returns a function. Like I said, I am not really sure why I am doing this, but I am sure that my immediate Dart analysis problem is that the Foo class does not have a bar() method, relying instead on noSuchMethod() to provide its functionality.

I really cannot decide whether I want to remove this outright from the book's test suite or replace it with the curry package for Dart. For now, I just mark Foo as a “proxy” class so that dartanalyzer will assume that noSuchMethod() (which must be defined for this annotation to be used) will support the methods being called:
@proxy
class Foo {
  noSuchMethod(args) {
    if (args.positionalArguments.length == 1)
      return (x) {
        return (y) {
          var _args = [x, y]..addAll(args.positionalArguments);
          return Function.apply(add, _args);
        };
      };
  }
}
In other words, I have no more warning about bar() not being defined. With that (and accounting for the language bug fix that addressed one of my analysis warnings), I have a fully passing test suite:
...
CONSOLE MESSAGE: PASS: [canvas] code runs
CONSOLE MESSAGE: PASS: [canvas] player is drawn
CONSOLE MESSAGE: 
CONSOLE MESSAGE: All 163 tests passed.
CONSOLE MESSAGE: unittest-suite-success

Static type analysis (expecting 4 warnings)...
Analyzing test.dart...
[warning] A value of type 'int' cannot be assigned to a variable of type 'String' (/home/chris/repos/csdart/Book/code/primitives/types.dart, line 34, col 16)
[warning] The argument type 'int' cannot be assigned to the parameter type 'String' (/home/chris/repos/csdart/Book/code/primitives/types.dart, line 42, col 20)
[warning] 0 positional arguments expected, but 1 found (/home/chris/repos/csdart/Book/code/varying_the_behavior/test/calling_methods_from_no_such_method.dart, line 22, col 17)
[warning] 0 positional arguments expected, but 1 found (/home/chris/repos/csdart/Book/code/varying_the_behavior/test/calling_methods_from_no_such_method.dart, line 26, col 16)
4 warnings found.

Looks good!
Time to dive into those errata!



Day #54


No comments:

Post a Comment