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;