summaryrefslogtreecommitdiffstats
path: root/test_vcx.t
diff options
context:
space:
mode:
authorSadeep Madurange <sadeep@asciimx.com>2026-04-04 11:55:08 +0800
committerSadeep Madurange <sadeep@asciimx.com>2026-04-04 14:52:02 +0800
commitcf421aeff4faa15a2a80495345ae76023830ca86 (patch)
treed417dd85f75fc4c532bb0954ed59bea87d350914 /test_vcx.t
parent3af329abee56900dd42cb305bd7a431ab3ded90a (diff)
downloadcvn-cf421aeff4faa15a2a80495345ae76023830ca86.tar.gz
Unit tests and fix hash collisions, deletions.
Diffstat (limited to 'test_vcx.t')
-rw-r--r--test_vcx.t167
1 files changed, 167 insertions, 0 deletions
diff --git a/test_vcx.t b/test_vcx.t
new file mode 100644
index 0000000..e619824
--- /dev/null
+++ b/test_vcx.t
@@ -0,0 +1,167 @@
+use strict;
+use warnings;
+use Test::More;
+use File::Path qw(remove_tree make_path);
+use File::Spec;
+use Cwd;
+use File::Glob qw(:bsd_glob);
+
+use constant ROOT => '.vcx';
+use constant HEAD => ROOT . '/head'; # Current commit ID
+use constant OBJ_DIR => ROOT . '/obj'; # Latest version of a file
+use constant REV_DIR => ROOT . '/rev'; # Commits
+
+# Staging area
+use constant TMP_DIR => ROOT . '/index';
+use constant TMP_TREE => TMP_DIR . '/tree';
+
+# Setup Sandbox
+my $sandbox = "sandbox";
+remove_tree($sandbox) if -d $sandbox;
+make_path($sandbox);
+
+my $orig_dir = getcwd();
+chdir($sandbox) or die "Cant enter sandbox: $!";
+
+my $cmd = File::Spec->catfile($orig_dir, "vcx");
+
+# Test 'init'
+ok(system("perl $cmd init") == 0, "Init command ran successfully");
+ok(-d ROOT, "ROOT directory created");
+ok(-e HEAD, "Head file created");
+ok(-d OBJ_DIR, "OBJ_DIR directory created");
+ok(-d REV_DIR, "REV_DIR directory created");
+
+# Test 'add'
+open my $fh, '>', "test.txt" or die $!;
+print $fh "Hello, world!";
+close $fh;
+
+ok(system("perl $cmd add test.txt > /dev/null") == 0,
+ "Add command ran successfully");
+ok(-d TMP_TREE, "Staging tree created");
+
+# Test 'commit -m'
+ok(system("perl $cmd commit -m 'Initial commit' > /dev/null") == 0,
+ "Commit with -m ran successfully");
+my $rev1 = File::Spec->catdir(REV_DIR, "0000001");
+ok(-d $rev1, "Revision 0000001 created");
+
+# Check the message content
+my $msg_file = File::Spec->catfile($rev1, "message");
+ok(-f $msg_file, "Message file exists in revision 1");
+
+open my $msg_fh, '<', $msg_file or die $!;
+my $saved_msg = <$msg_fh>; chomp $saved_msg; close $msg_fh;
+is($saved_msg, "Initial commit", "Commit message matches the input");
+
+# Test 'commit -am' (Auto-staging)
+open my $fh2, '>>', "test.txt" or die $!;
+print $fh2 "\nMore content";
+close $fh2;
+
+ok(system("perl $cmd commit -am 'Second commit' > /dev/null") == 0,
+ "Commit -am (auto-staging) ran successfully");
+
+# Check if the object store updated
+opendir(my $dh, OBJ_DIR) or die $!;
+my @objs = grep { /[a-f0-9]{40}/ } readdir($dh);
+closedir($dh);
+
+ok(scalar @objs >= 1, "Objects exist in store");
+
+# Shuffle subdirectories
+make_path("dirA/dirB");
+write_file("dirA/dirB/shuffle.txt", "Same Content");
+system("perl $cmd add dirA/dirB/shuffle.txt > /dev/null");
+system("perl $cmd commit -m 'Shuffle part 1' > /dev/null");
+
+# Move and delete
+make_path("dirC");
+rename("dirA/dirB/shuffle.txt", "dirC/shuffle.txt");
+remove_tree("dirA");
+
+# Commit the move
+system("perl $cmd commit -am 'Shuffle part 2' > /dev/null");
+
+# Verify Revision 4's tree structure
+my $head = read_file(HEAD);
+my ($tree_ptr) = bsd_glob(File::Spec->catfile(REV_DIR, $head, "tree-*"));
+my ($tree_hash) = $tree_ptr =~ /tree-([a-f0-9]{40})$/;
+my $actual_tree_path = File::Spec->catdir(OBJ_DIR, $tree_hash);
+
+# Check for new path
+ok(-l File::Spec->catfile($actual_tree_path, "dirC/shuffle.txt"),
+ "New path dirC/shuffle.txt exists in object store");
+
+# Check for deleted path (Should NOT exist)
+ok(!-d File::Spec->catdir($actual_tree_path, "dirA"),
+ "Deleted directory removed from tree snapshot");
+
+# Verify the symlink still points to the correct content blob
+my $link_target = readlink(File::Spec->catfile($actual_tree_path, "dirC/shuffle.txt"));
+like($link_target, qr/obj\/[a-f0-9]{40}/, "Symlink in tree points to a valid object blob");
+
+## Symlink-to-symlink
+write_file("target.txt", "Final Destination");
+symlink("target.txt", "link_a");
+symlink("link_a", "link_b");
+
+system("perl $cmd add link_b > /dev/null");
+system("perl $cmd commit -m 'Double link' > /dev/null");
+
+my $head = read_file(HEAD);
+my ($tree_ptr) = bsd_glob(File::Spec->catfile(REV_DIR, $head, "tree-*"));
+my $staged_link = File::Spec->catfile(OBJ_DIR, $tree_ptr =~ s/.*tree-//r, "link_b");
+
+is(readlink($staged_link), "link_a", "Symlink-to-symlink preserved literal target");
+
+## Empty file
+write_file("empty.txt", "");
+system("perl $cmd add empty.txt > /dev/null");
+system("perl $cmd commit -m 'Empty file' > /dev/null");
+
+ok(-e File::Spec->catfile(OBJ_DIR, "da39a3ee5e6b4b0d3255bfef95601890afd80709"),
+ "Empty file object created (da39a3...)");
+
+# The overwrite
+write_file("repeat.txt", "Version A");
+system("perl $cmd add repeat.txt > /dev/null");
+system("perl $cmd commit -m 'State A' > /dev/null");
+my $tree_v1 = (bsd_glob(File::Spec->catfile(REV_DIR, read_file(HEAD), "tree-*")))[0];
+
+write_file("repeat.txt", "Version B");
+system("perl $cmd add repeat.txt > /dev/null");
+system("perl $cmd commit -m 'State B' > /dev/null");
+
+## Revert content
+write_file("repeat.txt", "Version A");
+system("perl $cmd add repeat.txt > /dev/null");
+system("perl $cmd commit -m 'Back to State A' > /dev/null");
+my $tree_v3 = (bsd_glob(File::Spec->catfile(REV_DIR, read_file(HEAD), "tree-*")))[0];
+
+is($tree_v1 =~ s/.*tree-//r, $tree_v3 =~ s/.*tree-//r,
+ "Tree hash reverted perfectly after content restoration");
+
+# Cleanup
+chdir($orig_dir);
+# remove_tree($sandbox); # Uncomment this once you're sure it works
+
+done_testing();
+
+sub write_file {
+ my ($path, $content) = @_;
+ open my $fh, '>', $path or die "Could not open '$path' for writing: $!";
+ print $fh $content;
+ close $fh or die "Could not close '$path' after writing: $!";
+}
+
+sub read_file {
+ my $path = shift;
+ open my $fh, '<', $path or return "";
+ my $content = <$fh>;
+ chomp $content if $content;
+ close $fh;
+ return $content;
+}
+