mirror of
https://codeberg.org/h3xx/you-dont-need-pihole.git
synced 2026-06-14 17:55:39 +00:00
181 lines
4.9 KiB
Perl
181 lines
4.9 KiB
Perl
#!perl
|
|
use 5.012;
|
|
use warnings FATAL => 'all';
|
|
|
|
use Test::More 'no_plan';
|
|
|
|
use FindBin qw//;
|
|
my $changelog = "$FindBin::Bin/../CHANGELOG.md";
|
|
my $braced_inner_r = qr/(?:[^\]\\]|\\.)*/;
|
|
use constant PROJECT_GIT => 'https://codeberg.org/h3xx/you-dont-need-pihole';
|
|
|
|
SKIP: {
|
|
my $clh;
|
|
unless (open $clh, '<', $changelog) {
|
|
skip("failed to open changelog file to test: $!");
|
|
}
|
|
my @cl_lines = <$clh>;
|
|
close $clh;
|
|
|
|
ok(scalar @cl_lines,
|
|
'changelog contains at least one line'
|
|
);
|
|
|
|
my %end_links = _link_references(@cl_lines);
|
|
my @reflinks = _ref_links(@cl_lines);
|
|
|
|
my @unresolved = _unresolved_links(\%end_links, \@reflinks);
|
|
ok(!scalar @unresolved, ChangelogError->new('unresolved links', @unresolved));
|
|
|
|
my %unused_links = _unused_links(\%end_links, \@reflinks);
|
|
ok(!scalar %unused_links, ChangelogError->new('unused end links', values %unused_links));
|
|
|
|
my @incorrect_links = _incorrect_end_links(\%end_links, \@reflinks);
|
|
ok(!scalar @incorrect_links, ChangelogError->new('incorrect end links', @incorrect_links));
|
|
|
|
}
|
|
|
|
sub _link_references {
|
|
my @lines = @_;
|
|
my $end_link_r = qr/^\s*\[(?<key>$braced_inner_r)\]:\s*(?<url>.*?)\s*$/;
|
|
my %end_links;
|
|
my $line_number = 0;
|
|
foreach my $line (@lines) {
|
|
++$line_number;
|
|
if ($line =~ $end_link_r) {
|
|
$end_links{$+{key}} = {
|
|
line_number => $line_number,
|
|
url => $+{url},
|
|
key => $+{key},
|
|
};
|
|
}
|
|
}
|
|
return %end_links;
|
|
}
|
|
|
|
sub _ref_links {
|
|
my @lines = @_;
|
|
my $ref_link_r = qr/\[(?<text>$braced_inner_r)\]\[(?<key>$braced_inner_r)\]/;
|
|
# Make sure not to catch:
|
|
# - Normal links "[text](url)"
|
|
# - End links "[key]: url"
|
|
my $bare_ref_link_r = qr/\[(?<textkey>$braced_inner_r)\](?!\s*[(:])/;
|
|
my @reflinks;
|
|
my $line_number = 0;
|
|
foreach my $line (@lines) {
|
|
++$line_number;
|
|
if ($line =~ $ref_link_r) {
|
|
push @reflinks, {
|
|
line_number => $line_number,
|
|
text => $+{text},
|
|
key => $+{key},
|
|
};
|
|
} elsif ($line =~ $bare_ref_link_r) {
|
|
push @reflinks, {
|
|
line_number => $line_number,
|
|
text => $+{textkey},
|
|
key => $+{textkey},
|
|
};
|
|
}
|
|
}
|
|
return @reflinks;
|
|
}
|
|
|
|
sub _unused_links {
|
|
my ($end_links, $reflinks) = @_;
|
|
# Make a copy, lest we destroy data
|
|
my %unused = %{$end_links};
|
|
foreach my $link (@{$reflinks}) {
|
|
my $key = $link->{key};
|
|
delete $unused{$key};
|
|
}
|
|
return %unused;
|
|
}
|
|
|
|
sub _unresolved_links {
|
|
my ($end_links, $reflinks) = @_;
|
|
my @unresolved;
|
|
foreach my $link (@{$reflinks}) {
|
|
my $key = $link->{key};
|
|
unless (exists $end_links->{$key}) {
|
|
push @unresolved, $link;
|
|
}
|
|
}
|
|
return @unresolved;
|
|
}
|
|
|
|
sub _incorrect_end_links {
|
|
my ($end_links, $reflinks) = @_;
|
|
my @versions_in_order;
|
|
my %versions_seen;
|
|
foreach my $link (@{$reflinks}) {
|
|
my $key = $link->{key};
|
|
if (_is_version($key) && !exists $versions_seen{$key}) {
|
|
$versions_seen{$key} = 1;
|
|
push @versions_in_order, $key;
|
|
}
|
|
}
|
|
my %expected_links = (
|
|
Unreleased => _make_tag_link("v$versions_in_order[0]", 'HEAD'),
|
|
$versions_in_order[-1] => _make_tag_link("v$versions_in_order[-1]"),
|
|
);
|
|
foreach my $idx (0 .. ($#versions_in_order - 1)) {
|
|
my $this_version = $versions_in_order[$idx];
|
|
my $last_version = $versions_in_order[$idx + 1];
|
|
$expected_links{$this_version} = _make_tag_link("v$last_version", "v$this_version");
|
|
}
|
|
|
|
my @incorrect_links;
|
|
while (my ($key, $link) = each %{$end_links}) {
|
|
if (exists $expected_links{$key}) {
|
|
my $got = $link->{url};
|
|
my $expected = $expected_links{$key};
|
|
if ($got ne $expected) {
|
|
$link->{url_expected} = $expected;
|
|
push @incorrect_links, $link;
|
|
}
|
|
}
|
|
}
|
|
return @incorrect_links;
|
|
}
|
|
|
|
sub _make_tag_link {
|
|
my ($before_tag, $after_tag) = @_;
|
|
unless (defined $after_tag) {
|
|
return sprintf '%s/releases/tag/%s', PROJECT_GIT, $before_tag;
|
|
}
|
|
return sprintf '%s/compare/%s...%s', PROJECT_GIT, $before_tag, $after_tag;
|
|
}
|
|
|
|
sub _is_version {
|
|
my $ver = shift;
|
|
return $ver =~ /^[0-9.]+$/;
|
|
}
|
|
|
|
package ChangelogError;
|
|
use 5.012;
|
|
use warnings FATAL => 'all';
|
|
use overload '""' => '_as_string';
|
|
|
|
sub new {
|
|
my ($class, $name, @data) = @_;
|
|
return bless {
|
|
_name => $name,
|
|
_data => \@data,
|
|
}, $class;
|
|
}
|
|
|
|
sub _as_string {
|
|
my $self = shift;
|
|
my @all = ($self->{_name});
|
|
foreach my $datum (@{$self->{_data}}) {
|
|
my @out;
|
|
foreach my $key (qw/ line_number key text url url_expected /) {
|
|
if (defined $datum->{$key}) {
|
|
push @out, "$key: $datum->{$key}";
|
|
}
|
|
}
|
|
push @all, '- ' . (join ', ', @out);
|
|
}
|
|
return join "\n", @all;
|
|
}
|