extract setup_global_ignore() into testutil.pm
[gnu-stow.git] / t / testutil.pm
blobede2f2c9a302813b7c5cf015cb4a0f477e151457
1 #!/usr/bin/perl
3 # This file is part of GNU Stow.
5 # GNU Stow is free software: you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
10 # GNU Stow is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 # General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program. If not, see https://www.gnu.org/licenses/.
19 # Utilities shared by test scripts
22 package testutil;
24 use strict;
25 use warnings;
27 use Carp qw(confess croak);
28 use File::Basename;
29 use File::Path qw(make_path remove_tree);
30 use File::Spec;
31 use Test::More;
33 use Stow;
34 use Stow::Util qw(parent canon_path join_paths);
36 use base qw(Exporter);
37 our @EXPORT = qw(
38 $ABS_TEST_DIR
39 $TEST_DIR
40 init_test_dirs
42 new_Stow new_compat_Stow
43 make_path make_link make_invalid_link make_file
44 setup_global_ignore
45 remove_dir remove_file remove_link
46 cat_file
47 is_link is_dir_not_symlink is_nonexistent_path
50 our $TEST_DIR = 'tmp-testing-trees';
51 our $ABS_TEST_DIR = File::Spec->rel2abs('tmp-testing-trees');
53 sub init_test_dirs {
54 my $test_dir = shift || $TEST_DIR;
55 my $abs_test_dir = File::Spec->rel2abs($test_dir);
57 # Create a run_from/ subdirectory for tests which want to run
58 # from a separate directory outside the Stow directory or
59 # target directory.
60 for my $dir ("target", "stow", "run_from", "stow directory") {
61 my $path = "$test_dir/$dir";
62 -d $path and remove_tree($path);
63 make_path($path);
66 # Don't let user's ~/.stow-global-ignore affect test results
67 $ENV{HOME} = $abs_test_dir;
68 return $abs_test_dir;
71 sub new_Stow {
72 my %opts = @_;
73 # These default paths assume that execution will be triggered from
74 # within the target directory.
75 $opts{dir} ||= '../stow';
76 $opts{target} ||= '.';
77 $opts{test_mode} = 1;
78 my $stow = eval { new Stow(%opts) };
79 if ($@) {
80 confess "Error while trying to instantiate new Stow(%opts): $@";
82 return $stow;
85 sub new_compat_Stow {
86 my %opts = @_;
87 $opts{compat} = 1;
88 return new_Stow(%opts);
91 #===== SUBROUTINE ===========================================================
92 # Name : make_link()
93 # Purpose : safely create a link
94 # Parameters: $link_src => path to the link
95 # : $link_dest => where the new link should point
96 # : $invalid => true iff $link_dest refers to non-existent file
97 # Returns : n/a
98 # Throws : fatal error if the link can not be safely created
99 # Comments : checks for existing nodes
100 #============================================================================
101 sub make_link {
102 my ($link_src, $link_dest, $invalid) = @_;
104 if (-l $link_src) {
105 my $old_source = readlink join('/', parent($link_src), $link_dest)
106 or croak "$link_src is already a link but could not read link $link_src/$link_dest";
107 if ($old_source ne $link_dest) {
108 croak "$link_src already exists but points elsewhere\n";
111 croak "$link_src already exists and is not a link\n" if -e $link_src;
112 my $abs_target = File::Spec->rel2abs($link_src);
113 my $link_src_container = dirname($abs_target);
114 my $abs_source = File::Spec->rel2abs($link_dest, $link_src_container);
115 #warn "t $link_src c $link_src_container as $abs_source";
116 if (-e $abs_source) {
117 croak "Won't make invalid link pointing to existing $abs_target"
118 if $invalid;
120 else {
121 croak "Won't make link pointing to non-existent $abs_target"
122 unless $invalid;
124 symlink $link_dest, $link_src
125 or croak "could not create link $link_src => $link_dest ($!)\n";
128 #===== SUBROUTINE ===========================================================
129 # Name : make_invalid_link()
130 # Purpose : safely create an invalid link
131 # Parameters: $target => path to the link
132 # : $source => the non-existent source where the new link should point
133 # Returns : n/a
134 # Throws : fatal error if the link can not be safely created
135 # Comments : checks for existing nodes
136 #============================================================================
137 sub make_invalid_link {
138 my ($target, $source, $allow_invalid) = @_;
139 make_link($target, $source, 1);
142 #===== SUBROUTINE ===========================================================
143 # Name : create_file()
144 # Purpose : create an empty file
145 # Parameters: $path => proposed path to the file
146 # : $contents => (optional) contents to write to file
147 # Returns : n/a
148 # Throws : fatal error if the file could not be created
149 # Comments : detects clash with an existing non-file
150 #============================================================================
151 sub make_file {
152 my ($path, $contents) = @_;
154 if (-e $path and ! -f $path) {
155 croak "a non-file already exists at $path\n";
158 open my $FILE ,'>', $path
159 or croak "could not create file: $path ($!)\n";
160 print $FILE $contents if defined $contents;
161 close $FILE;
164 sub setup_global_ignore {
165 my ($contents) = @_;
166 my $global_ignore_file = join_paths($ENV{HOME}, $Stow::GLOBAL_IGNORE_FILE);
167 make_file($global_ignore_file, $contents);
168 return $global_ignore_file;
171 #===== SUBROUTINE ===========================================================
172 # Name : remove_link()
173 # Purpose : remove an esiting symbolic link
174 # Parameters: $path => path to the symbolic link
175 # Returns : n/a
176 # Throws : fatal error if the operation fails or if passed the path to a
177 # : non-link
178 # Comments : none
179 #============================================================================
180 sub remove_link {
181 my ($path) = @_;
182 if (not -l $path) {
183 croak qq(remove_link() called with a non-link: $path);
185 unlink $path or croak "could not remove link: $path ($!)\n";
186 return;
189 #===== SUBROUTINE ===========================================================
190 # Name : remove_file()
191 # Purpose : remove an existing empty file
192 # Parameters: $path => the path to the empty file
193 # Returns : n/a
194 # Throws : fatal error if given file is non-empty or the operation fails
195 # Comments : none
196 #============================================================================
197 sub remove_file {
198 my ($path) = @_;
199 if (-z $path) {
200 croak "file at $path is non-empty\n";
202 unlink $path or croak "could not remove empty file: $path ($!)\n";
203 return;
206 #===== SUBROUTINE ===========================================================
207 # Name : remove_dir()
208 # Purpose : safely remove a tree of test files
209 # Parameters: $dir => path to the top of the tree
210 # Returns : n/a
211 # Throws : fatal error if the tree contains a non-link or non-empty file
212 # Comments : recursively removes directories containing softlinks empty files
213 #============================================================================
214 sub remove_dir {
215 my ($dir) = @_;
217 if (not -d $dir) {
218 croak "$dir is not a directory";
221 opendir my $DIR, $dir or croak "cannot read directory: $dir ($!)\n";
222 my @listing = readdir $DIR;
223 closedir $DIR;
225 NODE:
226 for my $node (@listing) {
227 next NODE if $node eq '.';
228 next NODE if $node eq '..';
230 my $path = "$dir/$node";
231 if (-l $path or (-f $path and -z $path) or $node eq $Stow::LOCAL_IGNORE_FILE) {
232 unlink $path or croak "cannot unlink $path ($!)\n";
234 elsif (-d "$path") {
235 remove_dir($path);
237 else {
238 croak "$path is not a link, directory, or empty file\n";
241 rmdir $dir or croak "cannot rmdir $dir ($!)\n";
243 return;
246 #===== SUBROUTINE ===========================================================
247 # Name : cd()
248 # Purpose : wrapper around chdir
249 # Parameters: $dir => path to chdir to
250 # Returns : n/a
251 # Throws : fatal error if the chdir fails
252 # Comments : none
253 #============================================================================
254 sub cd {
255 my ($dir) = @_;
256 chdir $dir or croak "Failed to chdir($dir): $!\n";
259 #===== SUBROUTINE ===========================================================
260 # Name : cat_file()
261 # Purpose : return file contents
262 # Parameters: $file => file to read
263 # Returns : n/a
264 # Throws : fatal error if the open fails
265 # Comments : none
266 #============================================================================
267 sub cat_file {
268 my ($file) = @_;
269 open F, $file or croak "Failed to open($file): $!\n";
270 my $contents = join '', <F>;
271 close(F);
272 return $contents;
275 #===== SUBROUTINE ===========================================================
276 # Name : is_link()
277 # Purpose : assert path is a symlink
278 # Parameters: $path => path to check
279 # : $dest => target symlink should point to
280 #============================================================================
281 sub is_link {
282 my ($path, $dest) = @_;
283 ok(-l $path => "$path should be symlink");
284 is(readlink $path, $dest => "$path symlinks to $dest");
287 #===== SUBROUTINE ===========================================================
288 # Name : is_dir_not_symlink()
289 # Purpose : assert path is a directory not a symlink
290 # Parameters: $path => path to check
291 #============================================================================
292 sub is_dir_not_symlink {
293 my ($path) = @_;
294 ok(! -l $path => "$path should not be symlink");
295 ok(-d _ => "$path should be a directory");
298 #===== SUBROUTINE ===========================================================
299 # Name : is_nonexistent_path()
300 # Purpose : assert path does not exist
301 # Parameters: $path => path to check
302 #============================================================================
303 sub is_nonexistent_path {
304 my ($path) = @_;
305 ok(! -l $path => "$path should not be symlink");
306 ok(! -e _ => "$path should not exist");
312 # Local variables:
313 # mode: perl
314 # end:
315 # vim: ft=perl