Showing posts with label testing. Show all posts
Showing posts with label testing. Show all posts

2012/07/10

Dear Perl users on Windows...

I am close to giving up trying to support my Perl modules on Windows. Personally, I have nothing against it, despite all the odd quirky "not-quite-UNIX"es about it. I'm sure the same could be said in reverse, of all the "not-quite-Windows"es about the UNIXes, coming from a Windows developer.

However, I am continually annoyed at the almost complete lack of any smoke-test results ever coming from Cygwin or MSWin32 machines. I can upload a new module to CPAN and within literally hours have a dozen or so smoke-test reports from Linux and FreeBSD machines. Within a few days, a flood more results from these, and also Darwin, OpenBSD, NetBSD, Solaris. But quite often I can sit and wait weeks, if not months before I see the first result from Cygwin or MSWin32.

It's not that I'll ever actually stop looking after code - if someone were to point out a problem or send me a patch, I'm sure I could include it. It's just very hard for me as a developer without a Windows box to know if any of my code actually works on Windows, or if not, to be able to fix it. While I don't have any BSD, Solaris, or OS X boxes, I do at least have the confidence of the smoke-test reports to claim my code works there.

I must therefore conclude one of two things:

  • Nobody is using Perl, or at least my Perl modules, on Windows; or
  • Nobody using Perl on Windows cares about testing and code quality

I would really dislike having to declare this, because I feel sure there ought to be Windows users around who care. So perhaps someone somewhere can look into actually installing the test reporter module. It isn't hard, really...

2012/02/29

Patches^W Unit Tests Welcome

There's a saying in Open Source communities "Patches Welcome". However, I find that some sorts of patches are more welcome than others.

Recently I've been working on putting strptime into POSIX. In response of one of my patches on the perl5-porters@ mailing list, Nicholas Clark wrote me a reply, attached to which was a new set of unit tests, most of which the code did not currently pass, but ought to.

As patches go, this is one of the most useful patches I have ever received. He didn't try (too much) to fix the code, didn't spend that long explaining the problems. He just sent a unit test. Now let me explain why this is useful.

With a unit test script, I can have it run against any version of the code I write. I can run it every minute until it works. I can run it at 3 in the morning during one of my late-night hacking sessions. I can run it on the underground train on my commute to work. It is close, immediate, convenient.

A discussion over a patch to the code alone would be at least an order of magnitude slower than this. People keep different time schedules. People aren't always available online. People don't always get what each other means the first time, leading to more back-and-forth discussion until an agreement is reached.

Without wishing to upset too many members of the Open Source community that might disagree with me, I would say "Patches (to code) Welcome, but unit tests, examples, or documentation would be preferred." I can easily hack up some code to pass the tests you supply, whereas if you just supply some code it's much harder for me to work out what you actually wanted.

2010/10/31

Perl - Test::Identity

Today I uploaded a new module to CPAN; Test::Identity. It's possibly the quickest module I've ever written, from when I decided to write it, to when it was actually uploaded:
(18:02) sub Test::Identity::identical { is refaddr $_[0], refaddr $_[1], $_[2] } <== I'm about to write such in a module, unless anyone can suggest me a module that already has it
...
(19:21) * GumbyPAN CPAN Upload: Test-Identity-0.01 by PEVANS
I won't spend a long time explaining why, I'll just quote the docs:

This module provides a single testing function, identical. It asserts that a given reference is as expected; that is, it either refers to the same object or is undef. It is similar to Test::More::is except that it uses refaddr, ensuring that it behaves correctly even if the references under test are objects that overload stringification or numification.

It also provides better diagnostics if the test fails:
$ perl -MTest::More=tests,1 -MTest::Identity -e'identical [], {}'
1..1
not ok 1
# Failed test at -e line 1.
# Expected an anonymous HASH ref, got an anonymous ARRAY ref
# Looks like you failed 1 test of 1.

$ perl -MTest::More=tests,1 -MTest::Identity -e'identical [], []'
1..1
not ok 1
# Failed test at -e line 1.
# Expected an anonymous ARRAY ref to the correct object
# Looks like you failed 1 test of 1.

2010/08/15

Test to assert object identity

I've just copypasted the following test function into about the fifth different test script:
use Scalar::Util qw( refaddr );
sub identical
{
my ( $got, $expected, $name ) = @_;

my $got_addr = refaddr $got;
my $exp_addr = refaddr $expected;

ok( !defined $got_addr && !defined $exp_addr ||
$got_addr == $exp_addr,
$name ) or
diag( "Expected $got and $expected to refer to the same object" );
}
Rather than continuing to copypaste it around some more, can anyone suggest a standard Test:: module that contains it? Failing that, if it's really honestly the case that nobody has yet felt it necessary to provide one, could someone suggest a suitable module to contain it?

This behaviour cannot be implemented using, say, is( $obj, $expected ) because that will attempt to compare numerical equality, which of course will fail for any object that overloads numberification or numeric comparison operators.

2010/07/01

Good code, bad tests

I've been working on IO::Async::SSL recently. Both the previous upload, and the next one I shall shortly do, are fixes for failing smoke tests. Moreover, they're fixes for failing tests, of correct code.

The first concerns the semantics of the END block. The code looked roughly like
use Test::More
system( 'socat -help' ) == 0 or plan skip_all => "No socat";
plan tests => 3;

open my $kid, "-|", "socat", .... or die;
END { kill TERM => $kid }
The idea of this code was to ensure that socat is no longer running when the test exits. Of course, if the socat program isn't installed, the entire test is skipped. And the END block is still run. The result of this is that since $kid is undefined, the containing perl process is sent SIGTERM instead, and the test harness exits with an error. Ooops.

Now what I actually wanted here was
END { kill TERM => $kid if defined $kid }
I wonder if this situation would warrant a new block-alike, perhaps lets call it ENDX, which is only executed at END time if the line declaring it was actually reached in code. Perhaps it can be hacked up by
my @ends;
sub ENDX(&) {
push @ends, $_[0];
}
END { $_->() for @ends }
...
ENDX { kill TERM => $kid };
The second failure is a curious one that starts off looking like an OS-specific bug in the code. All the Linux smoke boxes were giving fails on a particularly innocent-looking test
is( $client->peeraddr, $server->sockaddr, 'Client socket address' );
Under closer inspection, it seems that the way Linux returns socket addresses from the kernel doesn't initialise the "holes" in the address structure, whereas most other OSes do.

The result of this is that rendered as strings of bytes, the two addresses don't necessarily contain all the same bytes as each other, even though they represent the same address. The way to fix this one is to unpack the addresses, known to be AF_INET addresses, and use is_deeply instead:
use Socket qw( unpack_sockaddr_in );

is_deeply( [ unpack_sockaddr_in $client->peeraddr ],
[ unpack_sockaddr_in $server->sockaddr ],
'Client socket address' );
This doesn't look too neat this way, but perhaps there's some scope for considering a Test::Sockaddr module or somesuch, to neaten it up. Perhaps a little special-purpose though..