summaryrefslogtreecommitdiffstats
path: root/vcx_patch.t
diff options
context:
space:
mode:
authorSadeep Madurange <sadeep@asciimx.com>2026-04-06 15:02:39 +0800
committerSadeep Madurange <sadeep@asciimx.com>2026-04-08 21:10:01 +0800
commit777ee74b76c17122608b6421b6824f31807ea52b (patch)
tree244fda492bb7172497a4002c749fc4011f87a3ad /vcx_patch.t
parenteb93a00ae4faafd7b6b3907546515d471c5cc30a (diff)
downloadcvn-777ee74b76c17122608b6421b6824f31807ea52b.tar.gz
wip: version registry, forward bin patch, CAS for objs.
Diffstat (limited to 'vcx_patch.t')
-rw-r--r--vcx_patch.t104
1 files changed, 104 insertions, 0 deletions
diff --git a/vcx_patch.t b/vcx_patch.t
new file mode 100644
index 0000000..d2b813f
--- /dev/null
+++ b/vcx_patch.t
@@ -0,0 +1,104 @@
+#!/usr/bin/perl
+use strict;
+use warnings;
+use Test::More;
+use File::Temp qw(tempfile);
+use File::Spec;
+
+# Overrides exit and silences the 'Usage' print during the 'do' call.
+BEGIN {
+ *CORE::GLOBAL::exit = sub { };
+}
+
+{
+ local @ARGV = ();
+ open my $oldout, ">&STDOUT";
+ open STDOUT, ">", File::Spec->devnull();
+ eval { do './vcx' };
+ open STDOUT, ">&", $oldout;
+}
+
+subtest 'Binary Patching: Shrink & Truncate' => sub {
+ my ($fh_old, $old_path) = tempfile();
+ my ($fh_new, $new_path) = tempfile();
+
+ # 8KB of 'A' shrinking to 6 bytes
+ print $fh_old "A" x 8192; close $fh_old;
+ print $fh_new "SHRINK"; close $fh_new;
+
+ my $patch = main::make_bin_patch($new_path, $old_path);
+ ok(defined($patch), "Patch generated for shrinking file");
+
+ main::apply_bin_patch($old_path, $patch);
+
+ is(-s $old_path, 6, "File size correctly truncated to 6 bytes");
+
+ open my $check, '<:raw', $old_path;
+ my $content = <$check>;
+ is($content, "SHRINK", "Content matches perfectly (no stale trailing data)");
+ close $check;
+};
+
+subtest 'Binary Patching: Growth & Extension' => sub {
+ my ($fh_old, $old_path) = tempfile();
+ my ($fh_new, $new_path) = tempfile();
+
+ print $fh_old "Tiny";
+ close $fh_old;
+
+ # Create a 5KB string to cross a block boundary (4096)
+ my $big_data = "EXTENDED" . ("." x 5000);
+ print $fh_new $big_data;
+ close $fh_new;
+
+ my $patch = main::make_bin_patch($new_path, $old_path);
+ main::apply_bin_patch($old_path, $patch);
+
+ is(-s $old_path, 5008, "File size correctly extended");
+
+ open my $check, '<:raw', $old_path;
+ my $result = do { local $/; <$check> };
+ is($result, $big_data, "Extended content matches perfectly");
+ close $check;
+};
+
+subtest 'Binary Patching: Sparse Block Edits' => sub {
+ my ($fh_old, $old_path) = tempfile();
+ my ($fh_new, $new_path) = tempfile();
+
+ # Create three 4KB blocks
+ my $data = ("X" x 4096) . ("Y" x 4096) . ("Z" x 4096);
+ print $fh_old $data; close $fh_old;
+
+ # Modify only the middle block ('Y' block)
+ substr($data, 5000, 10) = "MODIFIED!!";
+ print $fh_new $data; close $fh_new;
+
+ my $patch = main::make_bin_patch($new_path, $old_path);
+
+ # Header (8) + Block Header (12) + Block (4096) = ~4116 bytes
+ ok(length($patch) < 4200, "Patch is efficient (only captured the changed block)");
+
+ main::apply_bin_patch($old_path, $patch);
+
+ open my $check, '<:raw', $old_path;
+ my $final = do { local $/; <$check> };
+ is(substr($final, 5000, 10), "MODIFIED!!", "Middle block update applied");
+ is(substr($final, 0, 10), "XXXXXXXXXX", "First block preserved");
+ is(substr($final, -10), "ZZZZZZZZZZ", "Last block preserved");
+ close $check;
+};
+
+subtest 'Binary Patching: No Change Identity' => sub {
+ my ($fh_old, $old_path) = tempfile();
+ my ($fh_new, $new_path) = tempfile();
+
+ my $data = "Same data" x 50;
+ print $fh_old $data; close $fh_old;
+ print $fh_new $data; close $fh_new;
+
+ my $patch = main::make_bin_patch($new_path, $old_path);
+ is($patch, undef, "No patch generated for identical files");
+};
+
+done_testing();