std.c: Don't use `EXIT_SUCCESS`/`EXIT_FAILURE` as function return values
[sunny256-utils.git] / tests / git-wip.t
blob8d1d20c69addaaa7d4bdad65233a29f6b2ae1375
1 #!/usr/bin/env perl
3 #=======================================================================
4 # git-wip.t
5 # File ID: 3e1ac1d2-14cc-11e5-810f-000df06acc56
7 # Test suite for git-wip(1).
9 # Character set: UTF-8
10 # ©opyleft 2015– Øyvind A. Holm <sunny@sunbase.org>
11 # License: GNU General Public License version 2 or later, see end of
12 # file for legal stuff.
13 #=======================================================================
15 use strict;
16 use warnings;
18 BEGIN {
19 use Test::More qw{no_plan};
20 # use_ok() goes here
23 use Getopt::Long;
25 local $| = 1;
27 our $CMDB = "git-wip";
28 our $CMD = "../$CMDB";
30 our %Opt = (
32 'all' => 0,
33 'git' => defined($ENV{'GITWIP_GIT'}) ? $ENV{'GITWIP_GIT'} : 'git',
34 'help' => 0,
35 'quiet' => 0,
36 'todo' => 0,
37 'verbose' => 0,
38 'version' => 0,
42 our $progname = $0;
43 $progname =~ s/^.*\/(.*?)$/$1/;
44 our $VERSION = '0.0.0';
46 my %descriptions = ();
48 Getopt::Long::Configure('bundling');
49 GetOptions(
51 'all|a' => \$Opt{'all'},
52 'git|g=s' => \$Opt{'git'},
53 'help|h' => \$Opt{'help'},
54 'quiet|q+' => \$Opt{'quiet'},
55 'todo|t' => \$Opt{'todo'},
56 'verbose|v+' => \$Opt{'verbose'},
57 'version' => \$Opt{'version'},
59 ) || die("$progname: Option error. Use -h for help.\n");
61 $Opt{'verbose'} -= $Opt{'quiet'};
62 $Opt{'help'} && usage(0);
63 if ($Opt{'version'}) {
64 print_version();
65 exit(0);
68 exit(main());
70 sub main {
71 # {{{
72 my $Retval = 0;
74 diag(sprintf('========== Executing %s v%s ==========',
75 $progname, $VERSION));
77 if ($Opt{'todo'} && !$Opt{'all'}) {
78 goto todo_section;
81 =pod
83 testcmd("$CMD command", # {{{
84 <<'END',
85 [expected stdout]
86 END
87 '',
89 'description',
92 # }}}
94 =cut
96 diag('Testing -h (--help) option...');
97 likecmd("$CMD -h", # {{{
98 '/ Show this help/i',
99 '/^$/',
101 'Option -h prints help screen',
104 # }}}
105 diag('Testing -v (--verbose) option...');
106 likecmd("$CMD -h -v", # {{{
107 '/^\n\S+ \d+\.\d+\.\d+/s',
108 '/^$/',
110 'Option -v with -h returns version number and help screen',
113 # }}}
114 diag('Testing --version option...');
115 likecmd("$CMD --version", # {{{
116 '/^\S+ \d+\.\d+\.\d+/',
117 '/^$/',
119 'Option --version returns version number',
122 # }}}
123 # git(1) refuses to commit if user.email or user.name isn't defined,
124 # so abort if that's how things are.
125 like(`git config --get user.email`,
126 qr/./,
127 'user.email is defined in Git') ||
128 BAIL_OUT('user.email is not defined in Git');
129 like(`git config --get user.name`,
130 qr/./,
131 'user.name is defined in Git') ||
132 BAIL_OUT('user.name is not defined in Git');
134 my $Tmptop = "tmp-git-wip-t-$$-" . substr(rand, 2, 8);
135 diag("Creating tempdir...");
136 ok(mkdir($Tmptop), "mkdir [Tmptop]") or
137 die("$progname: $Tmptop: Cannot create directory: $!\n");
138 ok(chdir($Tmptop), "chdir [Tmptop]") or
139 die("$progname: $Tmptop: Cannot chdir: $!\n");
141 diag("Initialise repository...");
142 ok(mkdir("repo"), "mkdir repo");
143 ok(chdir("repo"), "chdir repo") || BAIL_OUT("Cannot chdir repo");
144 likecmd("$Opt{'git'} init", # {{{
145 '/.*/',
146 '/.*/',
148 'Initialise Git repository',
151 # }}}
152 likecmd("../../$CMD", # {{{
153 '/^$/',
154 '/fatal: ambiguous argument \'HEAD\': ' .
155 'unknown revision or path not in the working tree\./s',
157 'master doesn\'t exist yet',
160 # }}}
161 create_empty_commit("Init");
162 testcmd("$Opt{'git'} branch", # {{{
163 "* master\n",
166 'master is created',
169 # }}}
170 commit_new_file("file1.txt");
171 is(commit_log(''), <<END, "Commit log with file1.txt is ok"); # {{{
172 04c774c04d6f3c4915c535077c24bc00dba82828 Add file1.txt
173 4b825dc642cb6eb9a060e54bf8d69288fbee4904 Init
176 # }}}
177 diag("Test without arguments...");
178 testcmd("../../$CMD", # {{{
180 "Switched to branch 'wip'\n",
182 "Command without arguments uses default 'wip' branch",
185 # }}}
186 testcmd("../../$CMD", # {{{
188 "Switched to branch 'wip.wip'\n",
190 "No args again, create wip.wip",
193 # }}}
194 diag("Test -D option...");
195 likecmd("../../$CMD -D", # {{{
196 '/^wip\\nAlready up[ \-]to[ \-]date.*Deleted branch wip\.wip.*$/s',
197 '/^Switched to branch \'wip\'\\n$/',
199 "Delete empty branch with -D",
202 # }}}
203 diag("Test -m option...");
204 create_and_switch_to_subbranch('add-files', 'wip.add-files');
205 is(commit_log(''), <<END, "Commit log is unchanged since file1.txt"); # {{{
206 04c774c04d6f3c4915c535077c24bc00dba82828 Add file1.txt
207 4b825dc642cb6eb9a060e54bf8d69288fbee4904 Init
210 # }}}
211 commit_new_file("file2.txt");
212 commit_new_file("file3.txt");
213 is(commit_log(''), <<END, "Commit log with file3.txt is ok"); # {{{
214 5c0f1e77ac82fe0d382b312202a467446d5948f4 Add file3.txt
215 9ddbad632f192f4edd053709b3aaedc95bd9ac0e Add file2.txt
216 04c774c04d6f3c4915c535077c24bc00dba82828 Add file1.txt
217 4b825dc642cb6eb9a060e54bf8d69288fbee4904 Init
220 # }}}
221 likecmd("../../$CMD -m", # {{{
222 '/^wip\\nMerge made by .*'
223 . ' create mode 100644 file2\.txt\\n'
224 . ' create mode 100644 file3\.txt\\n'
225 . 'Deleted branch wip\.add-files .*'
226 . '/s',
227 '/^Switched to branch \'wip\'\\n$/',
229 "Merge wip.add-files to parent (wip)",
232 # }}}
233 is(commit_log(''), <<END, "Commit log after -m is ok"); # {{{
234 5c0f1e77ac82fe0d382b312202a467446d5948f4 Merge branch 'wip.add-files' into wip
235 5c0f1e77ac82fe0d382b312202a467446d5948f4 Add file3.txt
236 9ddbad632f192f4edd053709b3aaedc95bd9ac0e Add file2.txt
237 04c774c04d6f3c4915c535077c24bc00dba82828 Add file1.txt
238 4b825dc642cb6eb9a060e54bf8d69288fbee4904 Init
241 # }}}
242 create_and_switch_to_subbranch('more-files', 'wip.more-files');
243 commit_new_file("file4.txt");
244 diag("Test -p option...");
245 likecmd("../../$CMD -p", # {{{
246 '/^wip\\n$/',
247 '/^Switched to branch \'wip\'\\n$/',
249 "-p option works",
252 # }}}
253 testcmd("$Opt{'git'} branch", # {{{
254 <<END,
255 master
256 * wip
257 wip.more-files
261 "Branches after -p looks fine",
264 # }}}
265 likecmd("echo y | ../../$CMD -p", # {{{
266 '/^master$/',
267 '/^git-wip: Type \'y\' \+ Enter to set active branch ' .
268 'to \'master\' \(git checkout\)\.\.\.' .
269 'Switched to branch \'master\'$/',
271 'If -p option and no parent, checkout master',
274 # }}}
275 testcmd("$Opt{'git'} branch", # {{{
276 <<END,
277 * master
279 wip.more-files
283 "Check current branch status after -p to master",
286 # }}}
287 likecmd("../../$CMD", # {{{
288 '/^$/',
289 '/^fatal: [Aa] branch named \'wip\' already exists\.?\n$/',
291 "wip branch already exists",
294 # }}}
295 testcmd("$Opt{'git'} branch", # {{{
296 <<END,
297 * master
299 wip.more-files
303 "We're still on master because \"wip\" already exists",
306 # }}}
307 likecmd("$Opt{'git'} checkout wip.more-files", # {{{
308 '/^$/',
309 '/^Switched to branch \'wip\.more-files\'\\n$/',
311 "Go back to wip.more-files",
314 # }}}
315 commit_new_file("file5.txt");
316 is(commit_log(''), <<END, "Commit log with file5.txt is ok"); # {{{
317 375860ebe00ccc64321c2ade0c1525e7428458fa Add file5.txt
318 6c4c1a3c2c479e74e02394040d6da63046c1458c Add file4.txt
319 5c0f1e77ac82fe0d382b312202a467446d5948f4 Merge branch 'wip.add-files' into wip
320 5c0f1e77ac82fe0d382b312202a467446d5948f4 Add file3.txt
321 9ddbad632f192f4edd053709b3aaedc95bd9ac0e Add file2.txt
322 04c774c04d6f3c4915c535077c24bc00dba82828 Add file1.txt
323 4b825dc642cb6eb9a060e54bf8d69288fbee4904 Init
326 # }}}
327 diag("Testing -s option...");
328 likecmd("../../$CMD -s", # {{{
329 '/^wip\\nUpdating [0-9a-f]+\.\.[0-9a-f]+\\n' .
330 'Fast(-| )forward\\n' .
331 'Squash commit -- not updating HEAD\\n' .
332 '.*' .
333 ' create mode 100644 file4\.txt\\n' .
334 ' create mode 100644 file5\.txt\\n' .
335 '/s',
336 '/^Switched to branch \'wip\'\\n$/',
338 "Squash wip.more-files to parent with -s",
341 # }}}
342 is(commit_log(''), <<END, "Commit log after squash (-s) is ok"); # {{{
343 5c0f1e77ac82fe0d382b312202a467446d5948f4 Merge branch 'wip.add-files' into wip
344 5c0f1e77ac82fe0d382b312202a467446d5948f4 Add file3.txt
345 9ddbad632f192f4edd053709b3aaedc95bd9ac0e Add file2.txt
346 04c774c04d6f3c4915c535077c24bc00dba82828 Add file1.txt
347 4b825dc642cb6eb9a060e54bf8d69288fbee4904 Init
350 # }}}
351 likecmd("$Opt{'git'} commit -m 'Squash wip.more-files into wip'", # {{{
352 '/^\[wip [0-9a-f]+\] Squash wip\.more-files into wip\\n' .
353 ' 2 files changed, 2 insertions\(\+\)(, 0 deletions\(-\))?\\n' .
354 ' create mode 100644 file4\.txt\\n' .
355 ' create mode 100644 file5\.txt\\n$' .
356 '/s',
357 '/^$/',
359 "Commit squashed changes",
362 # }}}
363 is(commit_log(''), <<END, "Commit log after squash commit is ok"); # {{{
364 375860ebe00ccc64321c2ade0c1525e7428458fa Squash wip.more-files into wip
365 5c0f1e77ac82fe0d382b312202a467446d5948f4 Merge branch 'wip.add-files' into wip
366 5c0f1e77ac82fe0d382b312202a467446d5948f4 Add file3.txt
367 9ddbad632f192f4edd053709b3aaedc95bd9ac0e Add file2.txt
368 04c774c04d6f3c4915c535077c24bc00dba82828 Add file1.txt
369 4b825dc642cb6eb9a060e54bf8d69288fbee4904 Init
372 # }}}
373 likecmd("echo y | ../../$CMD -m", # {{{
374 '/^master\\n'
375 . 'Merge made by .*'
376 . ' create mode 100644 file2\.txt\\n'
377 . ' create mode 100644 file3\.txt\\n'
378 . ' create mode 100644 file4\.txt\\n'
379 . ' create mode 100644 file5\.txt\\n'
380 . 'Deleted branch wip \(was [0-9a-f]+\)\.\\n$'
381 . '/s',
382 '/^git-wip: Type \'y\' \+ Enter to merge wip to master\.\.\.' .
383 'Switched to branch \'master\'\\n$/',
385 "Merge wip to master with -m",
388 # }}}
389 is(commit_log(''), <<END, "Commit log after -m"); # {{{
390 375860ebe00ccc64321c2ade0c1525e7428458fa Merge branch 'wip'
391 375860ebe00ccc64321c2ade0c1525e7428458fa Squash wip.more-files into wip
392 5c0f1e77ac82fe0d382b312202a467446d5948f4 Merge branch 'wip.add-files' into wip
393 5c0f1e77ac82fe0d382b312202a467446d5948f4 Add file3.txt
394 9ddbad632f192f4edd053709b3aaedc95bd9ac0e Add file2.txt
395 04c774c04d6f3c4915c535077c24bc00dba82828 Add file1.txt
396 4b825dc642cb6eb9a060e54bf8d69288fbee4904 Init
399 # }}}
400 likecmd("echo y | ../../$CMD -m", # {{{
401 '/^$/',
402 '/^Is already on master, nowhere to merge branch\\n$/',
404 "Option -m on master doesn't work",
407 # }}}
408 likecmd("echo y | ../../$CMD -s", # {{{
409 '/^$/',
410 '/^Is already on master, nowhere to squash branch\\n$/',
412 "Neither does -s",
415 # }}}
416 diag("Test for unknown options...");
417 likecmd("../../$CMD -W", # {{{
418 '/^$/',
419 '/^git-wip: -W: Unknown option\\n$/',
421 "It doesn't recognise -W",
424 # }}}
425 likecmd("../../$CMD -e", # {{{
426 '/^$/',
427 '/^git-wip: -e: Unknown option\\n$/',
429 "It doesn't recognise -e (used by echo)",
432 # }}}
433 diag("Cleaning up temp files...");
434 ok(chdir(".."), "chdir .."); # From $Tmptop/repo/
435 likecmd("rm -rf repo", # {{{
436 '/^$/',
437 '/^$/',
439 'Delete repo/',
442 # }}}
443 ok(chdir(".."), "chdir .."); # From $Tmptop/
445 ok(-d $Tmptop, "[Tmptop] exists");
446 ok(rmdir($Tmptop), "rmdir([Tmptop])");
447 ok(!-e $Tmptop, "Tempdir is gone");
449 todo_section:
452 if ($Opt{'all'} || $Opt{'todo'}) {
453 diag('Running TODO tests...'); # {{{
455 TODO: {
457 local $TODO = '';
458 # Insert TODO tests here.
461 # TODO tests }}}
464 diag('Testing finished.');
465 return $Retval;
466 # }}}
467 } # main()
469 sub create_and_switch_to_subbranch {
470 # {{{
471 my ($branch, $exp_branch) = @_;
472 testcmd("../../$CMD $branch",
474 "Switched to branch '$exp_branch'\n",
476 "Create subbranch '$branch' and checkout '$exp_branch'",
478 return;
479 # }}}
480 } # create_and_switch_to_subbranch()
482 sub commit_log {
483 # {{{
484 my $ref = shift;
485 my $retval = '';
486 open(my $pipefp, "$Opt{'git'} log --format='%T %s' --topo-order $ref |") or
487 return("'$Opt{'git'} log' pipe error: $!\n");
488 while (<$pipefp>) {
489 $retval .= $_;
491 close($pipefp);
492 return($retval);
493 # }}}
494 } # commit_log()
496 sub commit_new_file {
497 # {{{
498 my $file = shift;
499 ok(!-e $file, "$file doesn't exist");
500 ok(open(my $outfp, ">$file"), "Create file '$file'");
501 ok(print($outfp "This is $file\n"), "Add content to $file");
502 ok(close($outfp), "Close $file");
503 ok(-f $file, "$file exists and is a regular file");
504 is(file_data($file), "This is $file\n", "Contents of $file is ok");
505 testcmd("$Opt{'git'} add \"$file\"",
509 "$Opt{'git'} add $file",
511 likecmd("$Opt{'git'} commit -m \"Add $file\"",
512 "/.* Add $file.*/s",
513 '/^$/',
515 "$Opt{'git'} commit (add $file)",
517 # }}}
518 } # commit_new_file()
520 sub create_empty_commit {
521 # {{{
522 my $msg = shift;
523 likecmd("$Opt{'git'} commit --allow-empty -m \"$msg\"",
524 '/.*/', '/.*/', 0, "Create empty commit");
525 return;
526 # }}}
527 } # create_empty_commit()
529 sub testcmd {
530 # {{{
531 my ($Cmd, $Exp_stdout, $Exp_stderr, $Exp_retval, $Desc) = @_;
532 defined($descriptions{$Desc}) &&
533 BAIL_OUT("testcmd(): '$Desc' description is used twice");
534 $descriptions{$Desc} = 1;
535 my $stderr_cmd = '';
536 my $cmd_outp_str = $Opt{'verbose'} >= 1 ? "\"$Cmd\" - " : '';
537 my $Txt = join('', $cmd_outp_str, defined($Desc) ? $Desc : '');
538 my $TMP_STDERR = "$CMDB-stderr.tmp";
539 my $retval = 1;
541 if (defined($Exp_stderr)) {
542 $stderr_cmd = " 2>$TMP_STDERR";
544 $retval &= is(`$Cmd$stderr_cmd`, $Exp_stdout, "$Txt (stdout)");
545 my $ret_val = $?;
546 if (defined($Exp_stderr)) {
547 $retval &= is(file_data($TMP_STDERR), $Exp_stderr, "$Txt (stderr)");
548 unlink($TMP_STDERR);
549 } else {
550 diag("Warning: stderr not defined for '$Txt'");
552 $retval &= is($ret_val >> 8, $Exp_retval, "$Txt (retval)");
554 return $retval;
555 # }}}
556 } # testcmd()
558 sub likecmd {
559 # {{{
560 my ($Cmd, $Exp_stdout, $Exp_stderr, $Exp_retval, $Desc) = @_;
561 defined($descriptions{$Desc}) &&
562 BAIL_OUT("likecmd(): '$Desc' description is used twice");
563 $descriptions{$Desc} = 1;
564 my $stderr_cmd = '';
565 my $cmd_outp_str = $Opt{'verbose'} >= 1 ? "\"$Cmd\" - " : '';
566 my $Txt = join('', $cmd_outp_str, defined($Desc) ? $Desc : '');
567 my $TMP_STDERR = "$CMDB-stderr.tmp";
568 my $retval = 1;
570 if (defined($Exp_stderr)) {
571 $stderr_cmd = " 2>$TMP_STDERR";
573 $retval &= like(`$Cmd$stderr_cmd`, $Exp_stdout, "$Txt (stdout)");
574 my $ret_val = $?;
575 if (defined($Exp_stderr)) {
576 $retval &= like(file_data($TMP_STDERR), $Exp_stderr, "$Txt (stderr)");
577 unlink($TMP_STDERR);
578 } else {
579 diag("Warning: stderr not defined for '$Txt'");
581 $retval &= is($ret_val >> 8, $Exp_retval, "$Txt (retval)");
583 return $retval;
584 # }}}
585 } # likecmd()
587 sub file_data {
588 # Return file content as a string {{{
589 my $File = shift;
590 my $Txt;
592 open(my $fp, '<', $File) or return undef;
593 local $/ = undef;
594 $Txt = <$fp>;
595 close($fp);
596 return $Txt;
597 # }}}
598 } # file_data()
600 sub print_version {
601 # Print program version {{{
602 print("$progname $VERSION\n");
603 return;
604 # }}}
605 } # print_version()
607 sub usage {
608 # Send the help message to stdout {{{
609 my $Retval = shift;
611 if ($Opt{'verbose'}) {
612 print("\n");
613 print_version();
615 print(<<"END");
617 Usage: $progname [options]
619 Contains tests for the $CMDB(1) program.
621 Options:
623 -a, --all
624 Run all tests, also TODOs.
625 -g X, --git X
626 Specify alternative git executable to use. Used to execute the tests
627 with different git versions. This can also be set with the GITWIP_GIT
628 environment variable.
629 -h, --help
630 Show this help.
631 -q, --quiet
632 Be more quiet. Can be repeated to increase silence.
633 -t, --todo
634 Run only the TODO tests.
635 -v, --verbose
636 Increase level of verbosity. Can be repeated.
637 --version
638 Print version information.
641 exit($Retval);
642 # }}}
643 } # usage()
645 sub msg {
646 # Print a status message to stderr based on verbosity level {{{
647 my ($verbose_level, $Txt) = @_;
649 $verbose_level > $Opt{'verbose'} && return;
650 print(STDERR "$progname: $Txt\n");
651 return;
652 # }}}
653 } # msg()
655 __END__
657 # This program is free software; you can redistribute it and/or modify
658 # it under the terms of the GNU General Public License as published by
659 # the Free Software Foundation; either version 2 of the License, or (at
660 # your option) any later version.
662 # This program is distributed in the hope that it will be useful, but
663 # WITHOUT ANY WARRANTY; without even the implied warranty of
664 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
665 # See the GNU General Public License for more details.
667 # You should have received a copy of the GNU General Public License
668 # along with this program.
669 # If not, see L<http://www.gnu.org/licenses/>.
671 # vim: set fenc=UTF-8 ft=perl fdm=marker ts=4 sw=4 sts=4 et fo+=w :