Search code examples
perl

"Cannot run test (h_c: is_deeply($n, $s)) with active children at /usr/lib/perl5/5.18.2/Test/More.pm line 1020"


Trying to convert some code from use Test::more to use Test::Builder I got this problem when trying to use is_deeply (perl 5.18.2):

Cannot run test (h_c: is_deeply($n, $s)) with active children at /usr/lib/perl5/5.18.2/Test/More.pm line 1020

The manual for Test::Builder states this example for explain:

is_deeply($have, $want) || diag explain $have;

However Test::Builder does not have the is_deeply method, but Test::More has it (I used use Test::More 'import' => [qw(is_deeply)]; to import only that). Unfortunately when trying to use it from a sub-test ($tb->child()), the program dies with the message cited above.

Is there a way to use is_deeply in a sub-test?

Message from within the Perl debugger:

Cannot run test (h_c: is_deeply($n, $s)) with active children at /usr/lib/perl5/5.18.2/Test/More.pm line 1020.
 at /usr/lib/perl5/5.18.2/Test/Builder.pm line 1969.
        Test::Builder::croak('Test::Builder=HASH(0x1b6e3c0)', 'Cannot run test (h_c: is_deeply($n, $s)) with active children') called at /usr/lib/perl5/5.18.2/Test/Builder.pm line 769
        Test::Builder::ok('Test::Builder=HASH(0x1b6e3c0)', 1, 'h_c: is_deeply($n, $s)') called at /usr/lib/perl5/5.18.2/Test/More.pm line 1020
        Test::More::is_deeply('HOTP::Core=ARRAY(0x31131c8)', 'HOTP::Core=ARRAY(0x310ab10)', 'h_c: is_deeply($n, $s)') called at otptest.pl line 96

More Details

As I failed to extract a minimum example, I dug into the code I have. Line 1020 of Test::More is $ok = $tb->ok( 1, $name ); (so the actual test succeeded). The interesting fact is $tb that is not the Test::Builder child instance used in the caller, but a local instance created in is_deeply as my $tb = Test::More->builder. That instance has a child that is the first-level child of my Test::Builder (that's not finished yet, of course), so the test $self->{Child_Name} in line 766 of Test::Builder::ok fails.

I'm tempted to make a copy of is_deeply passing the correct Test::Builder instance instead of creating a local one.

Anyway, here's the best approximation of the failure scenario I could make:

#!/usr/bin/perl
use strict;
use warnings;
use Test::Builder;
use Test::More 'import' => [qw(is_deeply)];

sub t2($$$)
{
    my ($TB, $a, $b) = @_;

    $TB->plan('tests' => 4);
    $TB->ok(1, 't2.1');
    $TB->ok(1, 't2.2');
    is_deeply($a, $b, 't2.is_deeply');
    $TB->ok(1, 't2.4');
    $TB->finalize();
}

sub t1($$$)
{
    my ($TB, $a, $b) = @_;

    $TB->plan('tests' => 2);
    t2($TB->child('t2'), $a, $b);
    $TB->ok(1, 't1.2');
    $TB->finalize();
}

sub main()
{
    my ($a, $b) = ([1], [1]);
    my $TB = Test::Builder->create();
    my $subtest = sub($) { $TB->diag($_[0]); return $TB->child($_[0]); };

    $TB->plan('tests' => 1);
    t1($subtest->('t1.1'), $a, $b);
    #t1($subtest->('t1.2'), $a, $b);
    $TB->done_testing();
}

main();

And this is the output I got:

  DB<0> c
1..1
# t1.1
    1..2
        1..4
        ok 1 - t2.1
        ok 2 - t2.2
ok 1 - t2.is_deeply
        ok 3 - t2.4
        # Looks like you planned 4 tests but ran 3.
    not ok 1 - t2
    #   Failed test 't2'
    #   at foo.pl line 72.
    ok 2 - t1.2
    # Looks like you failed 1 test of 2.
not ok 1 - t1.1
#   Failed test 't1.1'
#   at foo.pl line 84.
# Tests were run but no plan was declared and done_testing() was not seen.
Debugged program terminated.  Use q to quit or R to restart,

Insights

When debugging the code, I realized that my actual program used Test::Builder->new instead of Test::Builder->create (as in the minimal example). That made the difference (also see my answer https://stackoverflow.com/a/78159022/6607497):

#!/usr/bin/perl
use strict;
use warnings;
use Test::Builder;
use Test::More 'import' => [qw(is_deeply)];

sub t2($$$)
{
    my ($TB, $a, $b) = @_;

    $TB->plan('tests' => 4);
    $TB->ok(1, 't2.1');
    $TB->ok(1, 't2.2');
    #local $Test::Builder::Test = $TB;      # ugly and dangerous!
    is_deeply($a, $b, 't2.is_deeply');
    $TB->ok(1, 't2.4');
    $TB->finalize();
}

sub t1($$$)
{
    my ($TB, $a, $b) = @_;

    $TB->plan('tests' => 2);
    t2($TB->child('t2'), $a, $b);
    $TB->ok(1, 't1.2');
    $TB->finalize();
}

sub main()
{
    my ($a, $b) = ([1], [1]);
    my $TB = Test::Builder->new();
    my $subtest = sub($) { $TB->diag($_[0]); return $TB->child($_[0]); };

    $TB->plan('tests' => 1);
    t1($subtest->('t1.1'), $a, $b);
    #t1($subtest->('t1.2'), $a, $b);
    $TB->done_testing();
}

main();
Loading DB routines from perl5db.pl version 1.39_10
Editor support available.

Enter h or 'h h' for help, or 'man perldebug' for more help.

main::(foo.pl:42):      main();
  DB<0> c
1..1
# t1.1
    1..2
        1..4
        ok 1 - t2.1
        ok 2 - t2.2
Cannot run test (t2.is_deeply) with active children at /usr/lib/perl5/5.18.2/Test/More.pm line 1020.
 at /usr/lib/perl5/5.18.2/Test/Builder.pm line 1969.
        Test::Builder::croak('Test::Builder=HASH(0x2003c58)', 'Cannot run test (t2.is_deeply) with active children') called at /usr/lib/perl5/5.18.2/Test/Builder.pm line 769
        Test::Builder::ok('Test::Builder=HASH(0x2003c58)', 1, 't2.is_deeply') called at /usr/lib/perl5/5.18.2/Test/More.pm line 1020
        Test::More::is_deeply('ARRAY(0x1d12770)', 'ARRAY(0x2476030)', 't2.is_deeply') called at foo.pl line 15
        main::t2('Test::Builder=HASH(0x2508560)', 'ARRAY(0x1d12770)', 'ARRAY(0x2476030)') called at foo.pl line 25
        main::t1('Test::Builder=HASH(0x25116e0)', 'ARRAY(0x1d12770)', 'ARRAY(0x2476030)') called at foo.pl line 37
        main::main() called at foo.pl line 42
        # Child (t2) exited without calling finalize()
    not ok 1 - t2
    #   Failed test 't2'
    #   at /usr/lib/perl5/5.18.2/Test/Builder.pm line 1969.
    # Child (t1.1) exited without calling finalize()
not ok 1 - t1.1
#   Failed test 't1.1'
#   at /usr/lib/perl5/5.18.2/Test/Builder.pm line 1969.
# Looks like you failed 1 test of 1.
# Looks like your test exited with 29 just after 1.
Debugged program terminated.  Use q to quit or R to restart,

When un-commenting the local line, the output is instead:

main::(foo.pl:42):      main();
  DB<0> c
1..1
# t1.1
    1..2
        1..4
        ok 1 - t2.1
        ok 2 - t2.2
        ok 3 - t2.is_deeply
        ok 4 - t2.4
    ok 1 - t2
    ok 2 - t1.2
ok 1 - t1.1
Debugged program terminated.  Use q to quit or R to restart,

Solution

  • I believe the effect is caused by several bugs in Test::Builder:

    1. When using Test::Builder->new(), a "copy" of the returned objects is saved as $Test::Builder::Test, but that "copy" is actually a copied reference, so that setting the number of tests in the object(s) returned by new also changes the number of tests in $Test::Builder::Test.
    2. is_deeply from Test::More should be a method of Test::Builder actually, but it uses its own lexical instance of Test::More->builder. That, in turn. returns the value of $Test::Builder::Test. Naturally that won't work for nested tests (i.e.: children of Test::Builder).

    My solution was to add local $Test::Builder::Test = $TB; just before calling is_deeply.

    In addition I could have used Test::Builder->create()instead of Test::Builder->new(), but that still would not fix the output of is_deeply.