all lists on lists.proxmox.com
 help / color / mirror / Atom feed
From: "Michael Köppl" <m.koeppl@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [PATCH guest-common v6 08/18] tests: add tests for used-guest-ids max file size handling
Date: Thu, 24 Sep 2026 18:15:00 +0200	[thread overview]
Message-ID: <20260924161510.847362-9-m.koeppl@proxmox.com> (raw)
In-Reply-To: <20260924161510.847362-1-m.koeppl@proxmox.com>

The test cases cover whether a file exceeding the thresholds is
correctly detected and also cover multiple scenarios for merging ranges
with gaps between them until file size is below the threshold again.

Signed-off-by: Michael Köppl <m.koeppl@proxmox.com>
---
 src/tests/guest-id-tests.pl | 102 ++++++++++++++++++++++++++++++++++++
 1 file changed, 102 insertions(+)

diff --git a/src/tests/guest-id-tests.pl b/src/tests/guest-id-tests.pl
index 3be492a..6482f64 100755
--- a/src/tests/guest-id-tests.pl
+++ b/src/tests/guest-id-tests.pl
@@ -4,11 +4,16 @@ use v5.36;
 
 use lib qw(..);
 
+use List::Util qw(all min sum0);
 use Test::MockModule;
 use Test::More;
 
 my $FILE = '/etc/pve/virtual-guest/used-guest-ids';
 
+# must match the thresholds in PVE::GuestID
+my $SIZE_WARN = 512 * 1024;
+my $SIZE_MAX = 768 * 1024;
+
 my ($parse, $write);
 my $raw; # contents of the stubbed used-guest-ids file
 my $writes = 0;
@@ -42,6 +47,38 @@ sub guests(@ids) {
     return { ids => { map { $_ => { node => 'localhost', type => 'qemu' } } @ids } };
 }
 
+# single IDs from 100 upwards, with the gaps between them cycling through @gaps
+sub single_ids($count, @gaps) {
+    my @lines;
+    my $id = 100;
+    for my $i (0 .. $count - 1) {
+        push @lines, "$id\n";
+        $id += 1 + $gaps[$i % @gaps];
+    }
+    return join('', @lines);
+}
+
+sub gap_sizes($ranges) {
+    my @gap_sizes;
+    for my $i (1 .. $#$ranges) {
+        push @gap_sizes, $ranges->[$i]->[0] - $ranges->[$i - 1]->[1] - 1;
+    }
+    return @gap_sizes;
+}
+
+# whether every range in $inner lies within a range in $outer, both sorted
+sub covers($outer, $inner) {
+    my $i = 0;
+    for my $range ($inner->@*) {
+        while ($i < @$outer && $outer->[$i]->[1] < $range->[0]) {
+            $i++;
+        }
+        return 0 if $i >= @$outer;
+        return 0 if $outer->[$i]->[0] > $range->[0] || $outer->[$i]->[1] < $range->[1];
+    }
+    return 1;
+}
+
 subtest 'parse' => sub {
     my $tests = [
         ['canonical file', "100-250\n300\n900-1200\n", [[100, 250], [300, 300], [900, 1200]]],
@@ -101,6 +138,71 @@ subtest 'write' => sub {
     }
 };
 
+subtest 'write size limit' => sub {
+    my sub write_with_warnings($input) {
+        my @warnings;
+        local $SIG{__WARN__} = sub { push @warnings, $_[0] };
+        my $output = $write->($FILE, $parse->($FILE, $input));
+        return ($output, \@warnings);
+    }
+
+    my $small = single_ids(50_000, 1);
+    ok(length($small) < $SIZE_WARN, 'small list is below the warning threshold');
+    my ($output, $warnings) = write_with_warnings($small);
+    is($output, $small, 'below the warning threshold: written unchanged');
+    is_deeply($warnings, [], 'below the warning threshold: no warning');
+
+    my $medium = single_ids(90_000, 1);
+    ok(length($medium) > $SIZE_WARN && length($medium) < $SIZE_MAX,
+        'medium list is in between');
+    ($output, $warnings) = write_with_warnings($medium);
+    is($output, $medium, 'above the warning threshold: written unchanged');
+    is(scalar($warnings->@*), 1, 'above the warning threshold: one warning');
+    like(
+        $warnings->[0],
+        qr/^\Q$FILE\E is \d+ KiB, .* closed beyond 768 KiB\n\z/,
+        'above the warning threshold: size is warned about',
+    );
+
+    # above the hard limit, all gaps are equal, so they get closed from
+    # the lowest ID upwards
+    my $large = single_ids(150_000, 1);
+    ok(length($large) > $SIZE_MAX, 'large list is above the limit');
+    my $input = $parse->($FILE, $large);
+    ($output, $warnings) = write_with_warnings($large);
+    my $closed = $parse->($FILE, $output);
+    ok(length($output) <= $SIZE_MAX, 'above the limit: output fits');
+    ok(covers($closed, $input), 'above the limit: every used ID is still recorded');
+    is(scalar($warnings->@*), 1, 'above the limit: one warning');
+    my ($count) =
+        ($warnings->[0] // '') =~ m/would exceed 768 KiB, recorded (\d+) unused guest IDs? /;
+    is(
+        $count,
+        sum0(gap_sizes($input)) - sum0(gap_sizes($closed)),
+        'above the limit: number of IDs marked as used is reported',
+    );
+    is($closed->[0]->[0], 100, 'equal gaps: the lowest ID starts the joined range');
+    ok(
+        (all { $_->[0] == $_->[1] } $closed->@[1 .. $#$closed]),
+        'equal gaps: only the lowest ones are closed',
+    );
+
+    # above the hard limit, mixed gaps
+    my $mixed = single_ids(200_000, 1, 2, 5, 50);
+    $input = $parse->($FILE, $mixed);
+    ($output, $warnings) = write_with_warnings($mixed);
+    $closed = $parse->($FILE, $output);
+    my @remaining = gap_sizes($closed);
+    ok(length($output) <= $SIZE_MAX, 'mixed gaps: output fits');
+    ok(covers($closed, $input), 'mixed gaps: every used ID is still recorded');
+    is(min(@remaining), 5, 'mixed gaps: all gaps of 1 and 2 are closed first');
+    is(
+        scalar(grep { $_ == 50 } @remaining),
+        scalar(grep { $_ == 50 } gap_sizes($input)),
+        'mixed gaps: the largest gaps are kept',
+    );
+};
+
 subtest 'get_next_unused_id' => sub {
     my $tests = [
         # name, file contents, existing guests, input ID, expected ID
-- 
2.47.3





  parent reply	other threads:[~2026-09-24 16:15 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-24 16:14 [PATCH many v6 00/18] add option to prevent suggesting previously used VMIDs Michael Köppl
2026-09-24 16:14 ` [PATCH cluster v6 01/18] cluster files: add virtual-guest/used-guest-ids Michael Köppl
2026-09-24 16:14 ` [PATCH cluster v6 02/18] datacenter config: add unique subproperty to next-id Michael Köppl
2026-09-24 16:14 ` [PATCH cluster v6 03/18] datacenter config: next-id: add enforce subproperty Michael Köppl
2026-09-24 16:14 ` [PATCH guest-common v6 04/18] add module to track previously used guest IDs Michael Köppl
2026-09-24 16:14 ` [PATCH guest-common v6 05/18] tests: add tests for used guest ID tracking Michael Köppl
2026-09-24 16:14 ` [PATCH guest-common v6 06/18] abstract config: register used guest ID when creating config Michael Köppl
2026-09-24 16:14 ` [PATCH guest-common v6 07/18] guest id: keep used ID list below the pmxcfs file size limit Michael Köppl
2026-09-24 16:15 ` Michael Köppl [this message]
2026-09-24 16:15 ` [PATCH guest-common v6 09/18] guest id: optionally enforce the next-id range and uniqueness Michael Köppl
2026-09-24 16:15 ` [PATCH qemu-server v6 10/18] api: record VM ID as used on destruction and remote migration Michael Köppl
2026-09-24 16:15 ` [PATCH qemu-server v6 11/18] api, remote migrate: exempt existing VMs from next-id enforcement Michael Köppl
2026-09-24 16:15 ` [PATCH container v6 12/18] api: record CT ID as used on destruction and remote migration Michael Köppl
2026-09-24 16:15 ` [PATCH container v6 13/18] api, migrate: exempt existing CTs from next-id enforcement Michael Köppl
2026-09-24 16:15 ` [PATCH manager v6 14/18] fix #4369: api: optionally only suggest unique IDs Michael Köppl
2026-09-24 16:15 ` [PATCH manager v6 15/18] ui: dc options: rename VMID to guest ID Michael Köppl
2026-09-24 16:15 ` [PATCH manager v6 16/18] fix #4369: ui: dc options: add option for unique VM/CT IDs Michael Köppl
2026-09-24 16:15 ` [PATCH manager v6 17/18] api: nextid: reject IDs forbidden by next-id enforcement Michael Köppl
2026-09-24 16:15 ` [PATCH manager v6 18/18] ui: dc options: add option to enforce next free guest ID settings Michael Köppl

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260924161510.847362-9-m.koeppl@proxmox.com \
    --to=m.koeppl@proxmox.com \
    --cc=pve-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal