Use bytestring for expected error messages
[git-rebase2.git] / itest.pl
blobe9eeabf206b2d92779235115d445e0c508ea4ed7
1 #!/usr/bin/env perl
3 use strict;
5 use Carp;
6 use Cwd;
7 use Data::Dumper;
8 use Test::More qw(no_plan);
10 # {{{ initialize
11 if (! -e "/tmp/git-rehi/test-repo") {
12 !system("mkdir -p /tmp/git-rehi/test-repo && ( cd /tmp/git-rehi/test-repo && git init && git fast-import && git config user.email test.author\@example.com && git config user.name TestAuthor ) <itest-repo.data")
13 or die("Test repo initialization failed: $? ($!)");
15 my $SOURCE_DIR = getcwd;
16 my $STACK_ROOT = `stack path --local-install-root`;
17 $STACK_ROOT =~ s/[\n\r]+$//;
18 $STACK_ROOT =~ s/\\/\//g;
19 chdir("/tmp/git-rehi/test-repo") or die("Unable to chdir to /tmp/git-rehi/test-repo: $!");
20 my $testee = $STACK_ROOT . "/bin/git-rehi";
21 # }}}
23 # {{{ test utils
24 my @Tests = ();
25 sub t(&*) { my ($block, $name) = @_;
26 push @Tests, [$name, $block];
29 my $cmd_num = 0;
30 sub cmd($;$$) { my ($cmd) = @_;
31 my $goal_status = do { if (scalar @_ > 1) { $_[1]; } else { 0; }; };
32 my $output_dest = do { if (scalar @_ > 2) { $_[2]; } else { undef; }; };
33 my $output = `$cmd 2>&1 </dev/null`;
34 my $status = $?;
36 $cmd_num++;
37 if ($goal_status eq "!= 0" && $status != 0
38 || $goal_status =~ /^-?\d+$/ && $status == $goal_status)
40 if ($output =~ /Internal error: |Unexpected happened: |IO error: /) {
41 diag("Command failed with unexpected error\nCommand: $cmd\nOutput:\n$output\n");
42 fail("cmd$cmd_num");
43 } else {
44 ok("cmd$cmd_num");
46 } else {
47 diag("Command status does not match: $status vs $goal_status\nCommand: $cmd\nOutput:\n$output\n");
48 fail("cmd$cmd_num");
50 if (defined $output_dest) {
51 ${$output_dest} = $output;
55 my $SUBTEST_COMMIT_COUNT;
57 sub reset_repo {
58 `$testee --abort 2>&1`;
59 `git checkout -f --no-track -B master origin/master1 2>&1`;
60 die("reset checkout failed") if ($? != 0);
61 `git clean -f -x -d 2>&1`;
62 die("reset clean failed") if ($? != 0);
63 $SUBTEST_COMMIT_COUNT = 0;
66 my $TEST_NAME;
67 sub tc($;$) { my ($files, $tag) = @_;
68 foreach my $filename (keys %{$files}) {
69 if (defined $files->{$filename}) {
70 write_file($filename, $files->{$filename});
71 cmd("git add -- $filename");
72 } else {
73 cmd("git rm -- $filename");
76 my $msg = "$TEST_NAME$SUBTEST_COMMIT_COUNT";
77 $SUBTEST_COMMIT_COUNT++;
78 cmd("git commit -m $msg -q");
79 if (defined $tag) {
80 cmd("git tag -f $tag");
85 package env_guard;
86 sub new ($$) { my ($class, $name, $new_value) = @_;
87 my $res = { name => $name, prev_exists => exists $ENV{$name}, prev_value => $ENV{$name} };
88 $ENV{$name} = $new_value;
89 return (bless $res, $class);
92 sub DESTROY($) { my ($instance) = @_;
93 my $name = $instance->{name};
94 if ($instance->{prev_exists}) {
95 #print "return $name <-- $instance->{prev_value}";
96 $ENV{$name} = $instance->{prev_value};
97 } else {
98 #print "remove $name";
99 delete $ENV{$name};
103 # }}}
105 # {{{ utils
106 sub write_file($$) { my ($file,$content) = @_;
107 open(my $fh, ">", $file);
108 print $fh $content;
109 close $fh;
112 sub seq($$;$) { my ($from,$to,$prefix) = @_;
113 if (!defined $prefix) { $prefix = ""; }
114 my $buf = "";
115 for (my $i = $from; $i < $to; ++$i) {
116 $buf = $buf . $prefix . $i . "\n";
118 return $buf;
120 # }}}
123 cmd("git reset --hard origin/b2");
124 cmd("$testee origin/b1");
125 cmd("git diff --quiet origin/master1");
126 } smoke;
129 my $g = env_guard->new("GIT_EDITOR", "/bin/true");
130 cmd("git reset --hard origin/b2");
131 cmd("$testee -i origin/b1");
132 cmd("git diff --quiet origin/master1");
133 } smoke_edit;
136 my $g = env_guard->new("GIT_EDITOR", "$SOURCE_DIR/itest-edit.sh");
137 my $gc = env_guard->new("GIT_SEQUENCE_EDITOR_CASE", "set-comment");
138 cmd("git reset --hard origin/b2");
139 cmd("$testee -i origin/b1");
140 is(`git log --pretty=format:%B -1`, "test-comment\n");
141 } edit_comment;
144 my $g = env_guard->new("GIT_EDITOR", "$SOURCE_DIR/itest-edit.sh");
145 my $gc = env_guard->new("GIT_SEQUENCE_EDITOR_CASE", "edit-noop");
146 cmd("git reset --hard origin/b2");
147 cmd("$testee -i origin/base");
148 cmd("$testee --continue");
149 is(`git rev-parse origin/b2`, `git rev-parse HEAD`);
150 } edit_edit_noop;
153 my $g = env_guard->new("GIT_EDITOR", "$SOURCE_DIR/itest-edit.sh");
154 my $gc = env_guard->new("GIT_SEQUENCE_EDITOR_CASE", "edit-noop");
155 cmd("git reset --hard origin/b2");
156 cmd("$testee -i origin/base");
157 is(`git symbolic-ref --quiet HEAD`, "");
158 } is_detached;
161 cmd("git reset --hard origin/b1");
162 my $g = env_guard->new("GIT_EDITOR", "$SOURCE_DIR/itest-edit.sh");
163 cmd("$testee -i HEAD");
164 cmd("git diff --quiet origin/master1");
165 } edit_b2_to_b1;
168 my $g = env_guard->new("GIT_EDITOR", "$SOURCE_DIR/itest-edit.sh");
169 my $gc = env_guard->new("GIT_SEQUENCE_EDITOR_CASE", "empty");
170 cmd("$testee -i origin/b4 ..origin/base");
171 cmd("git diff --quiet origin/master1");
172 } edit_empty;
175 my $g = env_guard->new("GIT_EDITOR", "$SOURCE_DIR/itest-edit.sh");
176 my $gc = env_guard->new("GIT_SEQUENCE_EDITOR_CASE", "empty-with-comment");
177 cmd("$testee -i origin/b4 ..origin/base");
178 cmd("git diff --quiet origin/master1");
179 } edit_empty_with_comment;
182 cmd("git reset --hard origin/b1");
183 cmd("git branch -f tmp origin/b2");
184 cmd("$testee HEAD tmp");
185 is(`git symbolic-ref HEAD`, "refs/heads/tmp\n");
186 cmd("git diff --quiet origin/master1");
187 } explicit_target;
190 cmd("git reset --hard origin/b1");
191 cmd("git branch -f tmp origin/b2");
192 my $g = env_guard->new("GIT_EDITOR", "$SOURCE_DIR/itest-edit.sh");
193 my $gc = env_guard->new("GIT_SEQUENCE_EDITOR_CASE", "fail");
194 my $old_master = `git show --quiet --pretty=format:%h master`;
195 cmd("$testee -i HEAD tmp", "!= 0");
196 cmd("$testee --abort");
197 is(`git symbolic-ref HEAD`, "refs/heads/master\n");
198 is(`git show --quiet --pretty=format:%h master`, $old_master);
199 } gh_17;
202 cmd("$testee origin/base ..origin/b1..");
203 cmd("git diff --quiet origin/master1");
204 } parsing;
207 my $g = env_guard->new("GIT_EDITOR", "$SOURCE_DIR/itest-edit.sh");
208 my $gc = env_guard->new("GIT_SEQUENCE_EDITOR_CASE", "merge-c");
209 cmd("$testee -i origin/b2");
210 cmd("git diff --quiet origin/master1");
211 is(`git log --pretty=format:%B -1`, "merge\n");
212 } edit_merge;
215 my $g = env_guard->new("GIT_EDITOR", "$SOURCE_DIR/itest-edit.sh");
216 my $gc = env_guard->new("GIT_SEQUENCE_EDITOR_CASE", "merge-no-ff");
217 cmd("$testee -i origin/b4");
218 is_deeply([split(/[ \n]/,`git show --quiet --pretty=format:%p HEAD`)],
219 [split(/\n/, `git show --quiet --pretty=format:%h origin/b4 origin/b2`)]);
220 } edit_merge_no_ff;
223 my $g = env_guard->new("GIT_EDITOR", "$SOURCE_DIR/itest-edit.sh");
224 my $gc = env_guard->new("GIT_SEQUENCE_EDITOR_CASE", "merge-no-ff-reuse");
225 cmd("git reset --hard origin/b2");
226 cmd("$testee -i origin/b4");
227 is_deeply([split(/[ \n]/,`git show --quiet --pretty=format:%p HEAD`)],
228 [split(/\n/, `git show --quiet --pretty=format:%h origin/b4 origin/b2`)]);
229 } edit_merge_no_ff_reuse;
232 my $g = env_guard->new("GIT_EDITOR", "$SOURCE_DIR/itest-edit.sh");
233 my $gc = env_guard->new("GIT_SEQUENCE_EDITOR_CASE", "merge-no-c");
234 cmd("$testee -i origin/b2");
235 cmd("git diff --quiet origin/master1");
236 } edit_merge_no_c;
239 my $g = env_guard->new("GIT_EDITOR", "$SOURCE_DIR/itest-edit.sh");
240 my $gc = env_guard->new("GIT_SEQUENCE_EDITOR_CASE", "merge-c");
241 cmd("$testee -i origin/b1", "!= 0");
242 } fastforward_merge_fails;
245 cmd("git reset --hard origin/b4");
246 cmd("git commit --allow-empty -m UPDATE");
247 cmd("$testee HEAD origin/b4..origin/b3..origin/b2b3", "!= 0"); # conflict
248 cmd("$testee --continue", "!= 0"); # no continue without resolving
249 cmd("git checkout origin/b2b3 -- file1");
250 cmd("git add file1");
251 my $ge = env_guard->new("GIT_EDITOR", "$SOURCE_DIR/itest-edit.sh");
252 my $gc = env_guard->new("GIT_SEQUENCE_EDITOR_CASE", "merge-resolved");
253 cmd("$testee --continue");
254 cmd("git diff --quiet origin/b2b3");
255 is(`git log --pretty=format:%B -1`, "Merge origin/b2 with resolving conflict (test)\n");
256 } merge_conflict;
259 cmd("$testee origin/b1");
260 like(`cat .git/rehi_todo.backup`, qr/^merge -c [0-9a-z]+ [0-9a-z]+,HEAD merge$/);
261 is(`git show --quiet --pretty=format:%h HEAD`,
262 `git show --quiet --pretty=format:%h origin/master1`);
263 } merge_second_parent;
266 cmd("git reset --hard origin/base");
267 cmd("git commit --allow-empty -m UPDATE");
268 cmd("$testee HEAD origin/base..origin/b3..origin/b2b3", "!= 0");
269 ok(-f ".git/rehi/todo.backup");
270 } todo_backup;
273 my $g = env_guard->new("GIT_EDITOR", "$SOURCE_DIR/itest-edit.sh");
274 my $gc = env_guard->new("GIT_SEQUENCE_EDITOR_CASE", "merge-inner");
275 cmd("$testee -i origin/base~1 ..origin/base");
276 cmd("git diff --quiet origin/master1");
277 is(`git branch -r --merged=HEAD origin/master1` . `git branch -r --merged=HEAD origin/b1` . `git branch -r --merged=HEAD origin/b2`,
279 "source branches are not merged");
280 } inner_merge_handmade;
283 cmd("git reset --hard origin/b4");
284 cmd("git commit --allow-empty -m 'LOCAL'");
285 cmd("$testee HEAD ..origin/master1");
286 cmd("git diff --quiet origin/master1");
287 is(`git branch -r --merged=HEAD origin/master1` . `git branch -r --merged=HEAD origin/b1` . `git branch -r --merged=HEAD origin/b2`,
289 "source branches are not merged");
290 } inner_merge_detected;
293 cmd("git reset --hard origin/b2b3");
294 cmd("$testee origin/b4");
295 is(`git show --quiet --pretty=format:%h HEAD`,
296 `git show --quiet --pretty=format:%h origin/b2b3`);
297 } fastforward_over_merges;
300 my $g = env_guard->new("GIT_EDITOR", "$SOURCE_DIR/itest-edit.sh");
301 my $gc = env_guard->new("GIT_SEQUENCE_EDITOR_CASE", "merge-inner");
302 cmd("$testee -i origin/b4");
304 my $gc2 = env_guard->new("GIT_SEQUENCE_EDITOR_CASE", "merge-inner-broken");
305 if (-f "save_todo") { cmd("rm save_todo"); }
306 cmd("$testee -i origin/b4", "!= 0"); # unknown refs - should fail
307 ok(-f "save_todo"); # make sure it reached editor
309 } marks_cleared;
312 cmd("git reset --hard origin/b_noffmerge");
313 cmd("$testee origin/b4 ..origin/base..");
314 is(`git show --quiet --pretty=format:%h HEAD`,
315 `git show --quiet --pretty=format:%h origin/b_noffmerge`);
316 } handle_noff_merges;
319 cmd("git reset --hard origin/b_diamond_after_merge");
320 cmd("$testee origin/b2");
321 } inner_merge_after_merge;
324 cmd("git reset --hard origin/b_merge_of_merges");
325 cmd("$testee origin/b5");
326 } base_after_merge;
329 cmd("git reset --hard origin/base");
330 write_file("file", seq(1, 100, "line"));
331 cmd("git reset --hard origin/base");
332 cmd("git add file");
333 cmd("git commit -m base file");
334 cmd("git tag -f base");
335 cmd("git commit -m dummy --allow-empty");
336 cmd("git tag -f rehiBase");
337 write_file("file", seq(1, 33, "line") . "line33edit\n" . seq(34,100,"line"));
338 cmd("git commit -m b2 file");
339 cmd("git tag -f b2");
340 cmd("git reset --hard base");
341 write_file("file", seq(1, 73, "line") . "line73edit\n" . seq(74,100,"line"));
342 cmd("git commit -m b1 file");
343 cmd("git merge b2");
344 cmd("git tag -f b1");
345 cmd("$testee base rehiBase..");
346 cmd("git diff --exit-code b1..HEAD");
347 } merge_into_twice;
350 my $g = env_guard->new("GIT_EDITOR", "$SOURCE_DIR/itest-edit.sh");
351 my $gc2 = env_guard->new("GIT_SEQUENCE_EDITOR_CASE", "fail");
352 cmd("$testee -i origin/base", "!= 0");
353 cmd("grep -q 'pick.*change2\$' save_todo");
354 } optimal_first_parent;
357 my $g = env_guard->new("GIT_EDITOR", "$SOURCE_DIR/itest-edit.sh");
358 my $gc2 = env_guard->new("GIT_SEQUENCE_EDITOR_CASE", "fail");
359 cmd("$testee -i origin/base origin/b1~1..", "!= 0");
360 cmd("grep -q 'pick.*change1\$' save_todo");
361 } optimal_include_start_from_sourcefrom;
364 my $g = env_guard->new("GIT_EDITOR", "$SOURCE_DIR/itest-edit.sh");
365 my $gc2 = env_guard->new("GIT_SEQUENCE_EDITOR_CASE", "fail");
366 cmd("$testee -i origin/b1~1", "!= 0");
367 cmd("grep -q 'pick.*change1\$' save_todo");
368 } optimal_include_start_from_base;
371 my $g = env_guard->new("GIT_EDITOR", "$SOURCE_DIR/itest-edit.sh");
372 my $gc2 = env_guard->new("GIT_SEQUENCE_EDITOR_CASE", "fail");
373 cmd("$testee -i HEAD", "!= 0");
374 is(`$testee --current`, "Current: exec false\n");
375 } current_exec;
378 my $g = env_guard->new("GIT_EDITOR", "$SOURCE_DIR/itest-edit.sh");
379 my $gc2 = env_guard->new("GIT_SEQUENCE_EDITOR_CASE", "fail-pick");
380 my $hash = `git rev-parse --short origin/b2`;
381 $hash =~ s/[\n\r]*$//s;
382 my $gc3 = env_guard->new("GIT_SEQUENCE_EDITOR_STEP_HASH", $hash);
383 cmd("$testee -i origin/b3 ..origin/b2b3", "!= 0");
384 is(`$testee --current`, "Current: pick $hash change2\n");
385 } current_pick;
388 cmd("$testee --current", "!= 0");
389 } current_no_rebase;
392 tc({ "f1" => "l1\nl2\nl3\n" }, "base");
393 tc({ "f1" => "l1\nl2r1\nl3\n" });
394 tc({ "f1" => "l1\nl2r2\nl3\n" }, "src_dest");
395 cmd("git reset --hard base");
396 tc({ "f1" => "l1\nl2l1\nl3\n" });
398 my $g = env_guard->new("GIT_EDITOR", "$SOURCE_DIR/itest-edit.sh");
399 my $gc = env_guard->new("GIT_SEQUENCE_EDITOR_CASE", "pass");
400 cmd("$testee HEAD base..src_dest", "!= 0");
401 like(`$testee --current`, qr/pick [0-9a-f]+ current_failed_twice1\n/);
402 write_file("f1", "l1\nl2l2\nl3\n");
403 cmd("git add f1 && git commit --no-edit");
404 cmd("$testee --continue", "!= 0");
405 like(`$testee --current`, qr/pick [0-9a-f]+ current_failed_twice2\n/);
406 } current_failed_twice;
409 tc({ "failed_pick_prints_conflicts_filename" => "l1\nl2\nl3\n",
410 "failed_pick_prints_noconflict_filename" => "l1\nl2\n" }, "base");
411 tc({ "failed_pick_prints_conflicts_filename" => "l1\nl2r\nl3\n",
412 "failed_pick_prints_noconflict_filename" => "l1\nl2r\n" }, "src_dest");
413 cmd("git reset --hard base");
414 tc({ "failed_pick_prints_conflicts_filename" => "l1\nl2l\nl3\n" });
416 my $out;
417 cmd("$testee HEAD base..src_dest", "!= 0", \$out);
418 like($out, qr/\bfailed_pick_prints_conflicts_filename\b/);
419 unlike($out, qr/\bfailed_pick_prints_noconflict_filename\b/);
420 } failed_pick_prints_conflicts;
424 my $g = env_guard->new("GIT_EDITOR", "/bin/true");
425 cmd("git reset --hard origin/b3");
426 cmd("git merge -sours -m ours_with_ref_comment origin/b2");
427 cmd("git branch -f t_b3b2_ours");
428 cmd("git reset --hard origin/b3");
429 cmd("git commit --allow-empty -m dummy");
430 cmd("$testee -i HEAD t_b3b2_ours~1..t_b3b2_ours");
431 is("ours_with_ref_comment\n", `git log --pretty=format:%B -1`);
432 } ours_with_ref;
435 cmd("$testee origin/b5 origin/b5..", "!= 0");
436 cmd("$testee origin/b2");
437 } startfailclean;
439 my %argv_idx = ();
440 if (scalar @ARGV) {
441 @argv_idx{@ARGV} = ();
443 foreach my $test (@Tests) {
444 if (!scalar %argv_idx || exists $argv_idx{$test->[0]}) {
445 reset_repo;
446 $TEST_NAME = $test->[0];
447 if($] >= "5.012000") {
448 subtest $test->[0] => $test->[1];
449 } else {
450 print "test: " . $test->[0] . "\n";
451 $test->[1]();
452 print "end: " . $test->[0] . "\n";
457 # vim: foldmethod=marker