3 #=======================================================================
5 # File ID: 8f5fa76e-a802-11e5-bb87-fefdb24f8e10
7 # Test suite for filesynced(1).
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 #=======================================================================
19 use Test
::More
qw{no_plan
};
28 our $CMDB = "filesynced";
29 our $CMD = "../$CMDB";
30 my $SQLITE = "sqlite3";
44 $progname =~ s/^.*\/(.*?)$/$1/;
45 our $VERSION = '0.0.0';
47 my %descriptions = ();
49 Getopt
::Long
::Configure
('bundling');
52 'all|a' => \
$Opt{'all'},
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'}) {
76 diag
(sprintf('========== Executing %s v%s ==========',
77 $progname, $VERSION));
79 if ($Opt{'todo'} && !$Opt{'all'}) {
85 testcmd("$CMD command", # {{{
98 diag
('Testing -h (--help) option...');
99 likecmd
("$CMD -h", # {{{
100 '/ Show this help/i',
103 'Option -h prints help screen',
107 diag
('Testing -v (--verbose) option...');
108 likecmd
("$CMD -h -v", # {{{
109 '/^\n\S+ \d+\.\d+\.\d+/s',
112 'Option -v with -h returns version number and help screen',
116 diag
('Testing --version option...');
117 likecmd
("$CMD --version", # {{{
118 '/^\S+ \d+\.\d+\.\d+/',
121 'Option --version returns version number',
126 my $syncfile = "$ENV{'HOME'}/bin/synced.sql";
128 diag
("$syncfile not found, skipping tests");
132 my $Tmptop = "tmp-filesynced-t-$$-" . substr(rand, 2, 8);
135 ok
(mkdir($Tmptop), "mkdir [Tmptop]");
136 ok
(chdir($Tmptop), "chdir [Tmptop]") || BAIL_OUT
();
137 likecmd
("$GIT init repo-fs-t", # {{{
141 "git init repo-fs-t",
145 ok
(-d
"repo-fs-t/.git", "repo-fs-t/.git exists") || BAIL_OUT
();
146 ok
(-d
"../$Tmptop", "We're in [Tmptop]") || BAIL_OUT
();
147 ok
(chdir("repo-fs-t"), "chdir repo-fs-t");
148 $CMD = "../../../$CMDB";
149 ok
(-f
$CMD, "Executable is in place") || BAIL_OUT
();
150 testcmd
("$CMD -v", # No options, no database {{{
152 "filesynced: synced.sqlite: Sync database not found\n",
154 "No options, no database",
158 testcmd
("$CMD -l -v", # No database and -l {{{
160 "filesynced: synced.sqlite: Sync database not found\n",
162 "No database and -l",
167 chomp(my $sql_create_synced = <<END); # {{{
168 CREATE TABLE synced (
170 CONSTRAINT synced_file_length
171 CHECK (length(file) > 0)
178 CONSTRAINT synced_rev_length
179 CHECK (length(rev) = 40 OR rev = '')
182 CONSTRAINT synced_date_length
183 CHECK (date IS NULL OR length(date) = 19)
184 CONSTRAINT synced_date_valid
185 CHECK (date IS NULL OR datetime(date) IS NOT NULL)
189 chomp(my $sql_create_todo = <<END); # {{{
192 CONSTRAINT todo_file_length
193 CHECK(length(file) > 0)
198 CONSTRAINT todo_pri_range
199 CHECK(pri BETWEEN 1 AND 5)
205 testcmd
("$CMD --init", # {{{
209 "--init without options",
213 is
(sql_dump
("synced.sql"), <<END, "synced.sql is ok"); # {{{
219 likecmd
("$CMD --init", # {{{
221 '/\/repo-fs-t\/synced.sql already exists\n$/s',
223 "Refuse to --init when synced.sql exists",
227 testcmd
("sqlite3 synced.sqlite <synced.sql", '', '', 0, # {{{
228 "Create synced.sqlite from synced.sql");
231 ok
(unlink("synced.sql"), "Delete synced.sql");
232 likecmd
("$CMD --init", # {{{
234 '/\/repo-fs-t\/synced.sqlite: File already exists\n' .
235 'filesynced: No token received from filesynced --lock\n' .
238 "It also reacts negatively to the presence of synced.sqlite",
242 ok
(unlink("synced.sqlite"), "Delete synced.sqlite");
244 testcmd
("$CMD --init", # {{{
248 "Create synced.sql again with --init",
252 testcmd
("$CMD --lock >key.txt", "", "", 0, # {{{
253 "Use --lock, store key in key.txt",
257 my $realtoken = file_data
("key.txt");
258 likecmd
("$CMD --lock --timeout 0", # {{{
261 'filesynced --lock: .+\/repo-fs-t\/synced.sql\.lock: ' .
262 'Waiting for lockdir\.\.\.\n' .
263 'filesynced: Lock not aquired after 0 seconds, aborting\n' .
266 "Try to lock again, wimp gives up after 0 seconds",
270 like
($realtoken, # {{{
274 '20\d\d' . '[01]\d' . '\d\d' .
276 '[0-2]\d' . '[0-5]\d' . '[0-6]\d' .
288 testcmd
("$CMD --unlock", # {{{
290 "filesynced --unlock: Token mismatch\n",
292 "No argument to --unlock",
296 testcmd
("$CMD --unlock ''", # {{{
298 "filesynced --unlock: Token mismatch\n",
300 "--unlock receives empty string",
304 testcmd
("$CMD --unlock token_20141212T123456Z.1234." . ("2" x
40), # {{{
306 "filesynced --unlock: Token mismatch\n",
308 "--unlock token is wrong",
312 testcmd
("$CMD --unlock $realtoken", # {{{
316 "--unlock token is valid",
320 likecmd
("$CMD --lock", # {{{
324 "--lock, throw away the token",
328 testcmd
("$CMD --unlock -f", # {{{
332 "--unlock with -f (force)",
336 likecmd
("$CMD --lock", # {{{
340 "--lock again, throw away the token",
344 testcmd
("$CMD --force --unlock", # {{{
348 "--unlock with --force",
353 testcmd
("$CMD --add nonexisting.txt", # {{{
355 "filesynced: nonexisting.txt: File not found, no entries updated\n",
357 "Try to --add non-existing file",
361 ok
(create_file
("tmpfile.txt", "This is tmpfile.txt"),
362 "Create tmpfile.txt");
363 testcmd
("$CMD --add tmpfile.txt nonexisting.txt", # {{{
365 "filesynced: nonexisting.txt: File not found, no entries updated\n",
367 "Try to --add existing and non-existing file",
371 is
(sql_dump
("synced.sql"), # {{{
376 "tmpfile.txt is not added to synced.sql yet",
380 testcmd
("$CMD --add tmpfile.txt", # {{{
384 "Add tmpfile.txt with --add",
388 is
(sql_dump
("synced.sql"), # {{{
392 synced|tmpfile.txt|NULL|NULL|NULL
394 "tmpfile.txt is added to synced.sql",
398 likecmd
("$CMD --add tmpfile.txt", # {{{
400 '/filesynced: Cannot add "tmpfile\.txt" to the database, ' .
401 'no entries updated\n/s',
403 "Fail to add it again",
407 is
(sql_dump
("synced.sql"), # {{{
411 synced|tmpfile.txt|NULL|NULL|NULL
413 "There's only one tmpfile.txt in synced.sql",
417 ok
(!-d
"synced.sql.lock", "synced.sql.lock/ is gone");
419 testcmd
("$CMD --delete nonexisting.txt", # {{{
423 "--delete nonexisting.txt",
427 testcmd
("$CMD --delete tmpfile.txt", # {{{
429 "filesynced: Deleted tmpfile.txt from synced\n",
431 "--delete tmpfile.txt",
435 is
(sql_dump
("synced.sql"), # {{{
440 "tmpfile.txt is gone from synced.sql",
444 testcmd
("$CMD --add -t bash tmpfile.txt", # {{{
448 "Add tmpfile.txt again, now with -t bash",
452 is
(sql_dump
("synced.sql"), # {{{
456 synced|tmpfile.txt|Lib/std/bash|NULL|NULL
458 "tmpfile.txt is added to synced.sql with orig value",
463 create_file
("file1", "This is file1.\n");
464 testcmd
("git add file1", # {{{
472 likecmd
("git commit -m 'Add file1'", # {{{
473 '/^.+Add file1.+$/s',
480 testcmd
("$CMD --unsynced", # {{{
484 "file1 is not listed by --unsynced because " .
485 "it's not in synced.sql",
489 testcmd
("$CMD --add -t Lib/std/bash file1", # {{{
497 testcmd
("$CMD --unsynced", # {{{
501 "file1 is listed by --unsynced",
505 testcmd
("$CMD HEAD file1", # {{{
509 "Mark file1 as updated",
513 testcmd
("$CMD --unsynced", # {{{
517 "file1 should be gone from --unsynced now",
521 # diag("--valid-sha");
522 # FIXME: Create tests for --valid-sha. Have to set up some sync
524 testcmd
("$CMD --delete file1", # {{{
526 "filesynced: Deleted file1 from synced\n",
532 diag
("--create-index");
533 testcmd
("$CMD --create-index", # {{{
537 "Use with --create-index",
541 is
(sql_dump
("synced.sql"), # {{{
545 CREATE INDEX idx_synced_file ON synced (file);
546 CREATE INDEX idx_synced_orig ON synced (orig);
547 CREATE INDEX idx_synced_rev ON synced (rev);
548 synced|tmpfile.txt|Lib/std/bash|NULL|NULL
550 "synced.sql contains indexes",
556 ok
(chdir(".."), "chdir ..");
557 testcmd
("rm -rf repo-fs-t", '', '', 0, "Delete repo-fs-t/");
558 ok
(chdir(".."), "chdir ..");
559 ok
(rmdir($Tmptop), "rmdir [Tmptop]");
564 if ($Opt{'all'} || $Opt{'todo'}) {
565 diag
('Running TODO tests...'); # {{{
570 # Insert TODO tests here.
576 diag
('Testing finished.');
586 msg
(5, "sql(): db = '$db'");
587 local(*CHLD_IN
, *CHLD_OUT
, *CHLD_ERR
);
590 my $pid = open3
(*CHLD_IN
, *CHLD_OUT
, *CHLD_ERR
, $SQLITE, $db) or (
592 msg
(0, "sql(): open3() error: $!"),
593 return("sql() error"),
595 msg
(5, "sql(): sql = '$sql'");
596 print(CHLD_IN
"$sql\n") or msg
(0, "sql(): print CHLD_IN error: $!");
598 @retval = <CHLD_OUT
>;
599 msg
(5, "sql(): retval = '" . join('|', @retval) . "'");
600 my @child_stderr = <CHLD_ERR
>;
601 if (scalar(@child_stderr)) {
602 msg
(1, "$SQLITE error: " . join('', @child_stderr));
605 return(join('', @retval));
610 # Return contents of database file {{{
613 return sql
($File, <<END);
616 SELECT 'synced', * FROM synced;
624 my $db = "$File.sqlite";
627 is
(system("$SQLITE $db <$File"), 0, "Create $db from $File");
628 ok
(-f
$db, "$db exists");
629 $Txt = sqlite_dump
("$db");
630 ok
(unlink($db), "Delete $db");
638 my ($Cmd, $Exp_stdout, $Exp_stderr, $Exp_retval, $Desc) = @_;
639 defined($descriptions{$Desc}) &&
640 BAIL_OUT
("testcmd(): '$Desc' description is used twice");
641 $descriptions{$Desc} = 1;
643 my $cmd_outp_str = $Opt{'verbose'} >= 1 ?
"\"$Cmd\" - " : '';
644 my $Txt = join('', $cmd_outp_str, defined($Desc) ?
$Desc : '');
645 my $TMP_STDERR = "$CMDB-stderr.tmp";
648 if (defined($Exp_stderr)) {
649 $stderr_cmd = " 2>$TMP_STDERR";
651 $retval &= is
(`$Cmd$stderr_cmd`, $Exp_stdout, "$Txt (stdout)");
653 if (defined($Exp_stderr)) {
654 $retval &= is
(file_data
($TMP_STDERR), $Exp_stderr, "$Txt (stderr)");
657 diag
("Warning: stderr not defined for '$Txt'");
659 $retval &= is
($ret_val >> 8, $Exp_retval, "$Txt (retval)");
667 my ($Cmd, $Exp_stdout, $Exp_stderr, $Exp_retval, $Desc) = @_;
668 defined($descriptions{$Desc}) &&
669 BAIL_OUT
("likecmd(): '$Desc' description is used twice");
670 $descriptions{$Desc} = 1;
672 my $cmd_outp_str = $Opt{'verbose'} >= 1 ?
"\"$Cmd\" - " : '';
673 my $Txt = join('', $cmd_outp_str, defined($Desc) ?
$Desc : '');
674 my $TMP_STDERR = "$CMDB-stderr.tmp";
677 if (defined($Exp_stderr)) {
678 $stderr_cmd = " 2>$TMP_STDERR";
680 $retval &= like
(`$Cmd$stderr_cmd`, $Exp_stdout, "$Txt (stdout)");
682 if (defined($Exp_stderr)) {
683 $retval &= like
(file_data
($TMP_STDERR), $Exp_stderr, "$Txt (stderr)");
686 diag
("Warning: stderr not defined for '$Txt'");
688 $retval &= is
($ret_val >> 8, $Exp_retval, "$Txt (retval)");
695 # Return file content as a string {{{
699 open(my $fp, '<', $File) or return undef;
708 # Create new file and fill it with data {{{
709 my ($file, $text) = @_;
711 if (open(my $fp, ">$file")) {
717 "$file was successfully created",
720 return($retval); # 0 if error, 1 if ok
725 # Print program version {{{
726 print("$progname $VERSION\n");
732 # Send the help message to stdout {{{
735 if ($Opt{'verbose'}) {
741 Usage: $progname [options]
743 Contains tests for the $CMDB(1) program.
748 Run all tests, also TODOs.
752 Be more quiet. Can be repeated to increase silence.
754 Run only the TODO tests.
756 Increase level of verbosity. Can be repeated.
758 Print version information.
766 # Print a status message to stderr based on verbosity level {{{
767 my ($verbose_level, $Txt) = @_;
769 $verbose_level > $Opt{'verbose'} && return;
770 print(STDERR
"$progname: $Txt\n");
777 # This program is free software; you can redistribute it and/or modify
778 # it under the terms of the GNU General Public License as published by
779 # the Free Software Foundation; either version 2 of the License, or (at
780 # your option) any later version.
782 # This program is distributed in the hope that it will be useful, but
783 # WITHOUT ANY WARRANTY; without even the implied warranty of
784 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
785 # See the GNU General Public License for more details.
787 # You should have received a copy of the GNU General Public License
788 # along with this program.
789 # If not, see L<http://www.gnu.org/licenses/>.
791 # vim: set fenc=UTF-8 ft=perl fdm=marker ts=4 sw=4 sts=4 et fo+=w :