Beefy Boxes and Bandwidth Generously Provided by pair Networks
We don't bite newbies here... much
 
PerlMonks  

How can I enable utf8 layer on Test::More diag output

by mje (Curate)
on Jul 22, 2008 at 14:19 UTC ( [id://699317]=perlquestion: print w/replies, xml ) Need Help??

mje has asked for the wisdom of the Perl Monks concerning the following question:

use strict; use warnings; use Test::More qw(no_plan); binmode STDOUT, ":utf8"; binmode STDERR, ":utf8"; my $data = "\x{263a}xxx" . chr(0x05d0) . "\x{20ac}"; diag($data);
produces:
Wide character in print at /usr/share/perl/5.8/Test/Builder.pm line 12 +75. # unicode characters here # No tests run!
According to Test::More it uses stdout/stderr by default. How can I enable utf8 layer on diag output when using Test::More?

Replies are listed 'Best First'.
Re: How can I enable utf8 layer on Test::More diag output
by moritz (Cardinal) on Jul 22, 2008 at 14:44 UTC
    I ran into the exact same problem while testing Perl6::Str, and I worked around it with the following sub, through which I piped all my output to diag(). It doesn't display the non-ASCII-Characters, but it does enable very detailed analysis even on non-utf-8 terminals:
    use charnames (); sub escape_str { my $str = shift; $str =~ s{([^\0-\177])}{_N_escape($1)}eg; return $str; } sub _N_escape { return '\N{' . charnames::viacode(ord($_[0])) . '}'; }

    However your post encouraged me to dig into Test::More source code, and in Test::Builder I found this code:

    sub _print_diag { my $self = shift; local($\, $", $,) = (undef, ' ', ''); my $fh = $self->todo ? $self->todo_output : $self->failure_output; print $fh @_; }

    So it seems you have to get hold of a test builder object, and then binmode todo_output or failure_output (or both):

    $ perl -MTest::More -wle 'binmode Test::More->builder->failure_output, + ":utf8"; diag chr(228)' # ä
    Not pretty, but it works.
      Yes, see my post below. You can set the failure_output and the todo_output but I don't see a place to obtain the handle from Test::Builder, just a way to set it. As a result this works:
      use Test::More; # both of the following are too late for Test::More # because it has already duplicated STDOUT, STDERR binmode(STDOUT, ":utf8"); binmode(STDERR, ":utf8"): my $tb = Test::More->builder; $tb->failure_output(\*STDERR); $tb->todo_output(\*STDERR); $tb->output(\*STDOUT);
      now we can diag utf8 data.
        Here's how you obtain the handles with the same methods you use for setting them:
        my $b = Test::More->builder; binmode STDOUT, $b->failure_output, ':utf8';
        # both of the following are too late for Test::More # because it has already duplicated STDOUT, STDERR

        That's not quite the cause. If it where, the following would work:

        BEGIN { binmode STDOUT, ':utf8'; binmode STDERR, ':utf8'; } use Test::More;
        however, it doesn't work for me.

        still for a general-purpose module I wouldn't assume that the terminal necessarily accepts UTF-8.

Re: How can I enable utf8 layer on Test::More diag output
by pc88mxer (Vicar) on Jul 22, 2008 at 14:48 UTC
    Instead of calling diag($data) you probably have to do this:
    my $tb = Test::More->builder; $tb->failure_output(\*STDOUT); $tb->diag($data);
    Of course you can wrap it in a subroutine.

    Update: Alternatively, instead of redefining failure_output, it might be better to just call binmode on it:

    my $tb = Test::More->builder; binmode $tb->failure_output, ":utf8"; $tb->diag(...);
      This does in deed work and I've found out why. The problem is that Test::Builder duplicates STDOUT and STDERR (comments say so you can change them without affecting Test::More) but it does NOT duplicate the IO layers. The funny thing is has code to do it but it is commented out:
      sub _open_testhandles { my $self = shift; return if $Opened_Testhandles; # We dup STDOUT and STDERR so people can change them in their # test suites while still getting normal test output. open( $Testout, ">&STDOUT") or die "Can't dup STDOUT: $!"; open( $Testerr, ">&STDERR") or die "Can't dup STDERR: $!"; # $self->_copy_io_layers( \*STDOUT, $Testout ); # $self->_copy_io_layers( \*STDERR, $Testerr ); $Opened_Testhandles = 1; } sub _copy_io_layers { my($self, $src, $dst) = @_; $self->_try(sub { require PerlIO; my @src_layers = PerlIO::get_layers($src); binmode $dst, join " ", map ":$_", @src_layers if @src_layers; }); }
      I've no idea why this is commented out. However, setting utf8 io layer on STDOUT and then calling failure_output to reset Test::More's idea of failure output works - just seems a bit of a hack.
Re: How can I enable utf8 layer on Test::More diag output
by massa (Hermit) on Jul 22, 2008 at 16:55 UTC
    Try (untested):
    use strict; use warnings; use open qw(:std :utf8); use Test::More qw(no_plan);
    it /should/ work....
    []s, HTH, Massa (κς,πμ,πλ)
      it /should/ work....
      But it doesn't.
        Yes, and I'm still puzzled. use open should affect the way Test/More.pm opens its todo_output and failure_output filehandles (*Test::More::TESTOUT and *Test::More::TESTERR, respectively); but somehow, open(TESTOUT, ">&STDOUT") does not respect the default stack in ${^OPEN} nor copies the stack on the STDOUT handle. A mistery to me. Anyway, the _real_ solution was already found:
        binmode(($_->todo ? $_->todo_output : $_->failure_output), ':utf8') for Test::More->builder
        (this one I tested !!) :-)
        []s, HTH, Massa (κς,πμ,πλ)

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://699317]
Approved by herveus
Front-paged by moritz
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others sharing their wisdom with the Monastery: (7)
As of 2024-04-24 09:55 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found