~titusx/nginx/module-substitutions

« back to all changes in this revision

Viewing changes to test/lib/Test/Nginx/Util.pm

  • Committer: Weibin Yao
  • Date: 2010-08-11 08:36:05 UTC
  • mfrom: (10.1.19)
  • Revision ID: git-v1:69c4c8dfe2c82aeabf8d6c5736b134c7dadaeb73
merge from the develop branch, r37


git-svn-id: http://substitutions4nginx.googlecode.com/svn/trunk@38 184bbb60-1f5e-11de-b650-e715bd6d7cf1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
package Test::Nginx::Util;
 
2
 
 
3
use strict;
 
4
use warnings;
 
5
 
 
6
our $VERSION = '0.08';
 
7
 
 
8
use base 'Exporter';
 
9
 
 
10
use POSIX qw( SIGQUIT SIGKILL SIGTERM );
 
11
use File::Spec ();
 
12
use HTTP::Response;
 
13
use Module::Install::Can;
 
14
use Cwd qw( cwd );
 
15
use List::Util qw( shuffle );
 
16
use Time::HiRes qw( sleep );
 
17
 
 
18
our $NoNginxManager = 0;
 
19
our $Profiling = 0;
 
20
 
 
21
our $RepeatEach = 1;
 
22
our $MAX_PROCESSES = 10;
 
23
 
 
24
our $NoShuffle = 0;
 
25
 
 
26
sub no_shuffle () {
 
27
    $NoShuffle = 1;
 
28
}
 
29
 
 
30
our $ForkManager;
 
31
 
 
32
if ($Profiling) {
 
33
    eval "use Parallel::ForkManager";
 
34
    if ($@) {
 
35
        die "Failed to load Parallel::ForkManager: $@\n";
 
36
    }
 
37
    $ForkManager = new Parallel::ForkManager($MAX_PROCESSES);
 
38
}
 
39
 
 
40
our $Workers                = 1;
 
41
our $WorkerConnections      = 64;
 
42
our $LogLevel               = 'debug';
 
43
our $MasterProcessEnabled   = 'on';
 
44
our $DaemonEnabled          = 'on';
 
45
our $ServerPort             = 1984;
 
46
our $ServerPortForClient    = 1984;
 
47
our $NoRootLocation = 1;
 
48
 
 
49
 
 
50
sub repeat_each (@) {
 
51
    if (@_) {
 
52
        $RepeatEach = shift;
 
53
    } else {
 
54
        return $RepeatEach;
 
55
    }
 
56
}
 
57
 
 
58
sub worker_connections (@) {
 
59
    if (@_) {
 
60
        $WorkerConnections = shift;
 
61
    } else {
 
62
        return $WorkerConnections;
 
63
    }
 
64
}
 
65
 
 
66
sub no_root_location () {
 
67
    $NoRootLocation = 1;
 
68
}
 
69
 
 
70
sub workers (@) {
 
71
    if (@_) {
 
72
        #warn "setting workers to $_[0]";
 
73
        $Workers = shift;
 
74
    } else {
 
75
        return $Workers;
 
76
    }
 
77
}
 
78
 
 
79
sub log_level (@) {
 
80
    if (@_) {
 
81
        $LogLevel = shift;
 
82
    } else {
 
83
        return $LogLevel;
 
84
    }
 
85
}
 
86
 
 
87
sub master_on () {
 
88
    $MasterProcessEnabled = 'on';
 
89
}
 
90
 
 
91
sub master_process_enabled (@) {
 
92
    if (@_) {
 
93
        $MasterProcessEnabled = shift() ? 'on' : 'off';
 
94
    } else {
 
95
        return $MasterProcessEnabled;
 
96
    }
 
97
}
 
98
 
 
99
our @EXPORT_OK = qw(
 
100
    setup_server_root
 
101
    write_config_file
 
102
    get_canon_version
 
103
    get_nginx_version
 
104
    trim
 
105
    show_all_chars
 
106
    parse_headers
 
107
    run_tests
 
108
    $ServerPortForClient
 
109
    $ServerPort
 
110
    $NginxVersion
 
111
    $PidFile
 
112
    $ServRoot
 
113
    $ConfFile
 
114
    $RunTestHelper
 
115
    $NoNginxManager
 
116
    $RepeatEach
 
117
    worker_connections
 
118
    workers
 
119
    master_on
 
120
    config_preamble
 
121
    repeat_each
 
122
    master_process_enabled
 
123
    log_level
 
124
    no_shuffle
 
125
    no_root_location
 
126
);
 
127
 
 
128
 
 
129
if ($Profiling) {
 
130
    $DaemonEnabled          = 'off';
 
131
    $MasterProcessEnabled   = 'off';
 
132
}
 
133
 
 
134
our $ConfigPreamble = '';
 
135
 
 
136
sub config_preamble ($) {
 
137
    $ConfigPreamble = shift;
 
138
}
 
139
 
 
140
our $RunTestHelper;
 
141
 
 
142
our $NginxVersion;
 
143
our $NginxRawVersion;
 
144
our $TODO;
 
145
 
 
146
#our ($PrevRequest, $PrevConfig);
 
147
 
 
148
our $ServRoot   = File::Spec->catfile(cwd(), 't/servroot');
 
149
our $LogDir     = File::Spec->catfile($ServRoot, 'logs');
 
150
our $ErrLogFile = File::Spec->catfile($LogDir, 'error.log');
 
151
our $AccLogFile = File::Spec->catfile($LogDir, 'access.log');
 
152
our $HtmlDir    = File::Spec->catfile($ServRoot, 'html');
 
153
our $ConfDir    = File::Spec->catfile($ServRoot, 'conf');
 
154
our $ConfFile   = File::Spec->catfile($ConfDir, 'nginx.conf');
 
155
our $PidFile    = File::Spec->catfile($LogDir, 'nginx.pid');
 
156
 
 
157
sub run_tests () {
 
158
    $NginxVersion = get_nginx_version();
 
159
 
 
160
    if (defined $NginxVersion) {
 
161
        #warn "[INFO] Using nginx version $NginxVersion ($NginxRawVersion)\n";
 
162
    }
 
163
 
 
164
    for my $block ($NoShuffle ? Test::Base::blocks() : shuffle Test::Base::blocks()) {
 
165
        #for (1..3) {
 
166
            run_test($block);
 
167
        #}
 
168
    }
 
169
 
 
170
    if ($Profiling) {
 
171
        $ForkManager->wait_all_children;
 
172
    }
 
173
}
 
174
 
 
175
sub setup_server_root () {
 
176
    if (-d $ServRoot) {
 
177
        #sleep 0.5;
 
178
        #die ".pid file $PidFile exists.\n";
 
179
        system("rm -rf t/servroot > /dev/null") == 0 or
 
180
            die "Can't remove t/servroot";
 
181
        #sleep 0.5;
 
182
    }
 
183
    mkdir $ServRoot or
 
184
        die "Failed to do mkdir $ServRoot\n";
 
185
    mkdir $LogDir or
 
186
        die "Failed to do mkdir $LogDir\n";
 
187
    mkdir $HtmlDir or
 
188
        die "Failed to do mkdir $HtmlDir\n";
 
189
 
 
190
    my $index_file = "$HtmlDir/index.html";
 
191
 
 
192
    open my $out, ">$index_file" or
 
193
        die "Can't open $index_file for writing: $!\n";
 
194
 
 
195
    print $out '<html><head><title>It works!</title></head><body>It works!</body></html>';
 
196
 
 
197
    close $out;
 
198
 
 
199
    mkdir $ConfDir or
 
200
        die "Failed to do mkdir $ConfDir\n";
 
201
}
 
202
 
 
203
sub write_config_file ($$) {
 
204
    my ($config, $http_config) = @_;
 
205
 
 
206
    if (!defined $config) {
 
207
        $config = '';
 
208
    }
 
209
 
 
210
    if (!defined $http_config) {
 
211
        $http_config = '';
 
212
    }
 
213
 
 
214
    open my $out, ">$ConfFile" or
 
215
        die "Can't open $ConfFile for writing: $!\n";
 
216
    print $out <<_EOC_;
 
217
worker_processes  $Workers;
 
218
daemon $DaemonEnabled;
 
219
master_process $MasterProcessEnabled;
 
220
error_log $ErrLogFile $LogLevel;
 
221
pid       $PidFile;
 
222
 
 
223
http {
 
224
    access_log $AccLogFile;
 
225
 
 
226
    default_type text/plain;
 
227
    keepalive_timeout  68;
 
228
 
 
229
$http_config
 
230
 
 
231
    server {
 
232
        listen          $ServerPort;
 
233
        #server_name     "_";
 
234
 
 
235
        client_max_body_size 30M;
 
236
        #client_body_buffer_size 4k;
 
237
 
 
238
        # Begin preamble config...
 
239
$ConfigPreamble
 
240
        # End preamble config...
 
241
 
 
242
        # Begin test case config...
 
243
$config
 
244
        # End test case config.
 
245
 
 
246
_EOC_
 
247
 
 
248
    if (! $NoRootLocation) {
 
249
        print $out <<_EOC_;
 
250
        location / {
 
251
            root $HtmlDir;
 
252
            index index.html index.htm;
 
253
        }
 
254
_EOC_
 
255
    }
 
256
 
 
257
    print $out <<_EOC_;
 
258
    }
 
259
}
 
260
 
 
261
events {
 
262
    worker_connections  $WorkerConnections;
 
263
}
 
264
 
 
265
_EOC_
 
266
    close $out;
 
267
}
 
268
 
 
269
sub get_canon_version (@) {
 
270
    sprintf "%d.%03d%03d", $_[0], $_[1], $_[2];
 
271
}
 
272
 
 
273
sub get_nginx_version () {
 
274
    my $out = `nginx -V 2>&1`;
 
275
    if (!defined $out || $? != 0) {
 
276
        warn "Failed to get the version of the Nginx in PATH.\n";
 
277
    }
 
278
    if ($out =~ m{nginx/(\d+)\.(\d+)\.(\d+)}s) {
 
279
        $NginxRawVersion = "$1.$2.$3";
 
280
        return get_canon_version($1, $2, $3);
 
281
    }
 
282
    warn "Failed to parse the output of \"nginx -V\": $out\n";
 
283
    return undef;
 
284
}
 
285
 
 
286
sub get_pid_from_pidfile ($) {
 
287
    my ($name) = @_;
 
288
    open my $in, $PidFile or
 
289
        Test::More::BAIL_OUT("$name - Failed to open the pid file $PidFile for reading: $!");
 
290
    my $pid = do { local $/; <$in> };
 
291
    #warn "Pid: $pid\n";
 
292
    close $in;
 
293
    $pid;
 
294
}
 
295
 
 
296
sub trim ($) {
 
297
    (my $s = shift) =~ s/^\s+|\s+$//g;
 
298
    $s =~ s/\n/ /gs;
 
299
    $s =~ s/\s{2,}/ /gs;
 
300
    $s;
 
301
}
 
302
 
 
303
sub show_all_chars ($) {
 
304
    my $s = shift;
 
305
    $s =~ s/\n/\\n/gs;
 
306
    $s =~ s/\r/\\r/gs;
 
307
    $s =~ s/\t/\\t/gs;
 
308
    $s;
 
309
}
 
310
 
 
311
sub parse_headers ($) {
 
312
    my $s = shift;
 
313
    my %headers;
 
314
    open my $in, '<', \$s;
 
315
    while (<$in>) {
 
316
        s/^\s+|\s+$//g;
 
317
        my ($key, $val) = split /\s*:\s*/, $_, 2;
 
318
        $headers{$key} = $val;
 
319
    }
 
320
    close $in;
 
321
    return \%headers;
 
322
}
 
323
 
 
324
sub run_test ($) {
 
325
    my $block = shift;
 
326
    my $name = $block->name;
 
327
 
 
328
    my $config = $block->config;
 
329
    if (!defined $config) {
 
330
        Test::More::BAIL_OUT("$name - No '--- config' section specified");
 
331
        #$config = $PrevConfig;
 
332
        die;
 
333
    }
 
334
 
 
335
    my $skip_nginx = $block->skip_nginx;
 
336
    my ($tests_to_skip, $should_skip, $skip_reason);
 
337
    if (defined $skip_nginx) {
 
338
        if ($skip_nginx =~ m{
 
339
                ^ \s* (\d+) \s* : \s*
 
340
                    ([<>]=?) \s* (\d+)\.(\d+)\.(\d+)
 
341
                    (?: \s* : \s* (.*) )?
 
342
                \s*$}x) {
 
343
            $tests_to_skip = $1;
 
344
            my ($op, $ver1, $ver2, $ver3) = ($2, $3, $4, $5);
 
345
            $skip_reason = $6;
 
346
            #warn "$ver1 $ver2 $ver3";
 
347
            my $ver = get_canon_version($ver1, $ver2, $ver3);
 
348
            if ((!defined $NginxVersion and $op =~ /^</)
 
349
                    or eval "$NginxVersion $op $ver")
 
350
            {
 
351
                $should_skip = 1;
 
352
            }
 
353
        } else {
 
354
            Test::More::BAIL_OUT("$name - Invalid --- skip_nginx spec: " .
 
355
                $skip_nginx);
 
356
            die;
 
357
        }
 
358
    }
 
359
    if (!defined $skip_reason) {
 
360
        $skip_reason = "various reasons";
 
361
    }
 
362
 
 
363
    my $todo_nginx = $block->todo_nginx;
 
364
    my ($should_todo, $todo_reason);
 
365
    if (defined $todo_nginx) {
 
366
        if ($todo_nginx =~ m{
 
367
                ^ \s*
 
368
                    ([<>]=?) \s* (\d+)\.(\d+)\.(\d+)
 
369
                    (?: \s* : \s* (.*) )?
 
370
                \s*$}x) {
 
371
            my ($op, $ver1, $ver2, $ver3) = ($1, $2, $3, $4);
 
372
            $todo_reason = $5;
 
373
            my $ver = get_canon_version($ver1, $ver2, $ver3);
 
374
            if ((!defined $NginxVersion and $op =~ /^</)
 
375
                    or eval "$NginxVersion $op $ver")
 
376
            {
 
377
                $should_todo = 1;
 
378
            }
 
379
        } else {
 
380
            Test::More::BAIL_OUT("$name - Invalid --- todo_nginx spec: " .
 
381
                $todo_nginx);
 
382
            die;
 
383
        }
 
384
    }
 
385
 
 
386
    if (!defined $todo_reason) {
 
387
        $todo_reason = "various reasons";
 
388
    }
 
389
 
 
390
    if (!$NoNginxManager && !$should_skip) {
 
391
        my $nginx_is_running = 1;
 
392
        if (-f $PidFile) {
 
393
            my $pid = get_pid_from_pidfile($name);
 
394
            if (!defined $pid or $pid eq '') {
 
395
                undef $nginx_is_running;
 
396
                goto start_nginx;
 
397
            }
 
398
 
 
399
            if (system("ps $pid > /dev/null") == 0) {
 
400
                #warn "found running nginx...";
 
401
                write_config_file($config, $block->http_config);
 
402
                if (kill(SIGQUIT, $pid) == 0) { # send quit signal
 
403
                    #warn("$name - Failed to send quit signal to the nginx process with PID $pid");
 
404
                }
 
405
                sleep 0.02;
 
406
                if (system("ps $pid > /dev/null") == 0) {
 
407
                    #warn "killing with force...\n";
 
408
                    kill(SIGKILL, $pid);
 
409
                    sleep 0.02;
 
410
                }
 
411
                undef $nginx_is_running;
 
412
            } else {
 
413
                unlink $PidFile or
 
414
                    die "Failed to remove pid file $PidFile\n";
 
415
                undef $nginx_is_running;
 
416
            }
 
417
        } else {
 
418
            undef $nginx_is_running;
 
419
        }
 
420
 
 
421
start_nginx:
 
422
 
 
423
        unless ($nginx_is_running) {
 
424
            #system("killall -9 nginx");
 
425
 
 
426
            #warn "*** Restarting the nginx server...\n";
 
427
            setup_server_root();
 
428
            write_config_file($config, $block->http_config);
 
429
            if ( ! Module::Install::Can->can_run('nginx') ) {
 
430
                Test::More::BAIL_OUT("$name - Cannot find the nginx executable in the PATH environment");
 
431
                die;
 
432
            }
 
433
        #if (system("nginx -p $ServRoot -c $ConfFile -t") != 0) {
 
434
        #Test::More::BAIL_OUT("$name - Invalid config file");
 
435
        #}
 
436
        #my $cmd = "nginx -p $ServRoot -c $ConfFile > /dev/null";
 
437
            my $cmd;
 
438
            if ($NginxVersion >= 0.007053) {
 
439
                $cmd = "nginx -p $ServRoot/ -c $ConfFile > /dev/null";
 
440
            } else {
 
441
                $cmd = "nginx -c $ConfFile > /dev/null";
 
442
            }
 
443
 
 
444
            if ($Profiling) {
 
445
                my $pid = $ForkManager->start;
 
446
                if (!$pid) {
 
447
                    # child process
 
448
                    if (system($cmd) != 0) {
 
449
                        Test::More::BAIL_OUT("$name - Cannot start nginx using command \"$cmd\".");
 
450
                    }
 
451
 
 
452
                    $ForkManager->finish; # terminate the child process
 
453
                }
 
454
            } else {
 
455
                if (system($cmd) != 0) {
 
456
                    Test::More::BAIL_OUT("$name - Cannot start nginx using command \"$cmd\".");
 
457
                }
 
458
            }
 
459
 
 
460
            sleep 0.1;
 
461
        }
 
462
    }
 
463
 
 
464
    if ($block->init) {
 
465
        eval $block->init;
 
466
        if ($@) {
 
467
            Test::More::BAIL_OUT("$name - init failed: $@");
 
468
        }
 
469
    }
 
470
 
 
471
    my $i = 0;
 
472
    while ($i++ < $RepeatEach) {
 
473
        if ($should_skip) {
 
474
            SKIP: {
 
475
                Test::More::skip("$name - $skip_reason", $tests_to_skip);
 
476
 
 
477
                $RunTestHelper->($block);
 
478
            }
 
479
        } elsif ($should_todo) {
 
480
            TODO: {
 
481
                local $TODO = "$name - $todo_reason";
 
482
 
 
483
                $RunTestHelper->($block);
 
484
            }
 
485
        } else {
 
486
            $RunTestHelper->($block);
 
487
        }
 
488
    }
 
489
 
 
490
    if (defined $block->quit && $Profiling) {
 
491
        warn "Found quit...";
 
492
        if (-f $PidFile) {
 
493
            my $pid = get_pid_from_pidfile($name);
 
494
            if (system("ps $pid > /dev/null") == 0) {
 
495
                write_config_file($config, $block->http_config);
 
496
                if (kill(SIGQUIT, $pid) == 0) { # send quit signal
 
497
                    #warn("$name - Failed to send quit signal to the nginx process with PID $pid");
 
498
                }
 
499
                sleep 0.02;
 
500
                if (system("ps $pid > /dev/null") == 0) {
 
501
                    #warn "killing with force...\n";
 
502
                    kill(SIGKILL, $pid);
 
503
                    sleep 0.02;
 
504
                }
 
505
            } else {
 
506
                unlink $PidFile or
 
507
                    die "Failed to remove pid file $PidFile\n";
 
508
            }
 
509
        }
 
510
    }
 
511
}
 
512
 
 
513
1;