~ubuntu-branches/ubuntu/trusty/drizzle/trusty

« back to all changes in this revision

Viewing changes to tests/randgen/lib/GenTest/App/GenTest.pm

  • Committer: Package Import Robot
  • Author(s): Clint Byrum
  • Date: 2012-06-19 10:46:49 UTC
  • mfrom: (1.1.6)
  • mto: This revision was merged to the branch mainline in revision 29.
  • Revision ID: package-import@ubuntu.com-20120619104649-e2l0ggd4oz3um0f4
Tags: upstream-7.1.36-stable
ImportĀ upstreamĀ versionĀ 7.1.36-stable

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/perl
 
2
 
 
3
# Copyright (c) 2008,2011 Oracle and/or its affiliates. All rights reserved.
 
4
# Use is subject to license terms.
 
5
#
 
6
# This program is free software; you can redistribute it and/or modify
 
7
# it under the terms of the GNU General Public License as published by
 
8
# the Free Software Foundation; version 2 of the License.
 
9
#
 
10
# This program 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.
 
14
#
 
15
# You should have received a copy of the GNU General Public License
 
16
# along with this program; if not, write to the Free Software
 
17
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
 
18
# USA
 
19
 
 
20
package GenTest::App::GenTest;
 
21
 
 
22
@ISA = qw(GenTest);
 
23
 
 
24
use strict;
 
25
use Carp;
 
26
use Data::Dumper;
 
27
use File::Basename;
 
28
 
 
29
use GenTest;
 
30
use GenTest::Properties;
 
31
use GenTest::Constants;
 
32
use GenTest::App::Gendata;
 
33
use GenTest::App::GendataSimple;
 
34
use GenTest::IPC::Channel;
 
35
use GenTest::IPC::Process;
 
36
use GenTest::ErrorFilter;
 
37
 
 
38
use POSIX;
 
39
use Time::HiRes;
 
40
 
 
41
use GenTest::XML::Report;
 
42
use GenTest::XML::Test;
 
43
use GenTest::XML::BuildInfo;
 
44
use GenTest::XML::Transporter;
 
45
use GenTest::Constants;
 
46
use GenTest::Result;
 
47
use GenTest::Validator;
 
48
use GenTest::Executor;
 
49
use GenTest::Mixer;
 
50
use GenTest::Reporter;
 
51
use GenTest::ReporterManager;
 
52
use GenTest::Filter::Regexp;
 
53
use GenTest::Incident;
 
54
 
 
55
use constant PROCESS_TYPE_PARENT        => 0;
 
56
use constant PROCESS_TYPE_PERIODIC      => 1;
 
57
use constant PROCESS_TYPE_CHILD         => 2;
 
58
 
 
59
use constant GT_CONFIG => 0;
 
60
 
 
61
sub new {
 
62
    my $class = shift;
 
63
    
 
64
    my $self = $class->SUPER::new({
 
65
        'config' => GT_CONFIG},@_);
 
66
    
 
67
    croak ("Need config") if not defined $self->config;
 
68
 
 
69
    return $self;
 
70
}
 
71
 
 
72
sub config {
 
73
    return $_[0]->[GT_CONFIG];
 
74
}
 
75
 
 
76
sub run {
 
77
    my ($self) = @_;
 
78
 
 
79
    $| = 1;
 
80
    my $ctrl_c = 0;
 
81
    
 
82
    $SIG{INT} = sub { $ctrl_c = 1 };
 
83
    $SIG{TERM} = sub { exit(0) };
 
84
    $SIG{CHLD} = "IGNORE" if osWindows();
 
85
    
 
86
    if (defined $ENV{RQG_HOME}) {
 
87
        $ENV{RQG_HOME} = osWindows() ? $ENV{RQG_HOME}.'\\' : $ENV{RQG_HOME}.'/';
 
88
    }
 
89
    
 
90
    my $seed = $self->config->seed;
 
91
    if ($seed eq 'time') {
 
92
        $seed = time();
 
93
        say("Converting --seed=time to --seed=$seed");
 
94
    }
 
95
    
 
96
    $ENV{RQG_DEBUG} = 1 if $self->config->debug;
 
97
 
 
98
    my $queries = $self->config->queries;
 
99
    $queries =~ s{K}{000}so;
 
100
    $queries =~ s{M}{000000}so;
 
101
 
 
102
    say("-------------------------------\nConfiguration");
 
103
    $self->config->printProps;
 
104
 
 
105
    if ((defined $self->config->gendata) && 
 
106
        (not defined $self->config->property('start-dirty'))) {
 
107
        foreach my $dsn (@{$self->config->dsn}) {
 
108
            next if $dsn eq '';
 
109
            my $gendata_result;
 
110
            my $datagen;
 
111
            if ($self->config->gendata eq '') {
 
112
                $datagen = GenTest::App::GendataSimple->new(dsn => $dsn,
 
113
                                                            views => $self->config->views,
 
114
                                                            engine => $self->config->engine,
 
115
                                                            sqltrace=> $self->config->sqltrace,
 
116
                                                            notnull => $self->config->notnull);
 
117
            } else {
 
118
                $datagen = GenTest::App::Gendata->new(spec_file => $self->config->gendata,
 
119
                                                      dsn => $dsn,
 
120
                                                      engine => $self->config->engine,
 
121
                                                      seed => $seed,
 
122
                                                      debug => $self->config->debug,
 
123
                                                      rows => $self->config->rows,
 
124
                                                      views => $self->config->views,
 
125
                                                      varchar_length => $self->config->property('varchar-length'),
 
126
                                                      sqltrace => $self->config->sqltrace,
 
127
                                                      notnull => $self->config->notnull);
 
128
            }
 
129
            $gendata_result = $datagen->run();
 
130
            
 
131
            return $gendata_result if $gendata_result > STATUS_OK;
 
132
        }
 
133
    }
 
134
    
 
135
    my $test_start = time();
 
136
    my $test_end = $test_start + $self->config->duration;
 
137
 
 
138
    my $generator_name = "GenTest::Generator::".$self->config->generator;
 
139
    say("Loading Generator $generator_name.");
 
140
    eval("use $generator_name");
 
141
    croak($@) if $@;
 
142
 
 
143
    my $grammar;
 
144
 
 
145
    if ($generator_name eq 'GenTest::Generator::FromGrammar') {
 
146
        $grammar = GenTest::Grammar->new(
 
147
            grammar_file => $self->config->grammar
 
148
        ) if defined $self->config->grammar;
 
149
 
 
150
        return STATUS_ENVIRONMENT_FAILURE if not defined $grammar;
 
151
 
 
152
        if (defined $self->config->redefine) {
 
153
            my $patch_grammar = GenTest::Grammar->new(
 
154
                grammar_file => $self->config->redefine);
 
155
            $grammar = $grammar->patch($patch_grammar);
 
156
        }
 
157
 
 
158
        return STATUS_ENVIRONMENT_FAILURE if not defined $grammar;
 
159
    }
 
160
    
 
161
    my $channel = GenTest::IPC::Channel->new();
 
162
    
 
163
    my @executors;
 
164
    foreach my $i (0..2) {
 
165
        next if $self->config->dsn->[$i] eq '';
 
166
        my $executor = GenTest::Executor->newFromDSN($self->config->dsn->[$i], osWindows() ? undef : $channel);
 
167
        $executor->sqltrace($self->config->sqltrace);
 
168
        $executor->setId($i+1);
 
169
        push @executors, $executor;
 
170
        if ($executor->type() == DB_MYSQL) {
 
171
            my $metadata_executor = GenTest::Executor->newFromDSN($self->config->dsn->[$i], osWindows() ? undef : $channel);
 
172
            $metadata_executor->init();
 
173
            $metadata_executor->cacheMetaData() if defined $metadata_executor->dbh();
 
174
        }
 
175
    }
 
176
    
 
177
    my $drizzle_only = $executors[0]->type == DB_DRIZZLE;
 
178
    $drizzle_only = $drizzle_only && $executors[1]->type == DB_DRIZZLE if $#executors > 0;
 
179
    
 
180
    my $mysql_only = $executors[0]->type == DB_MYSQL;
 
181
    $mysql_only = $mysql_only && $executors[1]->type == DB_MYSQL if $#executors > 0;
 
182
    
 
183
    if (not defined $self->config->reporters or $#{$self->config->reporters} < 0) {
 
184
        $self->config->reporters([]);
 
185
        if ($mysql_only || $drizzle_only) {
 
186
            $self->config->reporters(['ErrorLog', 'Backtrace']);
 
187
            push @{$self->config->reporters}, 'ValgrindXMLErrors' if (defined $self->config->property('valgrind-xml'));
 
188
            push @{$self->config->reporters}, 'ReplicationConsistency' if $self->config->rpl_mode ne '';
 
189
        }
 
190
    } else {
 
191
        ## Remove the "None" reporter
 
192
        foreach my $i (0..$#{$self->config->reporters}) {
 
193
            delete $self->config->reporters->[$i] 
 
194
                if $self->config->reporters->[$i] eq "None" 
 
195
                or $self->config->reporters->[$i] eq '';
 
196
        }
 
197
    }
 
198
    
 
199
    say("Reporters: ".($#{$self->config->reporters} > -1 ? join(', ', @{$self->config->reporters}) : "(none)"));
 
200
    
 
201
    my $reporter_manager = GenTest::ReporterManager->new();
 
202
    
 
203
    if ($mysql_only || $drizzle_only ) {
 
204
        foreach my $i (0..2) {
 
205
            next if $self->config->dsn->[$i] eq '';
 
206
            foreach my $reporter (@{$self->config->reporters}) {
 
207
                my $add_result = $reporter_manager->addReporter($reporter, {
 
208
                    dsn                 => $self->config->dsn->[$i],
 
209
                    test_start  => $test_start,
 
210
                    test_end    => $test_end,
 
211
                    test_duration       => $self->config->duration,
 
212
                    properties => $self->config
 
213
                                                                } );
 
214
                return $add_result if $add_result > STATUS_OK;
 
215
            }
 
216
        }
 
217
    }
 
218
 
 
219
    if (not defined $self->config->validators or $#{$self->config->validators} < 0) {
 
220
        $self->config->validators([]);
 
221
        push(@{$self->config->validators}, 'ErrorMessageCorruption') 
 
222
            if ($mysql_only || $drizzle_only);
 
223
        if ($self->config->dsn->[2] ne '') {
 
224
            push @{$self->config->validators}, 'ResultsetComparator3';
 
225
        } elsif ($self->config->dsn->[1] ne '') {
 
226
            push @{$self->config->validators}, 'ResultsetComparator';
 
227
        }
 
228
        
 
229
        push @{$self->config->validators}, 'ReplicationSlaveStatus' 
 
230
            if $self->config->rpl_mode ne '' && ($mysql_only || $drizzle_only);
 
231
        push @{$self->config->validators}, 'MarkErrorLog' 
 
232
            if (defined $self->config->valgrind) && ($mysql_only || $drizzle_only);
 
233
        
 
234
        push @{$self->config->validators}, 'QueryProperties' 
 
235
            if defined $grammar && $grammar->hasProperties() && ($mysql_only || $drizzle_only);
 
236
    } else {
 
237
        ## Remove the "None" validator
 
238
        foreach my $i (0..$#{$self->config->validators}) {
 
239
            delete $self->config->validators->[$i] 
 
240
                if $self->config->validators->[$i] eq "None"
 
241
                or $self->config->validators->[$i] eq '';
 
242
        }
 
243
    }
 
244
    ## Add the transformer validator if --transformers is
 
245
    ## specified
 
246
    if (defined $self->config->transformers and 
 
247
        $#{$self->config->transformers} >= 0) {
 
248
        
 
249
        push @{$self->config->validators}, 'Transformer';
 
250
    }
 
251
 
 
252
    say("Validators: ".($self->config->validators and $#{$self->config->validators} > -1 ? join(', ', @{$self->config->validators}) : "(none)"));
 
253
    
 
254
    say("Transformers: ".join(', ', @{$self->config->transformers})) 
 
255
        if $self->config->transformers and $#{$self->config->transformers} > -1;
 
256
    
 
257
    my $filter_obj;
 
258
    
 
259
    $filter_obj = GenTest::Filter::Regexp->new( file => $self->config->filter ) 
 
260
        if defined $self->config->filter;
 
261
    
 
262
    say("Starting ".$self->config->threads." processes, ".
 
263
        $self->config->queries." queries each, duration ".
 
264
        $self->config->duration." seconds.");
 
265
    
 
266
    my $buildinfo;
 
267
    if (defined $self->config->property('xml-output')) {
 
268
        $buildinfo = GenTest::XML::BuildInfo->new(
 
269
            dsns => $self->config->dsn
 
270
            );
 
271
    }
 
272
    
 
273
    # XML: 
 
274
    #  Define test suite name for reporting purposes.
 
275
    #  Until we support test suites and/or reports with multiple suites/tests,
 
276
    #  we use the test name as test suite name, from config option "testname".
 
277
    #  Default test name is the basename portion of the grammar file name.
 
278
    #  If a grammar file is not given, the default is "rqg_no_name".
 
279
    my $test_suite_name = $self->config->testname;
 
280
    if (not defined $test_suite_name) {
 
281
        if (defined $self->config->grammar) {
 
282
            $test_suite_name = basename($self->config->grammar, '.yy');
 
283
        } else {
 
284
            $test_suite_name = "rqg_no_name";
 
285
        }
 
286
    }
 
287
    
 
288
    my $test = GenTest::XML::Test->new(
 
289
        id => time(),
 
290
        name => $test_suite_name,  # NOTE: Consider changing to test (or test case) name when suites are supported.
 
291
        attributes => {
 
292
            engine => $self->config->engine,
 
293
            gendata => $self->config->gendata,
 
294
            grammar => $self->config->grammar,
 
295
            threads => $self->config->threads,
 
296
            queries => $self->config->queries,
 
297
            validators => join (',', @{$self->config->validators}),
 
298
            reporters => join (',', @{$self->config->reporters}),
 
299
            seed => $seed,
 
300
            mask => $self->config->mask,
 
301
            mask_level => $self->config->property('mask-level'),
 
302
            rows => $self->config->rows,
 
303
            'varchar-length' => $self->config->property('varchar-length')
 
304
        }
 
305
        );
 
306
    
 
307
    my $report = GenTest::XML::Report->new(
 
308
        buildinfo => $buildinfo,
 
309
        name => $test_suite_name,  # NOTE: name here refers to the name of the test suite or "test".
 
310
        tests => [ $test ]
 
311
        );
 
312
 
 
313
    ### Start central reporting thread ####
 
314
    
 
315
    my $errorfilter = GenTest::ErrorFilter->new(channel=>$channel);
 
316
    my $errorfilter_p = GenTest::IPC::Process->new(object=>$errorfilter);
 
317
    if (!osWindows()) {
 
318
        $errorfilter_p->start();
 
319
    }
 
320
    
 
321
    ### Start children ###
 
322
 
 
323
    my $process_type;
 
324
    my %child_pids;
 
325
    my $id = 1;
 
326
    
 
327
    my $periodic_pid = fork();
 
328
    if ($periodic_pid == 0) {
 
329
        Time::HiRes::sleep(($self->config->threads + 1) / 10);
 
330
        say("Started periodic reporting process...");
 
331
        $process_type = PROCESS_TYPE_PERIODIC;
 
332
        $id = 0;
 
333
    } else {
 
334
        foreach my $i (1..$self->config->threads) {
 
335
            my $child_pid = fork();
 
336
            $channel->writer;
 
337
            if ($child_pid == 0) { # This is a child 
 
338
                $process_type = PROCESS_TYPE_CHILD;
 
339
                last;
 
340
            } else {
 
341
                $child_pids{$child_pid} = 1;
 
342
                $process_type = PROCESS_TYPE_PARENT;
 
343
                $seed++;
 
344
                $id++;
 
345
                Time::HiRes::sleep(0.1);        # fork slowly for more predictability
 
346
                next;
 
347
            }
 
348
        }
 
349
    }
 
350
 
 
351
 
 
352
    ### Do the job
 
353
 
 
354
    if ($process_type == PROCESS_TYPE_PARENT) {
 
355
        
 
356
        ### Main process
 
357
        
 
358
        if (osWindows()) {
 
359
            ## Important that this is done here in the parent after the last
 
360
            ## fork since on windows Process.pm uses threads
 
361
            $errorfilter_p->start();
 
362
        }
 
363
        # We are the parent process, wait for for all spawned processes to terminate
 
364
        my $children_died = 0;
 
365
        my $total_status = STATUS_OK;
 
366
        my $periodic_died = 0;
 
367
        
 
368
        ## Parent thread does not use channel
 
369
        $channel->close;
 
370
        
 
371
        while (1) {
 
372
            my $child_pid = wait();
 
373
            my $exit_status = $? > 0 ? ($? >> 8) : 0;
 
374
 
 
375
            $total_status = $exit_status if $exit_status > $total_status;
 
376
            
 
377
            if ($child_pid == $periodic_pid) {
 
378
                $periodic_died = 1;
 
379
                last;
 
380
            } else {
 
381
                $children_died++;
 
382
                delete $child_pids{$child_pid};
 
383
            }
 
384
            
 
385
            last if $exit_status >= STATUS_CRITICAL_FAILURE;
 
386
            last if $children_died == $self->config->threads;
 
387
            last if $child_pid == -1;
 
388
        }
 
389
 
 
390
        foreach my $child_pid (keys %child_pids) {
 
391
            say("Killing child process with pid $child_pid...");
 
392
            kill(15, $child_pid);
 
393
        }
 
394
        
 
395
        if ($periodic_died == 0) {
 
396
            # Wait for periodic process to return the status of its last execution
 
397
            Time::HiRes::sleep(1);
 
398
            say("Killing periodic reporting process with pid $periodic_pid...");
 
399
            kill(15, $periodic_pid);
 
400
            
 
401
            if (osWindows()) {
 
402
                # We use sleep() + non-blocking waitpid() due to a bug in ActiveState Perl
 
403
                Time::HiRes::sleep(1);
 
404
                waitpid($periodic_pid, &POSIX::WNOHANG() );
 
405
            } else {
 
406
                waitpid($periodic_pid, 0);
 
407
            }
 
408
            
 
409
            if ($? > -1 ) {
 
410
                my $periodic_status = $? > 0 ? $? >> 8 : 0;
 
411
                $total_status = $periodic_status if $periodic_status > $total_status;
 
412
            }
 
413
        }
 
414
        
 
415
        $errorfilter_p->kill();
 
416
        
 
417
        my @report_results;
 
418
        
 
419
        if ($total_status == STATUS_OK) {
 
420
            @report_results = $reporter_manager->report(REPORTER_TYPE_SUCCESS | REPORTER_TYPE_ALWAYS);
 
421
        } elsif (
 
422
            ($total_status == STATUS_LENGTH_MISMATCH) ||
 
423
            ($total_status == STATUS_CONTENT_MISMATCH)
 
424
            ) {
 
425
            @report_results = $reporter_manager->report(REPORTER_TYPE_DATA | REPORTER_TYPE_ALWAYS);
 
426
        } elsif ($total_status == STATUS_SERVER_CRASHED) {
 
427
            say("Server crash reported, initiating post-crash analysis...");
 
428
            @report_results = $reporter_manager->report(REPORTER_TYPE_CRASH | REPORTER_TYPE_ALWAYS);
 
429
        } elsif ($total_status == STATUS_SERVER_DEADLOCKED) {
 
430
            say("Server deadlock reported, initiating analysis...");
 
431
            @report_results = $reporter_manager->report(REPORTER_TYPE_DEADLOCK | REPORTER_TYPE_ALWAYS);
 
432
        } elsif ($total_status == STATUS_SERVER_KILLED) {
 
433
            @report_results = $reporter_manager->report(REPORTER_TYPE_SERVER_KILLED | REPORTER_TYPE_ALWAYS);
 
434
        } else {
 
435
            @report_results = $reporter_manager->report(REPORTER_TYPE_ALWAYS);
 
436
        }
 
437
        
 
438
        my $report_status = shift @report_results;
 
439
        $total_status = $report_status if $report_status > $total_status;
 
440
        $total_status = STATUS_OK if $total_status == STATUS_SERVER_KILLED;
 
441
        
 
442
        foreach my $incident (@report_results) {
 
443
            $test->addIncident($incident);
 
444
        }
 
445
        
 
446
        # If no Reporters reported an incident, and we have a test failure,
 
447
        # create an incident report and add it to the test report.
 
448
        if ((scalar(@report_results) < 1) && ($total_status != STATUS_OK)) {
 
449
            my $unreported_incident = GenTest::Incident->new(
 
450
                result      => 'fail',   # can we have other results as incidents?
 
451
                description => 'Non-zero status code from RQG test run',
 
452
                signature   => 'Exit status '.$total_status # better than nothing?
 
453
            );
 
454
            # Add the incident to the test report
 
455
            $test->addIncident($unreported_incident);
 
456
        }
 
457
        
 
458
        $test->end($total_status == STATUS_OK ? "pass" : "fail");
 
459
        
 
460
        if (defined $self->config->property('xml-output')) {
 
461
            open (XML , '>'.$self->config->property('xml-output')) or carp("Unable to open ".$self->config->property('xml-output').": $!");
 
462
            print XML $report->xml();
 
463
            close XML;
 
464
            say("XML report written to ". $self->config->property('xml-output'));
 
465
        }
 
466
 
 
467
        # XML Result reporting to Test Tool (TT).
 
468
        # Currently both --xml-output=<filename> and --report-xml-tt must be
 
469
        # set to trigger this.
 
470
        if (defined $self->config->property('report-xml-tt')) {
 
471
            my $xml_transporter = GenTest::XML::Transporter->new(
 
472
                type => $self->config->property('report-xml-tt-type')
 
473
            );
 
474
            # If xml-output option is not set, bail out. TODO: Make xml-output optional.
 
475
            if (not defined $self->config->property('xml-output')) {
 
476
                carp("ERROR: --xml-output=<filename> must be set when using --report-xml-tt");
 
477
            }
 
478
            my $result = $xml_transporter->sendXML(
 
479
                $self->config->property('xml-output'),
 
480
                $self->config->property('report-xml-tt-dest')
 
481
            );
 
482
            if ($result != STATUS_OK) {
 
483
                croak("Error from XML Transporter: $result");
 
484
            }
 
485
        }
 
486
        
 
487
        if ($total_status == STATUS_OK) {
 
488
            say("Test completed successfully.");
 
489
            return STATUS_OK;
 
490
        } else {
 
491
            say("Test completed with failure status ".status2text($total_status)." ($total_status)");
 
492
            return $total_status;
 
493
        }
 
494
    } elsif ($process_type == PROCESS_TYPE_PERIODIC) {
 
495
        ## Periodic does not use channel
 
496
        $channel->close();
 
497
        my $killed = 0;
 
498
        local $SIG{TERM} = sub { $killed = 1 };
 
499
 
 
500
        while (1) {
 
501
            my $reporter_status = $reporter_manager->monitor(REPORTER_TYPE_PERIODIC);
 
502
            $self->stop_child($reporter_status) if $reporter_status > STATUS_CRITICAL_FAILURE;
 
503
            last if $killed == 1;
 
504
            sleep(10);
 
505
        }
 
506
        $self->stop_child(STATUS_OK);
 
507
    } elsif ($process_type == PROCESS_TYPE_CHILD) {
 
508
 
 
509
        # We are a child process, execute the desired queries and terminate
 
510
 
 
511
        my $generator_obj = $generator_name->new(
 
512
            grammar => $grammar,
 
513
            varchar_length => $self->config->property('varchar-length'),
 
514
            seed => $seed + $id,
 
515
            thread_id => $id,
 
516
            mask => $self->config->mask,
 
517
            mask_level => $self->config->property('mask-level')
 
518
        );
 
519
        
 
520
        $self->stop_child(STATUS_ENVIRONMENT_FAILURE) if not defined $generator_obj;
 
521
 
 
522
        my $mixer = GenTest::Mixer->new(
 
523
            generator => $generator_obj,
 
524
            executors => \@executors,
 
525
            validators => $self->config->validators,
 
526
            properties =>  $self->config,
 
527
            filters => defined $filter_obj ? [ $filter_obj ] : undef
 
528
            );
 
529
        
 
530
        $self->stop_child(STATUS_ENVIRONMENT_FAILURE) if not defined $mixer;
 
531
        
 
532
        my $max_result = 0;
 
533
        
 
534
        foreach my $i (1..$queries) {
 
535
            my $result = $mixer->next();
 
536
            if ($result > STATUS_CRITICAL_FAILURE) {
 
537
                undef $mixer;   # so that destructors are called
 
538
                $self->stop_child($result);
 
539
            }
 
540
 
 
541
            $max_result = $result if $result > $max_result && $result > STATUS_TEST_FAILURE;
 
542
            last if $result == STATUS_EOF;
 
543
            last if $ctrl_c == 1;
 
544
            last if time() > $test_end;
 
545
        }
 
546
        
 
547
        for my $ex (@executors) {
 
548
            $ex->disconnect;
 
549
            undef $ex;
 
550
        }
 
551
 
 
552
        # Forcefully deallocate the Mixer so that Validator destructors are called
 
553
        undef $mixer;
 
554
        undef $filter_obj;
 
555
        
 
556
        if ($max_result > 0) {
 
557
            say("Child process completed with error code $max_result.");
 
558
            $self->stop_child($max_result);
 
559
        } else {
 
560
            say("Child process completed successfully.");
 
561
            $self->stop_child(STATUS_OK);
 
562
        }
 
563
    } else {
 
564
        croak ("Unknown process type $process_type");
 
565
    }
 
566
 
 
567
}
 
568
 
 
569
sub stop_child {
 
570
    my ($self, $status) = @_;
 
571
 
 
572
    die "calling stop_child() without a \$status" if not defined $status;
 
573
 
 
574
    if (osWindows()) {
 
575
        exit $status;
 
576
    } else {
 
577
        safe_exit($status);
 
578
    }
 
579
}
 
580
 
 
581
1;