Selkie.git | t/ | 76-image-rgba-source.rakutest
use Test;
use lib 'lib';
use Selkie::Widget::Image;
use Selkie::Sizing;
# Headless contract for the in-memory RGBA ingress (set-rgba), mirroring
# 58-image-blit-cache.rakutest's approach: exercise the public API
# without a live notcurses plane (the actual blit needs a sprixel-capable
# terminal — covered by 71-image-rgba-blit.rakutest under a terminal).
plan 8;
# A 2×2 RGBA buffer (16 bytes) — smallest valid payload.
sub rgba2x2(--> Buf[uint8]) {
Buf[uint8].new(
255, 0, 0,255, 0,255, 0,255,
0, 0,255,255, 255,255,255,255,
);
}
subtest 'set-rgba sets a source-id and clears the file source' => {
plan 3;
my $img = Selkie::Widget::Image.new(sizing => Sizing.fixed(5));
$img.set-rgba(rgba2x2(), :width(2), :height(2), :id('img-a'));
is $img.source-id, 'img-a', 'source-id reflects the caller :id';
is $img.file, Str, 'file source is cleared when RGBA is set';
nok $img.has-blit-plane, 'set-rgba does not eagerly create a blit-plane';
};
subtest 'set-rgba without :id falls back to a gen token (never the bytes)' => {
plan 2;
my $img = Selkie::Widget::Image.new(sizing => Sizing.fixed(5));
$img.set-rgba(rgba2x2(), :width(2), :height(2));
ok $img.source-id.defined, 'a source-id is assigned';
like $img.source-id, /^ 'gen-' \d+ $/, 'gen token, not a pixel comparison';
};
subtest 'each :id-less set-rgba is treated as new content' => {
plan 1;
my $img = Selkie::Widget::Image.new(sizing => Sizing.fixed(5));
$img.set-rgba(rgba2x2(), :width(2), :height(2));
my $first = $img.source-id;
$img.set-rgba(rgba2x2(), :width(2), :height(2));
isnt $img.source-id, $first, 'gen token advances on each set';
};
subtest 'mismatched buffer size is rejected' => {
plan 2;
my $img = Selkie::Widget::Image.new(sizing => Sizing.fixed(5));
throws-like { $img.set-rgba(Buf[uint8].new(1, 2, 3), :width(2), :height(2)) },
Exception, message => /'width'/,
'too-small buffer dies with a dimension error';
throws-like { $img.set-rgba(Buf[uint8].new(0 xx 64), :width(2), :height(2)) },
Exception, 'too-large buffer dies too';
};
subtest 'switching RGBA → file restores the file source' => {
plan 3;
my $img = Selkie::Widget::Image.new(sizing => Sizing.fixed(5));
$img.set-rgba(rgba2x2(), :width(2), :height(2), :id('img-a'));
$img.set-file('/foo.png');
is $img.file, '/foo.png', 'file source set';
is $img.source-id, '/foo.png', 'source-id becomes the path';
nok $img.has-blit-plane, 'no eager blit on the switch';
};
subtest 'switching file → RGBA clears the file accessor' => {
plan 2;
my $img = Selkie::Widget::Image.new(sizing => Sizing.fixed(5));
$img.set-file('/foo.png');
$img.set-rgba(rgba2x2(), :width(2), :height(2), :id('img-b'));
is $img.file, Str, 'file accessor cleared by set-rgba';
is $img.source-id, 'img-b', 'source-id is the RGBA id';
};
subtest 'clear-image clears both file and RGBA sources' => {
plan 3;
my $img = Selkie::Widget::Image.new(sizing => Sizing.fixed(5));
$img.set-rgba(rgba2x2(), :width(2), :height(2), :id('img-a'));
$img.clear-image;
is $img.file, Str, 'file cleared';
is $img.source-id, Str, 'source-id cleared';
nok $img.has-blit-plane, 'no blit-plane after clear';
};
subtest 'destroy after set-rgba is safe' => {
plan 1;
my $img = Selkie::Widget::Image.new(sizing => Sizing.fixed(5));
$img.set-rgba(rgba2x2(), :width(2), :height(2), :id('img-a'));
lives-ok { $img.destroy-blit-plane; $img.destroy-blit-plane },
'destroy-blit-plane is idempotent with an RGBA source set';
};
done-testing;