Tuesday, February 26, 2008

Horrible...

Everything I said about templates? Double for mixins.

A short mixin can be a wonderful thing. Like for unittests:

string test(string name)()
{
return `
Stdout("` ~ name ~ `");
scope (failure) Stdout("failed").newline;
scope (success) Stdout("passed").newline;`
}
unittest
{
mixin(test!("test naming test"));
}


But mixins should be passive, delegating to concrete methods and classes whenever possible. Line numbers are that important.

Monday, February 18, 2008

New project woes

Got a university project that's decidedly not Agile. And I don't have any time to do requirements analysis in this weakly waterfall style. If it were Agile, I'd only have time for about four or five iterations....

It's tempting to ignore the customers at this point and just code up an initial project that fulfills some need. Then I'd have something to talk about. The one source I have wants the system to have insane amounts of detail -- if I asked him to fill it out, he'd refuse, I think. And there's only one person in the department who would benefit from the information.

Sunday, February 17, 2008

CTFE and testability

A lot of my code makes heavy use of CTFE. Really heavy. As in, more lines of code get executed at compile time than at runtime. How do I start to write unittests for it?

Answer: I don't.

That's rather harsh. Let's look at one case, or actually an abbreviated form of it. This is taken from DMocks:


string Methods (T, string name) () {
string methodBodies = "";
foreach (method; __traits(getVirtualFunctions, T, name)) {
alias typeof(method) func;
static if (is(ReturnType!(func) == void)) {
methodBodies ~= VoidMethod!(T.stringof, name, ParameterTypeTuple!(func));
} else {
methodBodies ~= ReturningMethod!(
T.stringof,
name,
ReturnType!(func),
ParameterTypeTuple!(func));
}
}
return methodBodies;
}

string ReturningMethod (string type, string name, T, U...)() {
string qualified = type ~ `.` ~ name;
string args = String!(U)();
string argArgs = Arguments!(U);
string nameArgs = `"` ~ qualified ~ (U.length == 0 ? `"` : `", ` ~ Arguments!(U)());
string retArgs = T.stringof ~ (U.length == 0 ? `` : `, ` ~ args);
return `some very long string that uses those strings I just assigned above`;
}
// VoidMethod looks pretty much like ReturningMethod.


That is terribly ugly, of course. If I could do it at runtime, I could introduce about two classes for the first template, then use tango.text.convert.Layout and public static format strings to deal with ReturningMethod. I'd do it with a format string looking like this:


public const string methodOutline = `{0} {1} ({2}) {{{
{{0}}
}}}`;
public const string methodBodyPart1 = `{0} {1} ...`;
public const string methodBodyPart2 = `{0} {1} ...`; // and so on


Of course, Layout won't work at compile time. Even if it did, I'd be left with a glob of functions to go through for my "unit" tests. But at that rate, I might as well complete the integration test by running the code that mixes in this giant string.

So I've been thinking of how to do it. The answer lies in two parts:

  1. Write a compile-time string formatting function.

  2. Implement any method I want to test as a string that I mix in.



The former makes it much easier to make sure the result string has the right arguments in the right place. The latter means I can test each in isolation by providing default implementations of templates that I'm not testing.

But for now, I've got some ugly, unmaintainable blobs of code to rewrite. The joy.

Continuous Integration for D

You don't need to use Agile to benefit from a Continuous Integration server. The basic idea is, you've got a server that checks out your code whenever there are updates, builds it according to a build script, and (almost always) runs some scripted tests on it.

I'm pondering how to set one up for D, using a daemon of some sort. The approach looks something like this:


  • A config file with the repository information and where to check it out to.

  • A DSSS file in the root of the repository.

  • A list of directories that contain tests. These should only contain tests; perhaps an exclude option for files and directories that contain helper methods.



The build process isn't that exciting:

  • Check for updates; if none, sleep.

  • Run dsss build.

  • Recursively go through the test directories, running rebuild on each .d file and executing the resulting executable.

  • Publish the results.



This requires that each test file contain a main() method. However, it has the benefit that the main executable does not contain unit tests, enforcing or at least encouraging separation of concerns and design practices that do require tests to have private access to the tested modules.

Ugh.

Stack traces are beautiful. If you are ever in control of a standard library, make sure your exceptions have stack traces.

I'm trying to do TDD without them, and that means inserting a lot of tracing code, which is a bad sign.

Saturday, February 16, 2008

Did it...

Got the assertions stuff done, at least the basic stuff. The list stuff is annoying -- and not fully tested -- but should work with with builtin arrays (which, in D, are actually resizeable, sliceable, and everything you'd expect from a thorough library implementation) as well as just about any collection class that Tango contains.

The annoying part is formatting everything without taking mounds of code for it. I've got that working, aside from negation.

Now I'm working on test cases for everything else I've written. It's much easier to build stuff with Descent for an IDE and Rebuild, but still difficult enough that it's difficult to support a proper TDD model.

Hi!

The intent of this blog is to encourage me to code. As such, I may update infrequently or say stuff that seems quite inane. Also, most of my posts will relate strongly to my code and be irrelevant for most other people.

My code, since I've mentioned it, is mostly in the D programming language. I've got a collection of libraries called Felt in various stages of development, aiming at being a Castle clone. Or maybe I'm trying to catch up with Ayende @ Rahien. The code's all at the Felt dsource page.

Anyway, I'll catch up with you later.

felt assertions

I'm working on an assertions library right now. Library? Well, just a couple files, two structs, an exception class, and a half dozen functions.

It's in the D programming language. It depends on Tango Variant, which can actually handle structs of nontrivial sizes. Phobos Variant can't. And it should work with D1 and D2.

I need to get my hands on Tango for D2. Even if Phobos were decent, I can't afford to write so many version statements. It just isn't worth my time. But the reflection capabilities introduced in D2 are great.

The final product will look something like this:
expect(4).equals(4);
expect([1, 2, 3, 4]).has(2).where((int x) { return x % 2 == 0; });

// should be 2 where the constraint is false
expect([1, 2, 3, 4]).has(2).not.where((int x) { return x % 2 == 0; });

// should be other than two where the constraint is false
expect([1, 2, 3, 4]).not.has(2).where((int x) { return x % 3 == 0; });
expect([1, 2, 3, 4]).hasOne.sameAs(2);
expect(true);
expect(null).isNull;
o = new Object;
auto p = o;
expect(o).sameAs(p);
p = new Object;
expect(p).not.sameAs(o);

It'll be ready some time today. And then I should stop slacking and get Felt.Sleeper working. Or fix interfaces with dmocks.