From 0300e01870c6dd6663c0a02f839f7ad7ccd85563 Mon Sep 17 00:00:00 2001 From: Hayato Kuroda Date: Fri, 23 Oct 2026 12:13:38 +0900 Subject: [PATCH v5 2/2] Add fuzzy test --- src/test/recovery/meson.build | 1 + src/test/recovery/t/061_backup_label.pl | 126 ++++++++++++++++++++++++ 2 files changed, 127 insertions(+) create mode 100644 src/test/recovery/t/061_backup_label.pl diff --git a/src/test/recovery/meson.build b/src/test/recovery/meson.build index b6ac9dd07a7..c022b7d87dd 100644 --- a/src/test/recovery/meson.build +++ b/src/test/recovery/meson.build @@ -69,6 +69,7 @@ tests += { 't/058_shutdown_crash_restart.pl', 't/059_remote_apply_status_interval.pl', 't/060_multixact_crashed_updater.pl', + 't/061_backup_label.pl', ], }, } diff --git a/src/test/recovery/t/061_backup_label.pl b/src/test/recovery/t/061_backup_label.pl new file mode 100644 index 00000000000..90c62ca0da3 --- /dev/null +++ b/src/test/recovery/t/061_backup_label.pl @@ -0,0 +1,126 @@ +# Copyright (c) 2026, PostgreSQL Global Development Group + +# Test parsing of LSN fields in backup_label. + +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; +use Test::More; + +my @tests = ( + { + name => 'nul_in_start_lsn', + start_lsn => "0/123\0junk", + checkpoint_lsn => '0/1', + }, + { + name => 'nul_after_start_lsn', + start_lsn => "0/123\0", + checkpoint_lsn => '0/1', + }, + { + name => 'nul_in_checkpoint_lsn', + start_lsn => '0/1', + checkpoint_lsn => "0/123\0junk", + }, + { + name => 'nul_after_checkpoint_lsn', + start_lsn => '0/1', + checkpoint_lsn => "0/123\0", + }, + { + name => 'non_hexadecimal_start_lsn', + start_lsn => '0/12G', + checkpoint_lsn => '0/1', + }, + { + name => 'non_hexadecimal_checkpoint_lsn', + start_lsn => '0/1', + checkpoint_lsn => '0/12G', + }, + { + name => 'non_hexadecimal_start_lsn_prefix', + start_lsn => 'G/1', + checkpoint_lsn => '0/1', + }, + { + name => 'non_hexadecimal_checkpoint_lsn_prefix', + start_lsn => '0/1', + checkpoint_lsn => 'G/1', + }, + { + name => 'extra_slash_in_start_lsn', + start_lsn => '0/1/2', + checkpoint_lsn => '0/1', + }, + { + name => 'missing_slash_in_checkpoint_lsn', + start_lsn => '0/1', + checkpoint_lsn => '01', + }, + { + name => 'missing_high_component_in_start_lsn', + start_lsn => '/1', + checkpoint_lsn => '0/1', + }, + { + name => 'missing_low_component_in_checkpoint_lsn', + start_lsn => '0/1', + checkpoint_lsn => '0/', + }, + { + name => 'whitespace_in_start_lsn', + start_lsn => '0 /1', + checkpoint_lsn => '0/1', + }, + { + name => 'overlong_start_lsn', + start_lsn => '123456789/1', + checkpoint_lsn => '0/1', + }, + { + name => 'overlong_checkpoint_lsn', + start_lsn => '0/1', + checkpoint_lsn => '123456789/1', + }, + { + name => 'incremental_lsn', + start_lsn => '0/1', + checkpoint_lsn => '0/1', + incremental_lsn => '0/1', + error => 'this is an incremental backup, not a data directory', + }, + { + name => 'nul_in_incremental_lsn', + start_lsn => '0/1', + checkpoint_lsn => '0/1', + incremental_lsn => "0/123\0junk", + error => 'this is an incremental backup, not a data directory', + }, +); + +for my $test (@tests) +{ + my $node = PostgreSQL::Test::Cluster->new($test->{name}); + $node->init; + + open my $fh, '>', $node->data_dir . '/backup_label' + or BAIL_OUT("could not create backup_label: $!"); + binmode $fh; + print $fh + "START WAL LOCATION: $test->{start_lsn} " + . "(file 000000010000000000000000)\n"; + print $fh "CHECKPOINT LOCATION: $test->{checkpoint_lsn}\n"; + print $fh "INCREMENTAL FROM LSN: $test->{incremental_lsn}\n" + if defined $test->{incremental_lsn}; + close $fh or BAIL_OUT("could not close backup_label: $!"); + + is($node->start(fail_ok => 1), 0, "$test->{name} prevents startup"); + my $error = $test->{error} // 'invalid data in file "backup_label"'; + ok($node->log_contains(qr/FATAL: .*\Q$error\E/), + "$test->{name} is rejected"); +} + +done_testing(); -- 2.52.0