std.t: create_file(): Use `>` as separate parameter in open()
[sunny256-utils.git] / tests / git-dbr.t
blob24da6ef27d9a9ac68bcaaa007d193d3540228f15
1 #!/usr/bin/env perl
3 #=======================================================================
4 # git-dbr.t
5 # File ID: f7855efa-7a4f-11e5-968c-02010e0a6634
7 # Test suite for git-dbr(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 $CMD_BASENAME = "git-dbr";
28 our $CMD = "../$CMD_BASENAME";
30 our %Opt = (
32 'all' => 0,
33 'git' => defined($ENV{'GIT_DBR_GIT'}) ? $ENV{'GIT_DBR_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 my $GIT = $Opt{'git'};
70 exit(main());
72 sub main {
73 # {{{
74 my $Retval = 0;
76 diag(sprintf('========== Executing %s v%s ==========',
77 $progname, $VERSION));
79 if ($Opt{'todo'} && !$Opt{'all'}) {
80 goto todo_section;
83 =pod
85 testcmd("$CMD command", # {{{
86 <<'END',
87 [expected stdout]
88 END
89 '',
91 'description',
94 # }}}
96 =cut
98 diag('Testing -h (--help) option...');
99 likecmd("$CMD -h", # {{{
100 '/ Show this help/i',
101 '/^$/',
103 'Option -h prints help screen',
106 # }}}
107 diag('Testing -v (--verbose) option...');
108 likecmd("$CMD -hv", # {{{
109 '/^\n\S+ \d+\.\d+\.\d+/s',
110 '/^$/',
112 'Option -v with -h returns version number and help screen',
115 # }}}
116 diag('Testing --version option...');
117 likecmd("$CMD --version", # {{{
118 '/^\S+ \d+\.\d+\.\d+/',
119 '/^$/',
121 'Option --version returns version number',
124 # }}}
125 my $Tmptop = "tmp-git-dangling-t-$$-" . substr(rand, 2, 8);
126 ok(mkdir($Tmptop), "mkdir [Tmptop]");
127 ok(chdir($Tmptop), "chdir [Tmptop]");
128 diag("Initialise test repos");
129 likecmd("$GIT init repo_a", # {{{
130 '/.*/',
131 '/.*/',
133 "git init repo_a",
136 # }}}
137 if (!ok(-d "repo_a/.git", "repo_a/.git exists")) { # {{{
138 ok(chdir(".."), "chdir .. before bailout");
139 ok(rmdir($Tmptop), "rmdir [Tmptop] before bailout");
140 BAIL_OUT("repo_a wasn't created properly, aborting");
143 # }}}
144 likecmd("$GIT init repo_b", # {{{
145 '/.*/',
146 '/.*/',
148 "git init repo_b",
151 # }}}
152 ok(-d "repo_b/.git", "repo_b/.git exists");
153 ok(chdir("repo_a"), "chdir repo_a");
154 create_remote("repo_b", "../repo_b");
155 likecmd("$GIT commit --allow-empty -m 'Initial empty commit'", # {{{
156 '/.*/',
157 '/.*/',
159 "Create initial commit in repo_a",
162 # }}}
163 is(`$GIT log --format="%T %s"`, # {{{
164 "4b825dc642cb6eb9a060e54bf8d69288fbee4904 Initial empty commit\n",
165 "Initial commit in repo_a was created",
168 # }}}
169 create_branch("branch_a");
170 create_branch("branch_b");
171 create_branch("oldbranch");
172 create_branch("with/slash");
173 ok(chdir("../repo_b"), "chdir ../repo_b");
174 create_remote("repo_a", "../repo_a");
175 likecmd("$GIT fetch repo_a", # {{{
176 '/^$/',
177 '/^' .
178 'From \.\.\/repo_a\n' .
179 '.+' .
180 '\[new branch\].+repo_a\/branch_a\n' .
181 '.+' .
182 '\[new branch\].+repo_a\/branch_b\n' .
183 '.+' .
184 '\[new branch\].+repo_a\/master\n' .
185 '.+' .
186 '\[new branch\].+repo_a\/oldbranch\n' .
187 '.+' .
188 '\[new branch\].+repo_a\/with\/slash\n' .
189 '$/s',
191 "Fetch from repo_a",
194 # }}}
195 check_branches(<<END, "after initial fetch"); # {{{
196 remotes/repo_a/branch_a
197 remotes/repo_a/branch_b
198 remotes/repo_a/master
199 remotes/repo_a/oldbranch
200 remotes/repo_a/with/slash
203 # }}}
204 $CMD = "../../$CMD";
205 diag("Start of git-dbr tests");
206 testcmd("$CMD", # {{{
210 "No arguments",
213 # }}}
214 create_branch("oldbranch", "repo_a/oldbranch");
215 check_branches(<<END, "after oldbranch was created"); # {{{
216 oldbranch
217 remotes/repo_a/branch_a
218 remotes/repo_a/branch_b
219 remotes/repo_a/master
220 remotes/repo_a/oldbranch
221 remotes/repo_a/with/slash
224 # }}}
225 likecmd("$CMD oldbranch", # {{{
226 '/^Deleted branch oldbranch \(was [0-9a-f]+\)\.\n$/s',
227 '/^git-dbr: Executing \'git branch -D oldbranch\'\.\.\.\n$/s',
229 "Delete local branch 'oldbranch'",
232 # }}}
233 create_branch("oldbranch2", "repo_a/oldbranch");
234 likecmd("$CMD oldbranch2 repo_a/oldbranch", # {{{
235 '/^Deleted branch oldbranch2 \(was [0-9a-f]+\)\.\n$/s',
236 '/^' .
237 'git-dbr: Executing \'git branch -D oldbranch2\'\.\.\.\n' .
238 'git-dbr: Executing \'git push repo_a :oldbranch\'\.\.\.\n' .
239 'To \.\.\/repo_a\n' .
240 '.+' .
241 '\[deleted\]\s+oldbranch\n' .
242 '$/s',
244 "Delete local branch 'oldbranch2' " .
245 "and remote branch 'repo_a/oldbranch'",
248 # }}}
249 fetch("repo_a", "after deletion of oldbranch2 and repo_a/oldbranch");
250 check_branches(<<END, "after oldbranch2 and repo_a/oldbranch were deleted"); # {{{
251 remotes/repo_a/branch_a
252 remotes/repo_a/branch_b
253 remotes/repo_a/master
254 remotes/repo_a/with/slash
257 # }}}
258 test_branch("slash/branch_a");
259 test_branch("ab/cd/ef");
260 test_branch("remotes/abc");
261 test_branch("remotes/repo_a/abc");
262 test_branch("repo_a/abc");
263 test_branch("abc,");
264 test_branch("abc,,,,,");
265 test_branch("has++plus+signs");
266 test_branch("repo_a/abc,/,/,,,");
267 test_branch("slash/branch_a,");
268 test_branch("ab/cd/ef,");
269 test_branch("remotes/abc,");
270 test_branch("remotes/repo_a/abc,");
271 test_branch("repo_a/abc,");
272 test_branch("heads/master");
273 test_branch("refs/heads/master");
274 diag("branch name is identical to remote name");
275 create_branch("remotes/repo_a/master", "repo_a/master");
276 check_branches(<<END, "even with same name as remote");
277 remotes/repo_a/master
278 remotes/repo_a/branch_a
279 remotes/repo_a/branch_b
280 remotes/repo_a/master
281 remotes/repo_a/with/slash
283 likecmd("$CMD remotes/repo_a/master", # {{{
284 '/^Deleted branch remotes/repo_a/master \(was [0-9a-f]+\)\.\n$/s',
285 '/^git-dbr: Executing \'git branch -D ' .
286 'remotes/repo_a/master\'\.\.\.\n$/s',
288 "Delete local branch 'remotes/repo_a/master'",
291 # }}}
292 check_branches(<<END, "after 'remotes/repo_a/master' was deleted"); # {{{
293 remotes/repo_a/branch_a
294 remotes/repo_a/branch_b
295 remotes/repo_a/master
296 remotes/repo_a/with/slash
299 # }}}
300 likecmd("$CMD remotes/repo_a/with/slash", # {{{
301 '/^$/s',
302 '/^' .
303 'git-dbr: Executing \'git push repo_a :with\/slash\'\.\.\.\n' .
304 'To \.\.\/repo_a\n' .
305 '.+' .
306 '\[deleted\]\s+with\/slash\n' .
307 '$/s',
309 "Delete remote branch 'repo_a/with/slash'",
311 check_branches(<<END, "after local 'remotes/repo_a/master' was deleted");
312 remotes/repo_a/branch_a
313 remotes/repo_a/branch_b
314 remotes/repo_a/master
317 # }}}
318 create_branch("branch_a", "repo_a/branch_a");
319 likecmd("$GIT branch -a | grep branch_a | xargs $CMD", # {{{
320 '/^Deleted branch branch_a \(was [0-9a-f]+\)\.\n$/s',
321 '/^' .
322 'git-dbr: Executing \'git branch -D branch_a\'\.\.\.\n' .
323 'git-dbr: Executing \'git push repo_a :branch_a\'\.\.\.\n' .
324 'To \.\.\/repo_a\n' .
325 '.+' .
326 '\[deleted\]\s+branch_a\n' .
327 '$/s',
329 "Delete all 'branch_a' branches with xargs",
332 # }}}
333 fetch("repo_a", "after all 'branch_a' branches were deleted");
334 check_branches(<<END, "after all 'branch_a' branches were deleted"); # {{{
335 remotes/repo_a/branch_b
336 remotes/repo_a/master
339 # }}}
340 likecmd("$CMD repo_a/branch_b,", # {{{
341 '/^$/s',
342 '/^' .
343 'git-dbr: Executing \'git push repo_a :branch_b\'\.\.\.\n' .
344 'To \.\.\/repo_a\n' .
345 '.+' .
346 '\[deleted\]\s+branch_b\n' .
347 '$/s',
349 "Delete local branch 'repo_a/branch_b' and strip trailing comma",
352 # }}}
353 ok(chdir(".."), "chdir ..");
354 diag("Delete temporary test directories");
355 ok(-d "repo_a", "repo_a exists");
356 testcmd("rm -rf repo_a", # {{{
360 "Delete repo_a",
363 # }}}
364 ok(!-e "repo_a", "repo_a is gone");
365 ok(-d "repo_b", "repo_b exists");
366 testcmd("rm -rf repo_b", # {{{
370 "Delete repo_b",
373 # }}}
374 ok(!-e "repo_b", "repo_b is gone");
375 ok(chdir(".."), "chdir ..");
376 ok(rmdir($Tmptop), "rmdir [Tmptop]");
378 todo_section:
381 if ($Opt{'all'} || $Opt{'todo'}) {
382 diag('Running TODO tests...'); # {{{
384 TODO: {
386 local $TODO = '';
387 # Insert TODO tests here.
390 # TODO tests }}}
393 diag('Testing finished.');
394 return $Retval;
395 # }}}
396 } # main()
398 sub check_branches {
399 # {{{
400 my ($should_be, $desc) = @_;
401 is(`$GIT branch -a`, $should_be, "Branches are ok $desc");
402 return;
403 # }}}
404 } # check_branches()
406 sub create_branch {
407 # {{{
408 my ($branch, $remote_branch) = @_;
409 defined($remote_branch) || ($remote_branch = '');
410 likecmd("$GIT branch $branch $remote_branch",
411 '/.*/s',
412 '/^$/s',
414 "Create '$branch' branch" .
415 (length($remote_branch) ? " from $remote_branch" : ""),
417 return;
418 # }}}
419 } # create_branch()
421 sub create_remote {
422 # {{{
423 my ($name, $url) = @_;
424 testcmd("$GIT remote add $name $url",
428 "Create remote '$name'",
430 return;
431 # }}}
432 } # create_remote()
434 sub test_branch {
435 # Create and delete local branch. Check retval, but ignore output. {{{
436 my $branch = shift;
437 system("$GIT branch \"$branch\" repo_a/branch_a >/dev/null 2>/dev/null");
438 like(`$GIT branch -a`,
439 '/.*' .
440 ' ' . quotemeta($branch) . '\n' .
441 '.*$/s',
442 "Branch '$branch' exists",
444 my $exit_val = system("$CMD \"$branch\" >/dev/null 2>/dev/null");
445 $exit_val = $exit_val >> 8;
446 is($exit_val, 0, "Delete '$branch', retval ok");
447 unlike(`$GIT branch -a`,
448 '/.*' .
449 ' ' . $branch . '\n' .
450 '.*$/s',
451 "Branch '$branch' is gone",
453 return;
454 # }}}
455 } # test_branch()
457 sub fetch {
458 # {{{
459 my ($remote, $desc) = @_;
460 testcmd("$GIT fetch --prune $remote",
464 "Fetch from $remote ($desc)",
466 return;
467 # }}}
468 } # fetch()
470 sub testcmd {
471 # {{{
472 my ($Cmd, $Exp_stdout, $Exp_stderr, $Exp_retval, $Desc) = @_;
473 defined($descriptions{$Desc}) &&
474 BAIL_OUT("testcmd(): '$Desc' description is used twice");
475 $descriptions{$Desc} = 1;
476 my $stderr_cmd = '';
477 my $cmd_outp_str = $Opt{'verbose'} >= 1 ? "\"$Cmd\" - " : '';
478 my $Txt = join('', $cmd_outp_str, defined($Desc) ? $Desc : '');
479 my $TMP_STDERR = "$CMD_BASENAME-stderr.tmp";
480 my $retval = 1;
482 if (defined($Exp_stderr)) {
483 $stderr_cmd = " 2>$TMP_STDERR";
485 $retval &= is(`$Cmd$stderr_cmd`, $Exp_stdout, "$Txt (stdout)");
486 my $ret_val = $?;
487 if (defined($Exp_stderr)) {
488 $retval &= is(file_data($TMP_STDERR), $Exp_stderr, "$Txt (stderr)");
489 unlink($TMP_STDERR);
490 } else {
491 diag("Warning: stderr not defined for '$Txt'");
493 $retval &= is($ret_val >> 8, $Exp_retval, "$Txt (retval)");
495 return $retval;
496 # }}}
497 } # testcmd()
499 sub likecmd {
500 # {{{
501 my ($Cmd, $Exp_stdout, $Exp_stderr, $Exp_retval, $Desc) = @_;
502 defined($descriptions{$Desc}) &&
503 BAIL_OUT("likecmd(): '$Desc' description is used twice");
504 $descriptions{$Desc} = 1;
505 my $stderr_cmd = '';
506 my $cmd_outp_str = $Opt{'verbose'} >= 1 ? "\"$Cmd\" - " : '';
507 my $Txt = join('', $cmd_outp_str, defined($Desc) ? $Desc : '');
508 my $TMP_STDERR = "$CMD_BASENAME-stderr.tmp";
509 my $retval = 1;
511 if (defined($Exp_stderr)) {
512 $stderr_cmd = " 2>$TMP_STDERR";
514 $retval &= like(`$Cmd$stderr_cmd`, $Exp_stdout, "$Txt (stdout)");
515 my $ret_val = $?;
516 if (defined($Exp_stderr)) {
517 $retval &= like(file_data($TMP_STDERR), $Exp_stderr, "$Txt (stderr)");
518 unlink($TMP_STDERR);
519 } else {
520 diag("Warning: stderr not defined for '$Txt'");
522 $retval &= is($ret_val >> 8, $Exp_retval, "$Txt (retval)");
524 return $retval;
525 # }}}
526 } # likecmd()
528 sub file_data {
529 # Return file content as a string {{{
530 my $File = shift;
531 my $Txt;
533 open(my $fp, '<', $File) or return undef;
534 local $/ = undef;
535 $Txt = <$fp>;
536 close($fp);
537 return $Txt;
538 # }}}
539 } # file_data()
541 sub print_version {
542 # Print program version {{{
543 print("$progname $VERSION\n");
544 return;
545 # }}}
546 } # print_version()
548 sub usage {
549 # Send the help message to stdout {{{
550 my $Retval = shift;
552 if ($Opt{'verbose'}) {
553 print("\n");
554 print_version();
556 print(<<"END");
558 Usage: $progname [options]
560 Contains tests for the $CMD_BASENAME(1) program.
562 Options:
564 -a, --all
565 Run all tests, also TODOs.
566 -g X, --git X
567 Specify alternative git executable to use. Used to execute the tests
568 with different git versions. This can also be set with the
569 GIT_DBR_GIT environment variable.
570 -h, --help
571 Show this help.
572 -q, --quiet
573 Be more quiet. Can be repeated to increase silence.
574 -t, --todo
575 Run only the TODO tests.
576 -v, --verbose
577 Increase level of verbosity. Can be repeated.
578 --version
579 Print version information.
582 exit($Retval);
583 # }}}
584 } # usage()
586 sub msg {
587 # Print a status message to stderr based on verbosity level {{{
588 my ($verbose_level, $Txt) = @_;
590 $verbose_level > $Opt{'verbose'} && return;
591 print(STDERR "$progname: $Txt\n");
592 return;
593 # }}}
594 } # msg()
596 __END__
598 # This program is free software; you can redistribute it and/or modify
599 # it under the terms of the GNU General Public License as published by
600 # the Free Software Foundation; either version 2 of the License, or (at
601 # your option) any later version.
603 # This program is distributed in the hope that it will be useful, but
604 # WITHOUT ANY WARRANTY; without even the implied warranty of
605 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
606 # See the GNU General Public License for more details.
608 # You should have received a copy of the GNU General Public License
609 # along with this program.
610 # If not, see L<http://www.gnu.org/licenses/>.
612 # vim: set fenc=UTF-8 ft=perl fdm=marker ts=4 sw=4 sts=4 et fo+=w :