File-Raw-Base64
view release on metacpan or search on metacpan
t/02-roundtrip.t view on Meta::CPAN
for my $len (0, 1, 2, 3, 4, 7, 16, 64, 100, 1024, 65535) {
my $bytes = random_bytes($len);
# encode to file
my ($efh, $epath) = tempfile(UNLINK => 1);
close $efh;
file_spew($epath, $bytes, plugin => 'base64');
# read encoded form back as bytes (no plugin) just to make sure
# it's pure ASCII
my $enc = do { local (@ARGV, $/) = $epath; <> };
$enc = '' unless defined $enc;
like($enc, qr/^[A-Za-z0-9+\/=]*$/, "len=$len: encoded form is pure base64");
# decode through the plugin, expect identical bytes
my $got = file_slurp($epath, plugin => 'base64');
is($got, $bytes, "len=$len: round-trip preserves bytes")
or diag(sprintf("got %d bytes, want %d", length($got), length($bytes)));
}
done_testing;
t/03-urlsafe.t view on Meta::CPAN
my $payload = "\xff\xff\xff" . # all 1 bits -> "////"
"\xfb\xef\xbe"; # bits flipped to land on 62 indices
my ($f1, $p1) = tempfile(UNLINK => 1); close $f1;
my ($f2, $p2) = tempfile(UNLINK => 1); close $f2;
file_spew($p1, $payload, plugin => 'base64');
file_spew($p2, $payload, plugin => 'base64url');
my $std = do { local (@ARGV, $/) = $p1; <> };
my $url = do { local (@ARGV, $/) = $p2; <> };
# URL-safe is the same as standard with + -> -, / -> _
(my $expected = $std) =~ tr{+/}{-_};
is($url, $expected, 'base64url alphabet substitution: + -> -, / -> _');
# Round-trip via base64url
my ($f3, $p3) = tempfile(UNLINK => 1);
print $f3 $url; close $f3;
my $got = file_slurp($p3, plugin => 'base64url');
is($got, $payload, 'base64url decode round-trip');
# urlsafe option on the standard plugin overrides the alphabet
my ($f4, $p4) = tempfile(UNLINK => 1); close $f4;
file_spew($p4, $payload, plugin => 'base64', urlsafe => 1);
my $forced = do { local (@ARGV, $/) = $p4; <> };
is($forced, $expected,
'base64 plugin + urlsafe => 1 produces URL-safe alphabet');
done_testing;
t/04-wrap.t view on Meta::CPAN
use File::Raw qw(import);
use File::Temp qw(tempfile);
# 200 bytes -> ~268 base64 chars; enough to wrap several times at 64 / 76
my $payload = 'x' x 200;
# wrap => 0 (default): single line, no embedded newlines
{
my ($fh, $p) = tempfile(UNLINK => 1); close $fh;
file_spew($p, $payload, plugin => 'base64');
my $enc = do { local (@ARGV, $/) = $p; <> };
unlike($enc, qr/\n/, 'wrap => 0: no embedded newlines');
}
# wrap => 64: lines split every 64 chars (PEM style)
{
my ($fh, $p) = tempfile(UNLINK => 1); close $fh;
file_spew($p, $payload, plugin => 'base64', wrap => 64);
my $enc = do { local (@ARGV, $/) = $p; <> };
my @lines = split /\n/, $enc, -1;
pop @lines if @lines && $lines[-1] eq ''; # trailing newline
for my $line (@lines[0 .. $#lines - 1]) {
is(length($line), 64, "wrap => 64: full line is 64 chars");
}
cmp_ok(length($lines[-1]), '<=', 64,
'wrap => 64: last line <= 64 chars');
}
# wrap => 76 (MIME)
{
my ($fh, $p) = tempfile(UNLINK => 1); close $fh;
file_spew($p, $payload, plugin => 'base64', wrap => 76);
my $enc = do { local (@ARGV, $/) = $p; <> };
my @lines = split /\n/, $enc, -1;
pop @lines if @lines && $lines[-1] eq '';
for my $line (@lines[0 .. $#lines - 1]) {
is(length($line), 76, "wrap => 76: full line is 76 chars");
}
}
# Decode of wrapped output round-trips
{
my ($fh, $p) = tempfile(UNLINK => 1); close $fh;
file_spew($p, $payload, plugin => 'base64', wrap => 64);
my $back = file_slurp($p, plugin => 'base64');
is($back, $payload, 'decode strips embedded newlines, round-trips');
}
# Custom eol: \r\n
{
my ($fh, $p) = tempfile(UNLINK => 1); close $fh;
file_spew($p, $payload, plugin => 'base64', wrap => 64, eol => "\r\n");
my $enc = do { local (@ARGV, $/) = $p; <> };
like($enc, qr/\r\n/, 'eol => "\\r\\n" emits CRLF');
my $back = file_slurp($p, plugin => 'base64');
is($back, $payload, 'decode tolerates CRLF line endings');
}
done_testing;
my $der = "\x30\x82\x01\x22" . ('x' x 60); # 64 bytes; pretend it's DER
{
my ($fh, $p) = tempfile(UNLINK => 1); close $fh;
file_spew($p, $der,
plugin => 'base64',
pem => 1,
pem_label => 'CERTIFICATE',
wrap => 64,
);
my $pem = do { local (@ARGV, $/) = $p; <> };
like($pem, qr/^-----BEGIN CERTIFICATE-----\n/, 'PEM has BEGIN line');
like($pem, qr/\n-----END CERTIFICATE-----\n?$/, 'PEM has END line');
# Round-trip via decode
my $back = file_slurp($p, plugin => 'base64', pem => 1);
is($back, $der, 'PEM round-trip: decode reproduces the DER bytes');
}
# Different label
{
my ($fh, $p) = tempfile(UNLINK => 1); close $fh;
file_spew($p, $der,
plugin => 'base64',
pem => 1,
pem_label => 'PRIVATE KEY',
wrap => 64,
);
my $pem = do { local (@ARGV, $/) = $p; <> };
like($pem, qr/-----BEGIN PRIVATE KEY-----/, 'custom pem_label honoured');
my $back = file_slurp($p, plugin => 'base64', pem => 1);
is($back, $der, 'PEM round-trip with PRIVATE KEY label');
}
# Decode tolerates extra whitespace inside the PEM envelope
{
my ($fh, $p) = tempfile(UNLINK => 1);
print $fh "-----BEGIN DATA-----\n",
t/07-padding.t view on Meta::CPAN
use warnings;
use Test::More;
use File::Raw::Base64;
use File::Raw qw(import);
use File::Temp qw(tempfile);
# Default: padding is on, matching RFC 4648
{
my ($fh, $p) = tempfile(UNLINK => 1); close $fh;
file_spew($p, 'f', plugin => 'base64');
my $enc = do { local (@ARGV, $/) = $p; <> };
is($enc, 'Zg==', 'default padding produces "Zg=="');
}
# padding => 0: strip trailing '='
{
my ($fh, $p) = tempfile(UNLINK => 1); close $fh;
file_spew($p, 'f', plugin => 'base64', padding => 0);
my $enc = do { local (@ARGV, $/) = $p; <> };
is($enc, 'Zg', 'padding => 0 strips trailing "="');
}
# Decoder is always tolerant of missing padding
{
my ($fh, $p) = tempfile(UNLINK => 1);
print $fh 'Zg';
close $fh;
my $b = file_slurp($p, plugin => 'base64');
is($b, 'f', 'decoder accepts missing padding');
}
# URL-safe + no padding (the JWT idiom): round-trip
{
my $payload = "Hello, JWT!";
my ($fh, $p) = tempfile(UNLINK => 1); close $fh;
file_spew($p, $payload,
plugin => 'base64url',
padding => 0,
);
my $enc = do { local (@ARGV, $/) = $p; <> };
unlike($enc, qr/=/, 'urlsafe + padding=0: no "=" in output');
my $back = file_slurp($p, plugin => 'base64url');
is($back, $payload, 'urlsafe + padding=0 round-trip');
}
done_testing;
t/08-empty-and-large.t view on Meta::CPAN
use warnings;
use Test::More;
use File::Raw::Base64;
use File::Raw qw(import);
use File::Temp qw(tempfile);
# Empty input round-trips to empty output (both directions)
{
my ($fh, $p) = tempfile(UNLINK => 1); close $fh;
file_spew($p, '', plugin => 'base64');
my $enc = do { local (@ARGV, $/) = $p; <> };
$enc = '' unless defined $enc;
is($enc, '', 'encode empty -> empty');
my $b = file_slurp($p, plugin => 'base64');
is($b, '', 'decode empty -> empty');
}
# Larger-than-default-buffer round-trip. We deliberately stay modest
# (512 KiB) and build the payload via a tiny deterministic LCG instead
# of `pack 'C*', map { ... } 1..N` - the map-list approach builds an
( run in 1.274 second using v1.01-cache-2.11-cpan-8f98c5d2c55 )