<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">package Test2::Compare::Base;
use strict;
use warnings;

our $VERSION = '0.000162';

use Carp qw/confess croak/;
use Scalar::Util qw/blessed/;

use Test2::Util::Sub qw/sub_info/;
use Test2::Compare::Delta();

sub MAX_CYCLES() { 75 }

use Test2::Util::HashBase qw{builder _file _lines _info called};
use Test2::Util::Ref qw/render_ref/;

{
    no warnings 'once';
    *set_lines = \&amp;set__lines;
    *set_file  = \&amp;set__file;
}

sub clone {
    my $self = shift;
    my $class = blessed($self);

    # Shallow copy is good enough for all the current compare types.
    return bless({%$self}, $class);
}

sub init {
    my $self = shift;
    $self-&gt;{+_LINES} = delete $self-&gt;{lines} if exists $self-&gt;{lines};
    $self-&gt;{+_FILE}  = delete $self-&gt;{file}  if exists $self-&gt;{file};
}

sub file {
    my $self = shift;
    return $self-&gt;{+_FILE} if $self-&gt;{+_FILE};

    if ($self-&gt;{+BUILDER}) {
        $self-&gt;{+_INFO} ||= sub_info($self-&gt;{+BUILDER});
        return $self-&gt;{+_INFO}-&gt;{file};
    }
    elsif ($self-&gt;{+CALLED}) {
        return $self-&gt;{+CALLED}-&gt;[1];
    }

    return undef;
}

sub lines {
    my $self = shift;
    return $self-&gt;{+_LINES} if $self-&gt;{+_LINES};

    if ($self-&gt;{+BUILDER}) {
        $self-&gt;{+_INFO} ||= sub_info($self-&gt;{+BUILDER});
        return $self-&gt;{+_INFO}-&gt;{lines} if @{$self-&gt;{+_INFO}-&gt;{lines}};
    }
    if ($self-&gt;{+CALLED}) {
        return [$self-&gt;{+CALLED}-&gt;[2]];
    }
    return [];
}

sub delta_class { 'Test2::Compare::Delta' }

sub deltas { () }
sub got_lines { () }

sub stringify_got { 0 }

sub operator { '' }
sub verify   { confess "unimplemented" }
sub name     { confess "unimplemented" }

sub render {
    my $self = shift;
    return $self-&gt;name;
}

sub run {
    my $self = shift;
    my %params = @_;

    my $id      = $params{id};
    my $convert = $params{convert} or confess "no convert sub provided";
    my $seen    = $params{seen} ||= {};

    $params{exists} = exists $params{got} ? 1 : 0
        unless exists $params{exists};

    my $exists = $params{exists};
    my $got = $exists ? $params{got} : undef;

    my $gotname = render_ref($got);

    # Prevent infinite cycles
    if (defined($got) &amp;&amp; ref $got) {
        die "Cycle detected in comparison, aborting"
            if $seen-&gt;{$gotname} &amp;&amp; $seen-&gt;{$gotname} &gt;= MAX_CYCLES;
        $seen-&gt;{$gotname}++;
    }

    my $ok = $self-&gt;verify(%params);
    my @deltas = $ok ? $self-&gt;deltas(%params) : ();

    $seen-&gt;{$gotname}-- if defined $got &amp;&amp; ref $got;

    return if $ok &amp;&amp; !@deltas;

    return $self-&gt;delta_class-&gt;new(
        verified =&gt; $ok,
        id       =&gt; $id,
        got      =&gt; $got,
        check    =&gt; $self,
        children =&gt; \@deltas,
        $exists ? () : (dne =&gt; 'got'),
    );
}

1;

__END__

=pod

=encoding UTF-8

=head1 NAME

Test2::Compare::Base - Base class for comparison classes.

=head1 DESCRIPTION

All comparison classes for Test2::Compare should inherit from this base class.

=head1 SYNOPSIS

    package Test2::Compare::MyCheck;
    use strict;
    use warnings;

    use base 'Test2::Compare::Base';
    use Test2::Util::HashBase qw/stuff/;

    sub name { 'STUFF' }

    sub operator {
        my $self = shift;
        my ($got) = @_;
        return 'eq';
    }

    sub verify {
        my $self = shift;
        my $params = @_;

        # Always check if $got exists! This method must return false if no
        # value at all was received.
        return 0 unless $params{exists};

        my $got = $params{got};

        # Returns true if both values match. This includes undef, 0, and other
        # false-y values!
        return $got eq $self-&gt;stuff;
    }

=head1 METHODS

Some of these must be overridden, others can be.

=over 4

=item $dclass = $check-&gt;delta_class

Returns the delta subclass that should be used. By default
L&lt;Test2::Compare::Delta&gt; is used.

=item @deltas = $check-&gt;deltas(id =&gt; $id, exists =&gt; $bool, got =&gt; $got, convert =&gt; \&amp;convert, seen =&gt; \%seen)

Should return child deltas.

=item @lines = $check-&gt;got_lines($got)

This is your chance to provide line numbers for errors in the C&lt;$got&gt;
structure.

=item $op = $check-&gt;operator()

=item $op = $check-&gt;operator($got)

Returns the operator that was used to compare the check with the received data
in C&lt;$got&gt;. If there was no value for got then there will be no arguments,
undef will only be an argument if undef was seen in C&lt;$got&gt;. This is how you
can tell the difference between a missing value and an undefined one.

=item $bool = $check-&gt;verify(id =&gt; $id, exists =&gt; $bool, got =&gt; $got, convert =&gt; \&amp;convert, seen =&gt; \%seen)

Return true if there is a shallow match, that is both items are arrayrefs, both
items are the same string or same number, etc. This should not recurse, as deep
checks are done in C&lt;&lt; $check-&gt;deltas() &gt;&gt;.

=item $name = $check-&gt;name

Get the name of the check.

=item $display = $check-&gt;render

What should be displayed in a table for this check, usually the name or value.

=item $delta = $check-&gt;run(id =&gt; $id, exists =&gt; $bool, got =&gt; $got, convert =&gt; \&amp;convert, seen =&gt; \%seen)

This is where the checking is done, first a shallow check using
C&lt;&lt; $check-&gt;verify &gt;&gt;, then checking C&lt;&lt; $check-&gt;deltas() &gt;&gt;. C&lt;\%seen&gt; is used
to prevent cycles.

=back

=head1 SOURCE

The source code repository for Test2-Suite can be found at
F&lt;https://github.com/Test-More/Test2-Suite/&gt;.

=head1 MAINTAINERS

=over 4

=item Chad Granum E&lt;lt&gt;exodist@cpan.orgE&lt;gt&gt;

=back

=head1 AUTHORS

=over 4

=item Chad Granum E&lt;lt&gt;exodist@cpan.orgE&lt;gt&gt;

=back

=head1 COPYRIGHT

Copyright 2018 Chad Granum E&lt;lt&gt;exodist@cpan.orgE&lt;gt&gt;.

This program is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.

See F&lt;http://dev.perl.org/licenses/&gt;

=cut
</pre></body></html>