Group
Extension

Convert-Binary-C/tests/602_threads.t

################################################################################
#
# Copyright (c) 2002-2024 Marcus Holland-Moritz. All rights reserved.
# This program is free software; you can redistribute it and/or modify
# it under the same terms as Perl itself.
#
################################################################################

use Test;
use Config;
use Convert::Binary::C @ARGV;
use constant NUM_THREADS => 4;

$^W = 1;

BEGIN {
  plan tests => NUM_THREADS
}

my $CCCFG = require './tests/include/config.pl';

#===================================================================
# load appropriate threads module and start a couple of threads
#===================================================================

my $have_threads = ($Config{useithreads} && $] >= 5.008) ||
                    $Config{use5005threads};

my $reason = $Config{useithreads} || $Config{use5005threads}
             ? "unsupported threads configuration"
             : "no threads";

if ($have_threads && $] > 5.009004 && $] < 5.010001) {
  # Threads are broken between 5.9.4 and 5.10.1
  $reason = "threads are broken in this perl version";
  $have_threads = 0;
}

my @t;

if ($have_threads) {
  if ($Config{use5005threads}) {
    require Thread;
    @t = map { Thread->new( \&task, $_ ) } 1 .. NUM_THREADS;
  }
  elsif ($Config{useithreads} && $] >= 5.008) {
    require threads;
    @t = map { threads->new( \&task, $_ ) } 1 .. NUM_THREADS;
  }
}
else {
  Convert::Binary::C->new->parse('');  # allocate/free some memory
  @t = 1 .. NUM_THREADS
}

skip($have_threads ? '' : $reason,
     $have_threads ? $_->join : $_, '', "thread failed") for @t;

sub task
{
  my $arg = shift;
  my $p;

  eval {
    $p = Convert::Binary::C->new(
      %$CCCFG,
      EnumSize       => 0,
    );
    if ($arg % 2) {
      print "# parse_file ($arg) called\n";
      $p->parse_file('tests/include/include.c');
      print "# parse_file ($arg) returned\n";
    }
    else {
      open FH, "tests/include/include.c" or die;
      my $code = do { local $/; <FH> };
      close FH;
      print "# parse ($arg) called\n";
      $p->parse($code);
      print "# parse ($arg) returned\n";
    }
  };

  $@ and return $@;

  # some simplified checks from the parse test

  my @enum_ids     = $p->enum_names;
  my @compound_ids = $p->compound_names;
  my @struct_ids   = $p->struct_names;
  my @union_ids    = $p->union_names;
  my @typedef_ids  = $p->typedef_names;

  @enum_ids     ==  1 or return "incorrect number of enum identifiers";
  @compound_ids == 20 or return "incorrect number of compound identifiers";
  @struct_ids   == 19 or return "incorrect number of struct identifiers";
  @union_ids    ==  1 or return "incorrect number of union identifiers";
  @typedef_ids  == 54 or return "incorrect number of typedef identifiers";

  my @enums     = $p->enum;
  my @compounds = $p->compound;
  my @structs   = $p->struct;
  my @unions    = $p->union;
  my @typedefs  = $p->typedef;

  @enums      == 12 or return "incorrect number of enums";
  @compounds  == 26 or return "incorrect number of compounds";
  @structs    == 20 or return "incorrect number of structs";
  @unions     ==  6 or return "incorrect number of unions";
  @typedefs   == 54 or return "incorrect number of typedefs";

  my %size = do { local (@ARGV, $/) = ('tests/include/sizeof.pl'); eval <> };
  my $max_size = 0;
  my @fail = ();

  local $SIG{__WARN__} = sub {
    print "# unexpected warning: $_[0]";
    push @fail, $_[0];
  };

  for my $t (keys %size) {
    my $s = eval { $p->sizeof($t) };

    if ($@) {
      print "# sizeof failed for '$t': $@\n";
    }
    elsif ($size{$t} != $s) {
      print "# incorrect size for '$t' (expected $size{$t}, got $s)\n";
    }
    else {
      $max_size = $s if $s > $max_size;
      next;
    }

    push @fail, $t unless $s == $size{$t}
  }

  @fail == 0 or return "size test failed for [@fail]";

  # don't use random data as it may cause failures
  # for floating point values
  my $data = pack 'C*', map { $_ & 0xFF } 1 .. $max_size;
  @fail = ();

  for my $id (@enum_ids, @compound_ids, @typedef_ids) {

    # skip long doubles
    next if grep { $id eq $_ } qw( __convert_long_double float_t double_t );

    my $x = eval { $p->unpack($id, $data) };

    if( $@ ) {
      print "# ($arg) unpack failed for '$id': $@\n";
      push @fail, $id;
      next;
    }

    my $packed = eval { $p->pack($id, $x) };

    if ($@) {
      print "# ($arg) pack failed for '$id': $@\n";
      push @fail, $id;
      next;
    }

    unless (chkpack($data, $packed)) {
      print "# ($arg) inconsistent pack/unpack data for '$id'\n";
      push @fail, $id;
      next;
    }
  }

  @fail == 0 or return "pack test failed for [@fail]\n";

  print "# tests ($arg) finished successfully\n";

  return '';
}

sub chkpack
{
  my($orig, $pack) = @_;

  for (my $i = 0; $i < length $pack; ++$i) {
    my $p = ord substr $pack, $i, 1;
    if ($i < length $orig) {
      my $o = ord substr $orig, $i, 1;
      return 0 unless $p == $o or $p == 0;
    }
    else {
      return 0 unless $p == 0;
    }
  }

  return 1;
}


Powered by Groonga
Maintained by Kenichi Ishigaki <ishigaki@cpan.org>. If you find anything, submit it on GitHub.