diff options
| author | Sadeep Madurange <sadeep@asciimx.com> | 2026-04-04 11:55:08 +0800 |
|---|---|---|
| committer | Sadeep Madurange <sadeep@asciimx.com> | 2026-04-04 14:52:02 +0800 |
| commit | cf421aeff4faa15a2a80495345ae76023830ca86 (patch) | |
| tree | d417dd85f75fc4c532bb0954ed59bea87d350914 /test_vcx.t | |
| parent | 3af329abee56900dd42cb305bd7a431ab3ded90a (diff) | |
| download | cvn-cf421aeff4faa15a2a80495345ae76023830ca86.tar.gz | |
Unit tests and fix hash collisions, deletions.
Diffstat (limited to 'test_vcx.t')
| -rw-r--r-- | test_vcx.t | 167 |
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; +} + |
