summaryrefslogtreecommitdiffstats
path: root/vcx.t
diff options
context:
space:
mode:
Diffstat (limited to 'vcx.t')
-rw-r--r--vcx.t153
1 files changed, 153 insertions, 0 deletions
diff --git a/vcx.t b/vcx.t
new file mode 100644
index 0000000..bec9aa3
--- /dev/null
+++ b/vcx.t
@@ -0,0 +1,153 @@
+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';
+use constant OBJ_DIR => ROOT . '/obj';
+use constant REV_DIR => ROOT . '/rev';
+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");
+
+# Helper functions
+sub write_file {
+ my ($path, $content) = @_;
+ open my $fh, '>', $path or die $!;
+ print $fh $content;
+ close $fh;
+}
+
+sub read_file {
+ my $path = shift;
+ open my $fh, '<', $path or return "";
+ my $content = <$fh>;
+ chomp $content if $content;
+ close $fh;
+ return $content;
+}
+
+# Tests
+
+subtest 'Repository Initialization' => sub {
+ ok(system("perl $cmd init > /dev/null") == 0, "Init exit code 0");
+ 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");
+};
+
+subtest 'Adding and Committing Files' => sub {
+ write_file("test.txt", "Hello, world!");
+
+ ok(system("perl $cmd add test.txt > /dev/null") == 0, "Add file successful");
+ ok(-d TMP_TREE, "Staging tree created");
+
+ ok(system("perl $cmd commit -m 'Initial commit' > /dev/null") == 0, "Commit successful");
+
+ my $rev1 = File::Spec->catdir(REV_DIR, "0000001");
+ ok(-d $rev1, "Revision 0000001 directory exists");
+
+ my $msg_file = File::Spec->catfile($rev1, "message");
+ is(read_file($msg_file), "Initial commit", "Commit message stored correctly");
+};
+
+subtest 'Auto-staging with commit -am' => sub {
+ # Append content
+ open my $fh, '>>', "test.txt" or die $!;
+ print $fh "\nMore content";
+ close $fh;
+
+ ok(system("perl $cmd commit -am 'Second commit' > /dev/null") == 0, "Commit -am successful");
+
+ opendir(my $dh, OBJ_DIR) or die $!;
+ my @objs = grep { /[a-f0-9]{40}/ } readdir($dh);
+ closedir($dh);
+ ok(scalar @objs >= 1, "Content blobs found in object store");
+};
+
+subtest 'File Moves and Deletions (Tree Integrity)' => sub {
+ # Create nested structure
+ 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 file and delete old dir
+ make_path("dirC");
+ rename("dirA/dirB/shuffle.txt", "dirC/shuffle.txt");
+ remove_tree("dirA");
+
+ ok(system("perl $cmd commit -am 'Shuffle part 2' > /dev/null") == 0, "Commit move successful");
+
+ my $head = read_file(HEAD);
+ my ($tree_ptr) = bsd_glob(File::Spec->catfile(REV_DIR, $head, "tree-*"));
+ my $tree_hash = $tree_ptr =~ s/.*tree-//r;
+ my $actual_tree_path = File::Spec->catdir(OBJ_DIR, $tree_hash);
+
+ ok(-l File::Spec->catfile($actual_tree_path, "dirC/shuffle.txt"), "New path exists in tree");
+ ok(!-d File::Spec->catdir($actual_tree_path, "dirA"), "Deleted path removed from tree");
+
+ my $link_target = readlink(File::Spec->catfile($actual_tree_path, "dirC/shuffle.txt"));
+ like($link_target, qr/obj\/[a-f0-9]{40}/, "Tree link points to object store");
+};
+
+subtest 'Symlink Handling' => sub {
+ 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 targets preserved literally");
+};
+
+subtest 'Edge Cases: Content Reversion' => sub {
+ # Clear the staging area to ensure a "Pure" tree
+ remove_tree(TMP_DIR);
+ make_path(TMP_TREE);
+
+ make_path("revert_test");
+ write_file("revert_test/data.txt", "Version A");
+
+ # Commit State A
+ system("perl $cmd add revert_test/data.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];
+
+ # Commit State B
+ write_file("revert_test/data.txt", "Version B");
+ system("perl $cmd commit -am 'State B' > /dev/null");
+
+ # Revert to State A
+ write_file("revert_test/data.txt", "Version A");
+ system("perl $cmd commit -am 'Back to State A' > /dev/null");
+ my $tree_v3 = (bsd_glob(File::Spec->catfile(REV_DIR, read_file(HEAD), "tree-*")))[0];
+
+ # Extract hashes and compare
+ my $hash1 = $tree_v1 =~ s/.*tree-//r;
+ my $hash3 = $tree_v3 =~ s/.*tree-//r;
+
+ is($hash1, $hash3, "Tree hashes match after index reset and content restoration");
+};
+
+# Cleanup
+chdir($orig_dir);
+done_testing();