installed_progs.t: Python checks stdout too, 150 ok
[sunny256-utils.git] / tests / git-testadd.t
blobed456e9b95e29d24c828b893b06e245a90168b01
1 #!/usr/bin/env perl
3 #==============================================================================
4 # git-testadd.t
5 # File ID: dd2d5468-4cdb-11e6-bed9-02010e0a6634
7 # Test suite for git-testadd(1).
9 # Character set: UTF-8
10 # ©opyleft 2016– Øyvind A. Holm <sunny@sunbase.org>
11 # License: GNU General Public License version 2 or later, see end of file for
12 # 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-testadd";
28 our $CMD = "../$CMDB";
30 our %Opt = (
32 'all' => 0,
33 'git' => defined($ENV{'TESTADD_GIT'}) ? $ENV{'TESTADD_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 my $Retval = 0;
73 diag(sprintf('========== Executing %s v%s ==========',
74 $progname, $VERSION));
76 if ($Opt{'todo'} && !$Opt{'all'}) {
77 ok(1, "No todo tests here");
78 return 0;
81 test_standard_options();
82 test_executable();
84 diag('Testing finished.');
86 return $Retval;
89 sub test_standard_options {
90 diag('Testing -h (--help) option...');
91 likecmd("$CMD -h",
92 '/ Show this help/i',
93 '/^$/',
95 'Option -h prints help screen');
97 diag('Testing -v (--verbose) option...');
98 likecmd("$CMD -h -v",
99 '/^\n\S+ \d+\.\d+\.\d+/s',
100 '/^$/',
102 'Option -v with -h returns version number and help screen');
104 diag('Testing --version option...');
105 likecmd("$CMD --version",
106 '/^\S+ \d+\.\d+\.\d+/',
107 '/^$/',
109 'Option --version returns version number');
110 return;
113 =pod
115 test_executable() - Specify options and expected output to test. The expected
116 output is represented as special flags in a comma-separated list, all flags
117 must have a comme before and after them. Some flags expect "()" added to them,
118 with an optional value inside.
120 =cut
122 sub test_executable {
123 my $toptmp = "tmp-git-testadd-t-$$-" . substr(rand, 2, 8);
124 my $repo = "$toptmp/repo";
126 diag("Initialise Git repository");
127 ok(! -d $toptmp, "[toptmp] doesn't exist") ||
128 BAIL_OUT("$toptmp already exists");
129 ok(mkdir($toptmp), "mkdir [toptmp]");
130 cmd("$Opt{'git'} init \"$repo\"", "$Opt{'git'} init [repo]");
131 ok(-d "$repo/.git", "[repo]/.git exists") ||
132 BAIL_OUT("$repo/.git doesn't exist");
133 ok(chdir($repo), "chdir [repo]");
134 $CMD = "../../$CMD";
136 test_options_without_commits();
137 test_options_with_commits();
139 diag("Clean up files");
140 ok(chdir("../.."), "chdir ../..");
141 $CMD =~ s/^\.\.\/\.\.\///;
142 ok(-d $repo, "[repo] exists") || BAIL_OUT("$repo not found");
143 testcmd("rm -rf \"$repo\"", "", "", 0, "rm -rf [repo]");
144 ok(rmdir($toptmp), "rmdir [toptmp]");
147 sub test_options_without_commits {
148 diag("No options");
149 test_options("No options",
150 ",rm(),clone(),cd(),cmd,", ",using(),nostaged,cmd,", 0,
151 "");
153 diag("-l/--label");
154 test_options("-l/--label",
155 ",rm(mylabel),clone(mylabel),cd(mylabel),cmd,",
156 ",using(mylabel),nostaged,cmd,", 0,
157 "-l mylabel", "--label mylabel");
159 diag("-p/--pristine");
160 test_options("-p/--pristine",
161 ",rm(),clone(),cd(),cmd,",
162 ",using(),unmodified,cmd,", 0,
163 "-p", "--pristine");
165 diag("-r/--ref");
166 test_options("-r/--ref",
168 ",invalidref(nosuchrefmate),", 1,
169 "-r nosuchrefmate", "--ref nosuchrefmate");
171 diag("-u/--unmodified");
172 test_options("-u/--unmodified, missing destdir",
174 ",using(),notfound(),", 1,
175 "-u", "--unmodified");
178 sub test_options_with_commits {
179 diag("Add, commit and modify a new file");
180 commit_new_file("file.txt");
181 is(commit_log("master"),
182 "1f935e2a5946e77df739c9d2f376af3778ddb71e Add file.txt\n",
183 "git log after file.txt was added");
184 cmd("$Opt{'git'} branch first", "Create 'first' branch");
185 is(commit_log("first"),
186 "1f935e2a5946e77df739c9d2f376af3778ddb71e Add file.txt\n",
187 "'first' branch looks ok");
188 cmd("echo New line >>file.txt", "Add new line to file.txt");
189 is(file_data("file.txt"), "This is file.txt\nNew line\n",
190 "New line was added to file.txt");
192 diag("Test without staged changes");
193 test_options("No options, no staged changes",
194 ",rm(),clone(),cd(),cmd,", ",using(),nostaged,cmd,", 0,
195 "");
197 diag("Test with staged changes");
198 cmd("$Opt{'git'} add file.txt", "Add changes in file.txt to Git");
199 test_options("No options, staged changes",
200 ",rm(),clone(),apply(),cd(),cmd,",
201 ",using(),applying,cmd,", 0, "");
203 diag("-l/--label");
204 test_options("-l/--label, staged changes",
205 ",rm(mylabel),clone(mylabel),apply(mylabel)," .
206 "cd(mylabel),cmd,",
207 ",using(mylabel),applying,cmd,", 0,
208 "-l mylabel", "--label mylabel");
210 diag("-p/--pristine");
211 test_options("-p/--pristine, staged changes",
212 ",rm(),clone(),cd(),cmd,",
213 ",using(),unmodified,cmd,", 0,
214 "-p", "--pristine");
216 diag("-u/--unmodified");
217 test_options("-u/--unmodified, staged changes",
219 ",using(),notfound(),", 1,
220 "-u", "--unmodified");
222 diag("-r/--ref");
223 cmd("$Opt{'git'} commit -m 'Add line to file.txt'",
224 "Commit line addition to file.txt");
225 is(commit_log("master"),
226 "dec5ff4abeb9999e6e6989a549ccc95185e7dfab Add line to file.txt\n" .
227 "1f935e2a5946e77df739c9d2f376af3778ddb71e Add file.txt\n",
228 "git log is ok after line addition");
229 test_options("-r/--ref",
231 ",invalidref(nosuchrefwc),", 1,
232 "-r nosuchrefwc", "--ref nosuchrefwc");
233 cmd("echo Third line >>file.txt", "Add third line to file.txt");
234 cmd("$Opt{'git'} add file.txt", "Stage third line in file.txt");
235 likecmd("$CMD -r first echo yup",
236 '/.*/',
237 o_err(",using(),clone(),swnewbranch(first),applying" .
238 ",applyfailed(file.txt),"),
240 "-r first, apply should fail");
241 cmd("$Opt{'git'} reset", "git reset");
242 cmd("$Opt{'git'} checkout -f file.txt",
243 "Remove changes from file.txt");
244 create_file("file2.txt", "This is file2.txt");
245 cmd("$Opt{'git'} add file2.txt", "Stage file2.txt for addition");
246 likecmd("$CMD -r first echo yup",
247 '/yup\n$/s',
248 o_err(",using(),clone(),swnewbranch(first),applying,cmd," .
249 ""),
251 "-r first, apply of file2.txt should succeed");
254 =pod
256 test_options() - Test the executable with the received options. Runs the
257 program with -n (--dry-run) and checks stdout, stderr and exit value.
259 =cut
261 sub test_options {
262 my ($desc, $stdout, $stderr, $exitval, @opts) = @_;
264 for my $opt (@opts) {
265 my $spc = length($opt) ? " " : "";
267 likecmd("$CMD -n$spc$opt echo yup",
268 o_out($stdout), o_err($stderr), $exitval,
269 "$desc ($opt)");
273 =pod
275 o_out() - Return string with expected stdout output. Parse contents of $flags,
276 a comma-separated list of flags with optional arguments in ().
278 =cut
280 sub o_out {
281 my $flags = shift;
282 my $retval = "";
284 $retval .= '/^';
285 if ($flags =~ /,rm\(([^\(\)]*)\),/) {
286 my $val = $1;
288 $val = length($val) ? "-$val" : "";
289 $retval .= "rm -rf \\.testadd$val\\.tmp\\n";
291 if ($flags =~ /,clone\(([^\(\)]*)\),/) {
292 my $val = $1;
294 $val = length($val) ? "-$val" : "";
295 $retval .= ".+ clone .+ \\.testadd$val\\.tmp\\n";
297 if ($flags =~ /,apply\(([^\(\)]*)\),/) {
298 my $val = $1;
300 $val = length($val) ? "-$val" : "";
301 $retval .= "eval .+ diff --cached --binary " .
302 "--no-textconv \\| " .
303 "\\(cd \"\\.testadd$val\\.tmp\" \\&\\& " .
304 ".+ apply\\)\\n" . "";
306 if ($flags =~ /,cd\(([^\(\)]*)\),/) {
307 my $val = $1;
309 $val = length($val) ? "-$val" : "";
310 $retval .= "cd \\.testadd$val\\.tmp\\/\\n";
312 if ($flags =~ /,cmd,/) {
313 $retval .= "eval echo yup\\n";
315 $retval .= '$/s';
317 return $retval;
320 =pod
322 o_err() - Return string with expected stderr output. Parse contents of $flags,
323 a comma-separated list of flags with optional arguments in ().
325 =cut
327 sub o_err {
328 my $flags = shift;
329 my $retval = "";
331 $retval .= '/^';
332 if ($flags =~ /,using\(([^\(\)]*)\),/) {
333 my $val = $1;
335 $val = length($val) ? "-$val" : "";
336 $retval .= "git-testadd: Using \"\\.testadd$val\\.tmp\" as " .
337 "destination directory\\n";
339 if ($flags =~ /,clone\(([^\(\)]*)\),/) {
340 my $val = $1;
342 $val = length($val) ? "-$val" : "";
343 $retval .= "Cloning into " .
344 "\\'\\.testadd$val\\.tmp\\'\\.\\.\\.\\n" .
345 "done\\.\\n";
347 if ($flags =~ /,invalidref\(([^\(\)]*)\),/) {
348 my $val = $1;
350 $retval .= "fatal: Needed a single revision\\n" .
351 "git-testadd: $val: Invalid Git ref\\n";
353 if ($flags =~ /,notfound\(([^\(\)]*)\),/) {
354 my $val = $1;
356 $val = length($val) ? "-$val" : "";
357 $retval .= "git-testadd: \\.testadd$val\\.tmp not found, " .
358 "-u\\/--unmodified needs it\\n";
360 if ($flags =~ /,swnewbranch\(([^\(\)]*)\),/) {
361 my $val = $1;
363 $retval .= "Switched to a new branch '$val'\\n";
365 if ($flags =~ /,applying,/) {
366 $retval .= "git-testadd: Applying staged changes\\n";
368 if ($flags =~ /,applyfailed\(([^\(\)]*)\),/) {
369 my $val = $1;
371 $retval .= "error: patch failed: $val:1\\n" .
372 "error: $val: patch does not apply\\n" .
373 "git-testadd: Could not apply patch\\n";
375 if ($flags =~ /,nostaged,/) {
376 $retval .= "git-testadd: No staged changes, running command " .
377 "with clean HEAD\\n";
379 if ($flags =~ /,unmodified,/) {
380 $retval .= "git-testadd: Running command with unmodified " .
381 "HEAD\\n";
383 if ($flags =~ /,cmd,/) {
384 $retval .= "\\n";
385 $retval .= "git-testadd: Executing \"echo yup\" in .+\\n";
387 $retval .= '$/s';
389 return $retval;
392 sub commit_new_file {
393 my $file = shift;
395 ok(!-e $file, "$file doesn't exist");
396 create_file($file, "This is $file\n");
397 cmd("$Opt{'git'} add \"$file\"", "$Opt{'git'} add $file");
398 cmd("$Opt{'git'} commit -m \"Add $file\"",
399 "$Opt{'git'} commit (add $file)");
402 sub commit_log {
403 my $ref = shift;
404 my $retval = '';
406 open(my $pipefp,
407 "$Opt{'git'} log --format='%T %s' --topo-order $ref |") or
408 return "'$Opt{'git'} log' pipe error: $!\n";
409 while (<$pipefp>) {
410 $retval .= $_;
412 close($pipefp);
414 return $retval;
417 sub cmd {
418 my ($cmd, $desc) = @_;
420 likecmd($cmd, '/.*/', '/.*/', 0, $desc);
423 sub testcmd {
424 my ($Cmd, $Exp_stdout, $Exp_stderr, $Exp_retval, $Desc) = @_;
425 defined($descriptions{$Desc}) &&
426 BAIL_OUT("testcmd(): '$Desc' description is used twice");
427 $descriptions{$Desc} = 1;
428 my $stderr_cmd = '';
429 my $cmd_outp_str = $Opt{'verbose'} >= 1 ? "\"$Cmd\" - " : '';
430 my $Txt = join('', $cmd_outp_str, defined($Desc) ? $Desc : '');
431 my $TMP_STDERR = "$CMDB-stderr.tmp";
432 my $retval = 1;
434 if (defined($Exp_stderr)) {
435 $stderr_cmd = " 2>$TMP_STDERR";
437 $retval &= is(`$Cmd$stderr_cmd`, $Exp_stdout, "$Txt (stdout)");
438 my $ret_val = $?;
439 if (defined($Exp_stderr)) {
440 $retval &= is(file_data($TMP_STDERR),
441 $Exp_stderr, "$Txt (stderr)");
442 unlink($TMP_STDERR);
443 } else {
444 diag("Warning: stderr not defined for '$Txt'");
446 $retval &= is($ret_val >> 8, $Exp_retval, "$Txt (retval)");
448 return $retval;
451 sub likecmd {
452 my ($Cmd, $Exp_stdout, $Exp_stderr, $Exp_retval, $Desc) = @_;
453 defined($descriptions{$Desc}) &&
454 BAIL_OUT("likecmd(): '$Desc' description is used twice");
455 $descriptions{$Desc} = 1;
456 my $stderr_cmd = '';
457 my $cmd_outp_str = $Opt{'verbose'} >= 1 ? "\"$Cmd\" - " : '';
458 my $Txt = join('', $cmd_outp_str, defined($Desc) ? $Desc : '');
459 my $TMP_STDERR = "$CMDB-stderr.tmp";
460 my $retval = 1;
462 if (defined($Exp_stderr)) {
463 $stderr_cmd = " 2>$TMP_STDERR";
465 $retval &= like(`$Cmd$stderr_cmd`, $Exp_stdout, "$Txt (stdout)");
466 my $ret_val = $?;
467 if (defined($Exp_stderr)) {
468 $retval &= like(file_data($TMP_STDERR),
469 $Exp_stderr, "$Txt (stderr)");
470 unlink($TMP_STDERR);
471 } else {
472 diag("Warning: stderr not defined for '$Txt'");
474 $retval &= is($ret_val >> 8, $Exp_retval, "$Txt (retval)");
476 return $retval;
479 sub file_data {
480 # Return file content as a string
481 my $File = shift;
482 my $Txt;
484 open(my $fp, '<', $File) or return undef;
485 local $/ = undef;
486 $Txt = <$fp>;
487 close($fp);
488 return $Txt;
491 sub create_file {
492 # Create new file and fill it with data
493 my ($file, $text) = @_;
494 my $retval = 0;
496 open(my $fp, ">$file") or return 0;
497 print($fp $text);
498 close($fp);
499 $retval = is(file_data($file), $text,
500 "$file was successfully created");
502 return $retval; # 0 if error, 1 if ok
505 sub print_version {
506 # Print program version
507 print("$progname $VERSION\n");
508 return;
511 sub usage {
512 # Send the help message to stdout
513 my $Retval = shift;
515 if ($Opt{'verbose'}) {
516 print("\n");
517 print_version();
519 print(<<"END");
521 Usage: $progname [options]
523 Contains tests for the $CMDB(1) program.
525 Options:
527 -a, --all
528 Run all tests, also TODOs.
529 -g X, --git X
530 Specify alternative git executable to use. Used to execute the tests
531 with different git versions. This can also be set with the
532 TESTADD_GIT environment variable.
533 -h, --help
534 Show this help.
535 -q, --quiet
536 Be more quiet. Can be repeated to increase silence.
537 -t, --todo
538 Run only the TODO tests.
539 -v, --verbose
540 Increase level of verbosity. Can be repeated.
541 --version
542 Print version information.
545 exit($Retval);
548 sub msg {
549 # Print a status message to stderr based on verbosity level
550 my ($verbose_level, $Txt) = @_;
552 $verbose_level > $Opt{'verbose'} && return;
553 print(STDERR "$progname: $Txt\n");
554 return;
557 __END__
559 # This program is free software; you can redistribute it and/or modify it under
560 # the terms of the GNU General Public License as published by the Free Software
561 # Foundation; either version 2 of the License, or (at your option) any later
562 # version.
564 # This program is distributed in the hope that it will be useful, but WITHOUT
565 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
566 # FOR A PARTICULAR PURPOSE.
567 # See the GNU General Public License for more details.
569 # You should have received a copy of the GNU General Public License along with
570 # this program.
571 # If not, see L<http://www.gnu.org/licenses/>.
573 # vim: set ts=8 sw=8 sts=8 noet fo+=w tw=79 fenc=UTF-8 :