Older blog entries for chromatic (starting at number 221)

Quieter Test Output:

I never really understood the point of <code>prove</code>; I write my tests as normal Perl programs, launchable from the command line. <code>perl t/testname.t</code> did most of what I wanted to do.

With more than 24 tests in a file, though, I often promised myself to write or find someday a variant test harness that ignored successes and reported only failures or an all clear message.

I looked at the code of <code>prove</code> today to find where to add it, but again, it doesn't seem to do anything I really need and there was no way to add it. I moved on to Test::Harness::Straps. I don't know if anyone's ever actually used this module (and it's admittedly a bit of a mess inside, though the long-promised Test::Builder refactoring may make it easier to write), but it took 67 lines of code to do what I needed (and a little more) in a well-factored way.

I call it qtest:

#!/usr/bin/perl

use strict; use warnings;

use Test::Harness::Straps;

my $strap = Test::Harness::Straps->new();

for my $file (@ARGV) { next unless -f $file;

my %results = $strap->analyze_file( $file );

if ($results{passing}) { report( sprintf('All (%d) tests passed in %s', $results{seen}, $file)); } elsif ($results{skip_all}) { report( sprintf('All (%d) tests skipped in %s', $results{seen}, $file)); } else { report( find_failures( $file, \%results ) ); } }

sub report { my $message = shift; print "$message\n"; }

sub find_failures { my ($file, $results) = @_; my $report = create_header($file, @{$results}{qw( max seen ok )}); my $count = 0;

for my $test ( @{ $results->{details} } ) { $count++; next if $test->{ok}; $report .= create_test_result( $count, @{ $test }{qw( name reason ) } ); }

return $report; }

sub create_header { my ($file, $expected, $seen, $passed) = @_; my $failed = $seen - $passed; return sprintf "File '%s'\nExpected %d / Seen %d / Okay %d / Failed %d\n", @_, $failed; }

sub create_test_result { my ($number, $name, $reason) = @_; $name =~ s/^-\s*//; $reason ||= ''; $reason = " ($reason)" if $reason; return sprintf "\tTest #%d: %s%s\n", $number, $name, $reason; }

In practice, I may tweak the formatting somewhat. It might be nice to report skips and surprisingly-passing TODO tests, too. I also might want a flag to end after the first failure, to solve the problem of cascading failures rolling off of the screen. Still, this was a lot simpler than I thought and already makes my life easier.

Excuses Not To Hack:

Having started Write Your Life the first week of October, I spent a month writing my own versions of the assignments. (No, you probably won't ever see them.) That was enlightening and habit-forming.

Unfortunately, the weird not-on-a-month-boundary timing caused me to miss the start of National Novel Writing Month by at least a week. That's too bad; if I'd known and could have planned for it, I would have participated. 50,000 words is intimidating, but a thousand a day is doable. (It would have been wrong to set aside my first ever One Month Project because I know I'd probably never go back to it.)

The discipline of writing at a set time every day for a month was valuable though. Instead of writing about myself (it drove me crazy to see personal pronouns in so many sentences), I've written over 7000 words of what may just become a novel in the past week.

You might see that online in the near future.

In the meantime, feeling guilty about not hacking (though not guilty enough to figure out why the JIT fixes for Parrot on AIX have broken it on Linux PPC), I've released Test::MockObject 0.15. This version allows you to prevent logging certain method calls; that'll make many lives easier.

Too Many Choices:

If "too many choices" of applications in the typical Linux distribution is really keeping people on Windows, why aren't there more people starving to death in the cereal aisles of grocery stores?

A Strange Mix of Writer and Programmer:

Though most people know me for my programming, I also consider myself a writer. My day job combines both fields very nicely, but it doesn't leave a lot of room for non-technical writing.

The other night, I had a conversation spanning several subjects, including the deaths of my grandparents, the fear that I hadn't heard their full life stories, and my desire to make the world a better place, in part by helping non-programmers learn just enough programming to solve smaller problems. I felt guilty explaining the latter idea, as it seems self-indulgent in some ways and trivial in others.

As often happens, several divergent ideas ganged up on me and presented a different solution. I do want to make the world a better place, but maybe programming isn't the right place at the moment. Instead, I'm looking for a wider audience.

I've spent the past week putting the foundations of Write Your Life in place. The idea is to encourage people who want to improve their writing to practice writing every day for a month by providing one topic per day.

It uses an autobiographical theme for two reasons. First, it's important to write what you know. Though it's still difficult to start writing a thousand words about your home, once you cross that barrier, it can be more difficult to stop. (I blew past the word limit and didn't even start on the upper story of my house.)

Second, I believe everyone has a story to tell. I really regret not learning more from my grandparents while I had the chance, though my friend did point out that they may have told me as much as they felt comfortable sharing. There's a wealth of wisdom and experience in the previous generations that we may lose unless we encourage people to write and speak.

I hope my project will convince a few people to publish their own lives. Please feel free to pass the link to your friends.

(I'll try to add an RSS or Atom feed for the assignments today.)

On "Scripting" Languages:

The nice thing about saying "nobody has ever written a reliable, high-performance program" in a so-called scripting language is the person saying it controls the debate. If you point one out, he can claim that such and such a program isn't reliable enough or doesn't exhibit high enough performance.

Remember folks, it's not the experience, skill, dedication, talent, or luck of your developers, it's whether you choose a language that forces a dedicated compilation stage... at least if you're poisoning the well.

The Command-Line is My File Chooser:

Like brian d foy, I have a lot of little command-line tools to make my life easier. Today, I wrote two more.

One of the parts of Mac OS X I actually miss (and there are a few) is its open command that examines a named file and attempts to open it in an appropriate application. That's an easy program to write, if you aim for 80% effectiveness, but it's so convenient that when I found myself wishing for it today, I spent five minutes writing it.

#!/usr/bin/perl

use strict; use warnings;

use File::MMagic;

my $file = shift or die "Usage: $0 <filename>\n";

my %subtypes = map { $_ => 1 } 'application/x-zip', 'text/plain', 'text/html'; my %apps = ( 'application/star-office' => 'xooffice', 'application/msword' => 'abiword-2.0', 'application/mozilla' => 'moz_tab', 'image/jpeg' => 'eog', 'image/gif' => 'eog', 'text/html' => 'moz_tab', ); my %exts = ( 'application/star-office', => qr/\.sxw\Z/, 'application/mozilla', => qr/\.html?\Z/i, );

my $mm = File::MMagic->new();

while (my ($subtype, $regex) = each %exts) { $mm->addFileExts( $regex, $subtype ); }

my $type = $mm->checktype_filename( $file ); $type = $mm->checktype_byfilename( $file ) if exists $subtypes{ $type };

die "Unknown type '$type'\n" unless exists $apps{ $type };

fork and exit; exec $apps{$type}, $file, @ARGV;

It's worth factoring out the file types, applications, and subtypes into data somewhere, but when I find myself needing to maintain a bigger list, I'll do that.

You might notice a program called moz_tab. What does that do? I usually have Mozilla running with several tabs on a different virtual desktop. (Now you begin to see what I missed from a real window manager when I used Mac OS X!) I do want to open HTML files in Mozilla, but I don't want to open them in a new browser window or, worse, with a different profile. moz_tab checks to see if there's an existing window and opens a new tab or a window as appropriate.

#!/bin/sh

# create an absolute path DIR=`pwd` FILE="$DIR/$1"

# check if Mozilla is already running /usr/bin/mozilla -remote 'ping()' STATUS=$?

# launch a new tab, if so if [ "$STATUS" == 0 ]; then exec /usr/bin/mozilla -remote "openurl(file://$FILE,new-tab)" fi

# or launch a new window exec /usr/bin/mozilla "file://$FILE"

It doesn't check for an absolute path before absolutifying the path, but if I need that, I'll add another line.

Having both of these programs available has saved me almost a minute today. That doesn't seem like much, but keep in mind that I'd have spent that minute navigating developer-hostile file chooser windows. If I can avoid that by using the developer-friendly command line in a ubiquitous terminal window, my life is much more pleasant.

P5NCI:

The name isn't great, the available thunks are too few, the known types are too simple, and it has a tendency to rebuild the C files every time you call ./Build, but P5NCI version 0.01 works and you're more than welcome to try it and send me patches.

Alternately, tell me how cool P5NCI::Library actually is in practice.

Thanks to jplindstrom for pointing me at Win32::API which already handles structs; I think I can modify that code to work on other platforms. What's a little sizeof between friends?

Throw Away Your C Compiler (after building Perl):

#!/usr/bin/perl

# P5NCI.t

BEGIN { chdir 't' if -d 't'; use blib; }

use strict; use warnings;

use Test::More tests => 9;

use_ok('P5NCI') or exit;

my $double_lib = P5NCI::load_lib( '../nci_demo.so' ); my $double_double = P5NCI::load_nci_func( $double_lib, 'double_double', 'dd' ); is( $double_double->( 1.0 ), 2.0 ); is( $double_double->( 3.14 ), 6.28 );

my $double_int = P5NCI::load_nci_func( $double_lib, 'double_int', 'ii' ); is( $double_int->( 1 ), 2 ); is( $double_int->( 3 ), 6 );

my $double_float = P5NCI::load_nci_func( $double_lib, 'double_float', 'ff' ); is( $double_float->( 1.0 ), 2.0 ); ok( abs( $double_float->( 0.314 ) - 0.628) < 0.00001 );

my $multiply_ints = P5NCI::load_nci_func( $double_lib, 'multiply_ints', 'iii' ); is( $multiply_ints->( 10, 20 ), 200 ); is( $multiply_ints->( 5, 5 ), 25 );

The interface is a bit grotty, the documentation is spotty, the build system needs some help, and it only handles really simple functions right now, but it works.

When It Works, It Works:

After wondering exactly what poor Greg Lapore was doing when he asked, innocently enough, "Why am I receiving an error message when I try to write a demo file from SDL::Tutorial?", I realized that it's because Pod::ToDemo 0.10 was irredeemably broken.

I have no idea why it seemed to work for me, but a bit of diving into the scary details of what happens when at initialization time, a bit of interface redesign, and a few more tests later, and the new Pod::ToDemo scheme in 0.20 is a little uglier for the end-user, a lot simpler for the basic, default case for the coder, and, most importantly, a working release.

Of course, you'll need the latest SDL Perl snapshot for the updated tutorials, but they're there and work now. Hopefully Dave will soon pull the patches for the new release he's threatened.

Saving Unicode Characters as Screenshots:

I have a small project that requires extracting several Unicode characters from a Word document as uncompressed high-resolution screenshots. The only problem is that I don't have Word -- nor do I run any operating system that can run Word.

Has anyone solved a similar problem? Are there any COM gurus who know the magic invocations to dump a few dozen Kana characters to images? I suspect a decent hacker could do this in 15 minutes or so, much better than the 10 hours it would take -- if I had the tools -- to convert these manually.

Drop me a line at chromatic at oreilly dot com if that's you.

212 older entries...

New Advogato Features

New HTML Parser: The long-awaited libxml2 based HTML parser code is live. It needs further work but already handles most markup better than the original parser.

Keep up with the latest Advogato features by reading the Advogato status blog.

If you're a C programmer with some spare time, take a look at the mod_virgule project page and help us with one of the tasks on the ToDo list!