1 # Copyright (C) 2004, 2005, 2006, 2008, 2012 Alex Schroeder <alex@gnu.org>
3 # This program is free software; you can redistribute it and/or modify it under
4 # the terms of the GNU General Public License as published by the Free Software
5 # Foundation; either version 3 of the License, or (at your option) any later
8 # This program is distributed in the hope that it will be useful, but WITHOUT
9 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
10 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 # You should have received a copy of the GNU General Public License along with
13 # this program. If not, see <http://www.gnu.org/licenses/>.
21 # Test::More explains how to fix wide character in print issues
22 my $builder = Test::More->builder;
23 binmode $builder->output, ":utf8";
24 binmode $builder->failure_output, ":utf8";
25 binmode $builder->todo_output, ":utf8";
27 # Import the functions
29 $raw = 0; # capture utf8 is the default
30 $RunCGI = 0; # don't print HTML on stdout
31 $UseConfig = 0; # don't read module files
32 $DataDir = 'test-data';
33 $ENV{WikiDataDir} = $DataDir;
36 # Try to guess which Perl we should be using. Since we loaded wiki.pl,
37 # our $ENV{PATH} is set to /bin:/usr/bin in order to find diff and
39 if ($ENV{PERLBREW_PATH}) {
40 $ENV{PATH} = $ENV{PERLBREW_PATH} . ':' . $ENV{PATH};
41 } elsif (-f '/usr/local/bin/perl') {
42 $ENV{PATH} = '/usr/local/bin:' . $ENV{PATH};
46 use vars qw($redirect);
49 $| = 1; # no output buffering
53 return '' unless $str;
54 utf8::encode($str); # turn to byte string
55 my @letters = split(//, $str);
56 my @safe = ('a' .. 'z', 'A' .. 'Z', '0' .. '9', '-', '_', '.'); # shell metachars are unsafe
57 foreach my $letter (@letters) {
58 my $pattern = quotemeta($letter);
59 if (not grep(/$pattern/, @safe)) {
60 $letter = sprintf("%%%02x", ord($letter));
63 return join('', @letters);
66 # Run perl in a subprocess and make sure it prints UTF-8 and not Latin-1
67 # If you use the download action, the output will be raw bytes. Use
68 # something like the following:
71 # $page = get_page('action=download id=Trogs');
76 open (CL, '-|', $command) or die "Can't run $command: $!";
78 open (CL, '-|:encoding(utf-8)', $command) or die "Can't run $command: $!";
86 my ($id, $text, $summary, $minor, $admin, @rest) = @_;
87 my $pwd = $admin ? 'foo' : 'wrong';
88 my $page = url_encode($id);
89 $text = url_encode($text);
90 $summary = url_encode($summary);
91 $minor = $minor ? 'on' : 'off';
92 my $rest = join(' ', @rest);
93 $redirect = capture("perl wiki.pl 'Save=1' 'title=$page' 'summary=$summary' 'recent_edit=$minor' 'text=$text' 'pwd=$pwd' $rest");
94 $output = capture("perl wiki.pl action=browse id=$page $rest");
95 if ($redirect =~ /^Status: 302 /) {
96 # just in case a new page got created or NearMap or InterMap
98 @IndexList = sort(keys %IndexHash);
99 ReInit($id); # if $id eq $InterMap, we need it to be in the $IndexHash before running ReInit()
105 return capture("perl wiki.pl @_");
111 $_ = '...' . substr($_, -60) if length > 63;
117 return map { s/\\n/\n/g; $_; } @strings;
120 # alternating input and output strings for applying rules
122 # translate embedded newlines (other backslashes remain untouched)
123 my @tests = newlines(@_);
124 my ($input, $output);
125 while (($input, $output, @tests) = @tests) {
126 my $result = apply_rules($input);
127 is($result, $output, name($input));
135 open(STDOUT, '>', \$output) or die "Can't open memory file: $!";
137 ApplyRules(QuoteHtml($input), 1);
141 # alternating input and output strings for applying macros instead of rules
142 sub run_macro_tests {
143 # translate embedded newlines (other backslashes remain untouched)
144 my %test = map { s/\\n/\n/g; $_; } @_;
145 # Note that the order of tests is not specified!
146 foreach my $input (keys %test) {
148 foreach my $macro (@MyMacros) { &$macro; }
149 is($_, $test{$input}, $input);
153 # one string, many tests
155 my ($page, @tests) = @_;
156 foreach my $test (@tests) {
157 like($page, qr($test), name($test));
161 # one string, many negative tests
162 sub test_page_negative {
164 foreach my $str (@_) {
165 unlike($page, qr($str), name("not $str"));
170 my ($check, $message, $page, @tests) = @_;
171 $page =~ s/^.*?(<html)/$1/s; # strip headers
172 $page =~ s/^.*?<\?xml.*?>\s*//s; # strip xml processing
174 my $parser = XML::LibXML->new();
178 eval { $doc = $parser->parse_html_string($page) };
179 eval { $doc = $parser->parse_string($page) } if $@;
180 skip("Cannot parse ".name($page).": $@", $#tests + 1) if $@;
181 foreach my $test (@tests) {
184 # utf8::encode: Converts in-place the character sequence to the
185 # corresponding octet sequence in *UTF-X*. The UTF8 flag is
186 # turned off, so that after this operation, the string is a byte
187 # string. (I have no idea why this is necessary, but there you
188 # go. See encoding.t tests and make sure the page file is
189 # encoded correctly.)
190 utf8::encode($bytes);
191 eval { $nodelist = $doc->findnodes($bytes) };
193 fail(&$check(1) ? "$test: $@" : "not $test: $@");
194 } elsif (ok(&$check($nodelist->size()),
195 name(&$check(1) ? $test : "not $test"))) {
196 $result .= $nodelist->string_value();
198 $page =~ s/^.*?<html/<html/s;
199 diag($message, substr($page,0,30000)) unless $page_shown;
204 return $result; # return string_value() of all found nodes
208 xpath_do(sub { shift > 0; }, "No Matches\n", @_);
211 sub negative_xpath_test {
212 xpath_do(sub { shift == 0; }, "Unexpected Matches\n", @_);
216 sub xpath_test_negative {
217 return negative_xpath_test(@_);
220 sub xpath_run_tests {
221 # translate embedded newlines (other backslashes remain untouched)
222 my @tests = newlines(@_);
223 my ($input, $output);
224 while (($input, $output, @tests) = @tests) {
225 my $result = apply_rules($input);
226 xpath_test("<div>$result</div>", $output);
234 foreach my $item (@MyRules) {
235 if ($item ne $rule) {
241 die "Rule not found" unless $found;
246 my ($mod, $subdir) = @_;
247 $subdir .= '/' if $subdir and substr($subdir, -1) ne '/';
249 mkdir $ModuleDir unless -d $ModuleDir;
250 my $dir = `/bin/pwd`;
252 if (-l "$ModuleDir/$mod") {
254 } elsif (eval{ symlink("$dir/modules/$subdir$mod", "$ModuleDir/$mod"); 1; }) {
257 system("copy '$dir/modules/$subdir$mod' '$ModuleDir/$mod'");
259 die "Cannot symlink $mod: $!" unless -e "$ModuleDir/$mod";
260 do "$ModuleDir/$mod";
261 @MyRules = sort {$RuleOrder{$a} <=> $RuleOrder{$b}} @MyRules;
266 mkdir $ModuleDir unless -d $ModuleDir;
267 unlink("$ModuleDir/$mod") or die "Cannot unlink: $!";
272 system("/bin/rm -rf $DataDir");
274 system("c:/cygwin/bin/rm.exe -rf $DataDir");
276 die "Cannot remove $DataDir!\n" if -e $DataDir;
278 add_module('mac.pl') if $^O eq 'darwin'; # guessing HFS filesystem
279 open(F, '>:encoding(utf-8)', "$DataDir/config");
280 print F "\$AdminPass = 'foo';\n";
281 # this used to be the default in earlier CGI.pm versions
282 print F "\$ScriptName = 'http://localhost/wiki.pl';\n";
283 print F "\$SurgeProtection = 0;\n";
285 $ScriptName = 'http://localhost/test.pl'; # different!