App-OpenHAP
view release on metacpan or search on metacpan
t/conformance/hap-tlv8.t view on Meta::CPAN
#!/usr/bin/env perl
# ex:ts=8 sw=4:
# Conformance tests for spec/HAP-TLV8.md
use v5.36;
use Test::More;
use FindBin qw($RealBin);
use lib "$RealBin/../../lib";
use lib "$RealBin/../lib";
use Fugu::TestLog;
use_ok('Protocol::HAP::TLV');
use_ok('Protocol::HAP::Pairing');
subtest '[HAP-TLV8 §1] basic record structure' => sub {
my $encoded = Protocol::HAP::TLV::encode( 0x06, "\x01" );
is( length($encoded), 3, 'record is type + length + value' );
is( unpack( 'H*', $encoded ), '060101',
'type, length and value bytes in order' );
my $empty = Protocol::HAP::TLV::encode( 0x0A, '' );
is( unpack( 'H*', $empty ), '0a00', 'zero-length value encodes' );
};
subtest '[HAP-TLV8 §2] fragmentation of long values' => sub {
my $value = 'X' x 500;
my $encoded = Protocol::HAP::TLV::encode( 0x03, $value );
# 500 bytes -> 255 + 245: 2 records with 2-byte headers each
is( length($encoded), 500 + 4, 'two records for 500 bytes' );
# The first fragment must be exactly 255 bytes
my ( $t1, $l1 ) = unpack( 'CC', substr( $encoded, 0, 2 ) );
is( $t1, 0x03, 'first fragment has value type' );
is( $l1, 255, 'non-final fragment is exactly 255 bytes' );
my ( $t2, $l2 ) = unpack( 'CC', substr( $encoded, 257, 2 ) );
is( $t2, 0x03, 'second fragment has same type' );
is( $l2, 245, 'final fragment carries the remainder' );
# The decoder concatenates same-type records
my %decoded = Protocol::HAP::TLV::decode($encoded);
is( $decoded{0x03}, $value, 'fragments concatenated on decode' );
};
subtest '[HAP-TLV8 §8][HAP-TLV8 §8.2] 384-byte SRP key splits FF/81' =>
sub {
my $key = 'K' x 384;
my $encoded = Protocol::HAP::TLV::encode( 0x03, $key );
is( length($encoded), 384 + 4, '384 bytes need two records' );
is( unpack( 'C', substr( $encoded, 1, 1 ) ),
0xFF, 'first fragment length byte is FF (255)' );
is( unpack( 'C', substr( $encoded, 257 + 1, 1 ) ),
0x81, 'second fragment length byte is 81 (129)' );
my %decoded = Protocol::HAP::TLV::decode($encoded);
is( length( $decoded{0x03} ), 384, 'round-trips to 384 bytes' );
};
subtest '[HAP-TLV8 §3] separators between list items' => sub {
my $encoded = Protocol::HAP::TLV::encode(
Protocol::HAP::Pairing::kTLVType_Separator(), '' );
is( unpack( 'H*', $encoded ), 'ff00',
'separator is type 0xFF with zero length' );
};
subtest '[HAP-TLV8 §4] value encodings' => sub {
# [HAP-TLV8 §4.1] integers are little-endian and use the minimum
# bytes
is( unpack( 'H*', Protocol::HAP::TLV::encode( 0x0B, pack( 'C', 1 ) ) ),
'0b0101', '[HAP-TLV8 §4.1] integer 1 encodes as 01' );
is( unpack( 'H*', Protocol::HAP::TLV::encode( 0x0B, pack( 'v', 256 ) ) ),
'0b020001',
'[HAP-TLV8 §4.1] integer 256 encodes as 00 01 (LE)' );
is( unpack( 'H*', Protocol::HAP::TLV::encode( 0x0B, pack( 'V', 65536 ) ) ),
'0b0400000100',
'[HAP-TLV8 §4.1] integer 65536 encodes as 00 00 01 00 (LE)' );
# [HAP-TLV8 §4.2] strings without null terminator
is( unpack( 'H*', Protocol::HAP::TLV::encode( 0x01, 'Hello' ) ),
'010548656c6c6f',
'[HAP-TLV8 §4.2] UTF-8 bytes, no null terminator' );
# [HAP-TLV8 §4.3] binary data round-trips raw
my $binary = pack( 'H*', '00ff10deadbeef' );
my %decoded =
Protocol::HAP::TLV::decode( Protocol::HAP::TLV::encode( 0x05, $binary ) );
is( unpack( 'H*', $decoded{0x05} ),
'00ff10deadbeef',
'[HAP-TLV8 §4.3] raw bytes preserved' );
# [HAP-TLV8 §4.4] a TLV value can be a nested TLV structure
my $inner = Protocol::HAP::TLV::encode( 0x01, 'id', 0x0A, 'sig' );
my %outer =
Protocol::HAP::TLV::decode( Protocol::HAP::TLV::encode( 0x05, $inner ) );
my %nested = Protocol::HAP::TLV::decode( $outer{0x05} );
is( $nested{0x01}, 'id',
'[HAP-TLV8 §4.4] nested TLV decodes from sub-TLV value' );
is( $nested{0x0A}, 'sig', '[HAP-TLV8 §4.4] all nested fields kept' );
};
subtest '[HAP-TLV8 §5] pairing TLV type codes' => sub {
my %types = (
Method => 0x00,
Identifier => 0x01,
Salt => 0x02,
PublicKey => 0x03,
Proof => 0x04,
EncryptedData => 0x05,
State => 0x06,
Error => 0x07,
RetryDelay => 0x08,
Certificate => 0x09,
( run in 0.404 second using v1.01-cache-2.11-cpan-b16cb0d3907 )