Fixing a hard coded file path in a test in App::Cmd: Success remains elusive

Yesterday, I noticed that one of the tests in App::Cmd hard codes the directory separator in one of the test cases, and therefore fails on my perl 5.20.1 built with Visual Studio 2013 CE on Windows 8.1.

Keep in mind this passage from perldoc perlport:

If all this is intimidating, have no (well, maybe only a little) fear. There are modules that can help. The File::Spec modules provide methods to do the Right Thing on whatever platform happens to be running the program.

In general, production code should not have file paths hardcoded. Making them user-supplied or read from a configuration file is better, keeping in mind that file path syntax varies on different machines.

This is especially noticeable in scripts like Makefiles and test suites, which often assume / as a path separator for subdirectories.

I decided to fix the following line in t/basic.t:

my $version_expect = "basic.t (Test::MyCmd) version 0.123 (t/basic.t)\n";

To initiate the pull request, I forked the repo, and then cloned it on my laptop.

Ooops! App::Cmd uses Dist::Zilla. So, I did a quick cpanm Dist::Zilla, and noticed App::Cmd is one of Dist::Zilla’s dependencies. Clearly, this is not a problem for me, but by this time a lot of people would have given up.

Now, another dependency for Dist::Zilla is DateTime::TimeZone::Local::Win32 which depends on Win32::TieRegistry which depends on Win32API::Registry which fails to install.

So, we go down the rabbit hole … Why am I here? Oh, I had forgotten: Because one of the tests in the App::Cmd distribution hard codes the directory separator, and I wanted to fix that. Unfortunately, there is no way to fix something and run nmake test with that distribution, so I am trying to install Dist::Zilla etc etc so on and so forth.

The test script for Win32API::Registry is one hellish mess.

It gives me a great big WTF! when first it fails without modification:

C:\…\Win32API-Registry-0.32> prove -vb .. 1..216 ok 1Can't find key with values: More data is available ok 2 ok 3 ok 4 ok 5 ok 6 ok 7 ok 8 ok 9 ok 10 Dubious, test returned 255 (wstat 65280, 0xff00) Failed 206/216 subtests

There are these lines toward the beginning of the file:

  1. $|= 1 if $Debug= ( -t STDIN ) != ( -t STDOUT );
    1. $zero= 16; # Change to 0 when RegEnumKeyExA() and RegEnumValueA() # handle ERROR_MORE_DATA better!


If the lpData buffer is too small to receive the data, the function returns ERROR_MORE_DATA.

Why is there a line that says $zero= 16?!

I bite the bullet. I change those lines to:

  1. $|= 1 if $Debug= 1;
    1. $zero= 0;

assuming RegEnumKeyExA and RegEnumValueA actually do handle ERROR_MORE_DATA better. … — That just doesn’t sound right.

I get:

All tests successful. Files=1, Tests=216, 0 wallclock secs ( 0.13 usr + 0.09 sys = 0.22 CPU) Result: PASS

I decide to go ahead and install that, not knowing what I really did by setting $zero= 0. (BTW, what’s up with no space between variable name and equals sign? Do we have a Mathematician in the house? Pascal programmer?) Anyway, not to sound ungrateful, but I would love it if I could understand what the test code is doing. Is there a good reason for tests not to use strict and warnings?

So, back to cpanm Dist::Zilla

After a few minutes, I get:

==> Found dependencies: Log::Dispatch::Output ! Installing the dependencies failed: Module 'Log::Dispatch::Output' is not installed ! Bailing out the installation for Log-Dispatch-Array-1.003.

which is apparently because Sys::Syslog did not install due to Win32::TieRegistry failing to install.

That turns out to be due to a failure to find HKCU\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer because, on this machine, HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer is set.

So, I go ahead and nmake install that.

Back to cpanm Dist::Zilla … After a few minutes, I was rewarded with:

The solution to that is cpanm App::Cmd --force because I know there is very likely nothing wrong with the module: The only failure is due to a hard-coded path.

… or not … because Moose is still failing to install.

It turns out that is due to Devel::OverloadInfo which fails because Sub::Identify fails.

And, do you want to know why that fails?

Here is a screenshot:

#          got: 't\04codelocation-pureperl.t
#     expected: 't/04codelocation-pureperl.t

#   Failed test 'file'
#   at t\04codelocation-pureperl.t line 18.
#          got: 't\04codelocation-pureperl.t
#     expected: 't/04codelocation-pureperl.t
# Looks like you failed 2 tests of 6.
t\04codelocation-pureperl.t .. …
Failed 2/6 subtests
t\04codelocation.t ...........
t\04codelocation.t ........... 1/6 #   Failed …
#   at t\04codelocation.t line 18.
#          got: 't\04codelocation.t'
#     expected: 't/04codelocation.t'

#   Failed test 'file'
#   at t\04codelocation.t line 18.
#          got: 't\04codelocation.t'
#     expected: 't/04codelocation.t'
# Looks like you failed 2 tests of 6.

I give up … It’s time for cpanm Sub::Identify --force.

With that out of the way, are we done?

No, Moose still fails to install.

Do you want to know why?

Here, the errors look impressive enough:

And a look at the relevant code confirms that the test author failed to use quotemeta when interpolating a variable into a regular expression pattern:

OK, so I go ahead and try to install Moose for the nth time — this time not paying any attention to the rest of the tests.

Subsequently, the process of installing Dist::Zilla again comes to a halt when DateTime::TimeZone::Local::Win32 fails to install because DateTime failed to install. Do you know why DateTime fails to install? Because DateTime::TimeZone::Local::Win32 failed to install. So, I go ahead an ignore tests for DateTime::TimeZone and DateTime::TimeZone::Local::Win32.

After that, cpanm Dist::Zilla works.

By now, I am about past the point of caring about the hard-coded path in App::Cmd.

In closing, let me cite perldoc perlport again:

If all this is intimidating, have no (well, maybe only a little) fear. There are modules that can help. The File::Spec modules provide methods to do the Right Thing on whatever platform happens to be running the program.