The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl -w

use strict;

use Test::More tests => 14;
use Test::HexString;

# We'll be testing a lot using packed binary strings. Endian matters
# The strings are all written in little endian, using the following function
# to swap as appropriate
BEGIN {
   *bswap = ( pack("S", 0x1234) eq "\x12\x34" ) ? sub { reverse $_[0] }
                                                : sub { $_[0] }
}

package NetlinkTest;
use base qw( IO::Socket::Netlink );

sub message_class { "NetlinkTest::_Message" }

package NetlinkTest::_Message;
use base qw( IO::Socket::Netlink::_Message );

__PACKAGE__->has_nlattrs(
   "nlmsg",
   byte  => [ 1, "u8" ],
   short => [ 2, "u16" ],
   long  => [ 3, "u32" ],
   str   => [ 4, "asciiz" ],
   raw   => [ 5, "raw" ],
   nest  => [ 6, "nested" ],
);

package main;

my $sock = NetlinkTest->new( Protocol => 0 )
   or die "Cannot create NetlinkTest socket - $!";

my $message = $sock->new_message;

isa_ok( $message, 'NetlinkTest::_Message',         '$message' );
isa_ok( $message, 'IO::Socket::Netlink::_Message', '$message' );

$message->nlattrs( {} ); # empty

is_hexstr( $message->pack,
   bswap("\x10\0\0\0")."\0\0\0\0\0\0\0\0\0\0\0\0",
   '$message->pack' );

# Now lets put some interesting data in it
# We can't test more than one attribute at once because we can't guarantee
# the ordering it would apply

$message->nlattrs( { byte => 1 } );
is_hexstr( $message->nlmsg,
   bswap("\5\0").bswap("\1\0").bswap("\1")."\0\0\0",
   '$message->nlmsg with byte attr' );

$message->nlattrs( { short => 23 } );
is_hexstr( $message->nlmsg,
   bswap("\6\0").bswap("\2\0").bswap("\x17\0")."\0\0",
   '$message->nlmsg with short attr' );

$message->nlattrs( { long => 456 } );
is_hexstr( $message->nlmsg,
   bswap("\x08\0").bswap("\3\0").bswap("\xc8\1\0\0"),
   '$message->nlmsg with long attr' );

$message->nlattrs( { str => "ABCDE" } );
is_hexstr( $message->nlmsg,
   bswap("\x0a\0").bswap("\4\0")."ABCDE\0\0\0",
   '$message->nlmsg with str attr' );

$message->nlattrs( { raw => "X\0Y\0Z\0" } );
is_hexstr( $message->nlmsg,
   bswap("\x0a\0").bswap("\5\0")."X\0Y\0Z\0\0\0",
   '$message->nlmsg with raw attr' );

$message->nlattrs( { nest => { byte => 20 } } );
is_hexstr( $message->nlmsg,
   bswap("\x0c\0").bswap("\6\0").
      bswap("\5\0").bswap("\1\0").bswap("\x14")."\0\0\0",
   '$message->nlmsg with nested(byte) attr' );

$message = $sock->unpack_message(
   bswap("\x48\0\0\0")."\0\0\0\0\0\0\0\0\0\0\0\0".
      bswap("\5\0").bswap("\1\0").bswap("\7")."\0\0\0".
      bswap("\6\0").bswap("\2\0").bswap("\x59\0")."\0\0".
      bswap("\x08\0").bswap("\3\0").bswap("\xf3\3\0\0").
      bswap("\7\0").bswap("\4\0").bswap("FGH\0").
      bswap("\x0a\0").bswap("\5\0")."X\0Y\0Z\0\0\0".
      bswap("\x0c\0").bswap("\6\0").
         bswap("\5\0").bswap("\1\0").bswap("\x15")."\0\0\0",
);

ok( defined $message, '$sock->unpack_message defined' );

is_deeply( $message->nlattrs,
   {
      byte  => 7,
      short => 89,
      long  => 1011,
      str   => "FGH",
      raw   => "X\0Y\0Z\0",
      nest  => { byte => 21 },
   },
   '$message->attrs after unpack' );

is( $message->get_nlattr( "short" ), 89, '$message->get_nlattr' );

$message->change_nlattrs( long => 4321, str => "ZYX" );

is_deeply( $message->nlattrs,
   {
      byte  => 7,
      short => 89,
      long  => 4321,
      str   => "ZYX",
      raw   => "X\0Y\0Z\0",
      nest  => { byte => 21 },
   },
   '$message->attrs after change_nlattrs' );

$message->change_nlattrs( short => undef );

is_deeply( $message->nlattrs,
   {
      byte  => 7,
      long  => 4321,
      str   => "ZYX",
      raw   => "X\0Y\0Z\0",
      nest  => { byte => 21 },
   },
   '$message->attrs after change_nlattrs to delete' );