Subversion Repositories DevTools

Rev

Rev 4762 | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 4762 Rev 4768
Line 20... Line 20...
20
use warnings;
20
use warnings;
21
 
21
 
22
use Pod::Usage;
22
use Pod::Usage;
23
use Getopt::Long;
23
use Getopt::Long;
24
use Time::HiRes;
24
use Time::HiRes;
-
 
25
use File::Find;
25
use File::Spec;
26
use File::Spec;
26
use XML::Simple;
27
use XML::Simple;
27
use Cwd;
28
use Cwd;
28
 
29
 
29
use JatsError;
30
use JatsError;
Line 48... Line 49...
48
#               violated and all lines are unindented by one
49
#               violated and all lines are unindented by one
49
#               indentation level
50
#               indentation level
50
#
51
#
51
#               Processes any result files from Junit as run by ANT.
52
#               Processes any result files from Junit as run by ANT.
52
#
53
#
53
#               These result files are named 'Test-<name>.xml'.
54
#               These result files are named 'Test-<name>.xml' or 'TEST-<name>.xml.
54
#
55
#
55
#               Parse these files and extract the pass/fail information as
56
#               Parse these files and extract the pass/fail information as
56
#               well as details for failed tests. This information is written,
57
#               well as details for failed tests. This information is written,
57
#               as XML, to the output folder specified with the -o switch. The
58
#               as XML, to the output folder specified with the -o switch. The
58
#               files are named <Target>-<Time>.xml, where <Target> is
59
#               files are named <Target>-<Time>.xml, where <Target> is
Line 123... Line 124...
123
#   Set defaults and error checking
124
#   Set defaults and error checking
124
$opt_outFolder = '.' unless ($opt_outFolder);
125
$opt_outFolder = '.' unless ($opt_outFolder);
125
Error ("Must specify a target platform (e.g. -t 'WIN32')") unless($opt_target);
126
Error ("Must specify a target platform (e.g. -t 'WIN32')") unless($opt_target);
126
Error ("Output Folder \'$opt_outFolder\' does not exist") unless (-d $opt_outFolder);
127
Error ("Output Folder \'$opt_outFolder\' does not exist") unless (-d $opt_outFolder);
127
 
128
 
128
return doMain($opt_outFolder, $opt_target);
129
return doMain($opt_verbose, $opt_outFolder, $opt_target);
129
 
130
 
130
#   End of broken indentation convention
131
#   End of broken indentation convention
131
}
132
}
132
 
133
 
133
#------------------------------------------------------------------------------
134
#------------------------------------------------------------------------------
Line 144... Line 145...
144
#
145
#
145
# Returns     : 0 - some tests failed
146
# Returns     : 0 - some tests failed
146
#               1 - all tests passed
147
#               1 - all tests passed
147
#
148
#
148
sub doMain {
149
sub doMain {
149
    my ($outFolder, $target) = @_;
150
    my ($verbose, $outFolder, $target) = @_;
-
 
151
 
-
 
152
    # derive filename here to prevent File::Find from messing with it
-
 
153
    my $outfile = outputFilename($verbose, $outFolder, $target);
-
 
154
 
150
    my ($passed, @instance) = createBuildInstance($target);
155
    my ($passed, @instance) = createBuildInstance($verbose, $target);
151
    outputJatsXmlFile($outFolder, $target, @instance);
156
    outputJatsXmlFile($verbose, $outfile, @instance);
152
    return $passed;
157
    return $passed;
153
}
158
}
154
 
159
 
155
#------------------------------------------------------------------------------
160
#------------------------------------------------------------------------------
156
# Function    : outputJatsXmlFile
161
# Function    : outputJatsXmlFile
Line 165... Line 170...
165
# Output      : The result XML file.
170
# Output      : The result XML file.
166
#
171
#
167
# Returns     : The filename of the output file.
172
# Returns     : The filename of the output file.
168
#
173
#
169
sub outputJatsXmlFile {
174
sub outputJatsXmlFile {
170
    my ($output_folder, $target, @instance) = @_;
175
    my ($verbose, $filename, @instance) = @_;
171
    my %xml;
176
    my %xml;
172
 
177
 
173
    #   Each element in @instance is put into a <TestResult> XML element
178
    #   Each element in @instance is put into a <TestResult> XML element
174
    @{$xml{TestResult}} = @instance;
179
    @{$xml{TestResult}} = @instance;
175
    #   The 'MESSAGE' key for failed tests forms the content of the
180
    #   The 'MESSAGE' key for failed tests forms the content of the
176
    #   <TestResult> element. Other keys are converted to attributes.
181
    #   <TestResult> element. Other keys are converted to attributes.
177
    #   Assign <TestResults> as the root XMl node.
182
    #   Assign <TestResults> as the root XMl node.
178
    my $xmlData = XMLout(\%xml, ContentKey => 'MESSAGE', RootName => 'TestResults');
183
    my $xmlData = XMLout(\%xml, ContentKey => 'MESSAGE', RootName => 'TestResults');
179
 
184
 
180
    #   Construct the output filename from the microsecond time.
-
 
181
    my $time = Time::HiRes::time;
-
 
182
    $time =~ s/\.//;
-
 
183
    #   Append enough '0' to make 15 chars. This make uniform length numbers
-
 
184
    #   and allows filename sorting.
-
 
185
    $time .= "0"x(15-length($time));
-
 
186
    my $filename = "$output_folder/$target-$time.xml";
-
 
187
    Error("$filename already exists: $!\n") if -e $filename;
-
 
188
 
-
 
189
    #   Write the data to the XML file.
185
    #   Write the data to the XML file.
190
    open ( my $outFile, ">", $filename)
186
    open ( my $outFile, ">", $filename)
191
        || Error("Cannot open $filename for writing: $!\n");
187
        || Error(" Cannot open results file:$filename for writing: $!\n");
192
    print $outFile $xmlData;
188
    print $outFile $xmlData;
193
    return $filename;
189
    return $filename;
194
}
190
}
195
 
191
 
196
#------------------------------------------------------------------------------
192
#------------------------------------------------------------------------------
Line 218... Line 214...
218
#                   TARGET_PLATFORM - The platform that was used to run the test
214
#                   TARGET_PLATFORM - The platform that was used to run the test
219
#                   MESSAGE - if the test did not pass, there is more
215
#                   MESSAGE - if the test did not pass, there is more
220
#                             information here. Most likely a stack dump.
216
#                             information here. Most likely a stack dump.
221
#
217
#
222
sub createBuildInstance {
218
sub createBuildInstance {
223
    my ($target) = @_;
219
    my ($verbose, $target) = @_;
224
    Error("Must provide a target") unless defined($target);
220
    Error("Must provide a target") unless defined($target);
225
 
221
 
226
    my $filename = findAntResultsFile();
222
    my $filename = findAntResultsFile($verbose);
227
    my ($passed, @test_results) = parseTestRun($filename, $target);
223
    my ($passed, @test_results) = parseTestRun($verbose, $filename, $target);
228
 
224
 
229
    return ($passed, @test_results);
225
    return ($passed, @test_results);
230
}
226
}
231
 
227
 
232
#------------------------------------------------------------------------------
228
#------------------------------------------------------------------------------
-
 
229
# Function    : outputFilename
-
 
230
#
-
 
231
# Description : derive the absolute path of the reports file to write
-
 
232
#
-
 
233
# Inputs      : $output_folder - where to put the output file.
-
 
234
#               $target - the target platforn, used to construct the output
-
 
235
#                         filename.
-
 
236
#
-
 
237
# Output      : none
-
 
238
#
-
 
239
# Returns     : The filename of the output file.
-
 
240
#
-
 
241
sub outputFilename {
-
 
242
    my ($verbose, $output_folder, $target) = @_;
-
 
243
 
-
 
244
    #   Construct the output filename from the microsecond time.
-
 
245
    my $time = Time::HiRes::time;
-
 
246
    $time =~ s/\.//;
-
 
247
    #   Append enough '0' to make 15 chars. This make uniform length numbers
-
 
248
    #   and allows filename sorting.
-
 
249
    $time .= "0"x(15-length($time));
-
 
250
    my $filename = File::Spec->rel2abs("$output_folder/$target-$time.xml");
-
 
251
 
-
 
252
    Error("Output file:$filename already exists: $!\n") if -e $filename;
-
 
253
    Information("Writing output to $filename\n") if ($verbose gt 0);
-
 
254
 
-
 
255
    return $filename;
-
 
256
}
-
 
257
 
-
 
258
#------------------------------------------------------------------------------
233
# Function    : findAntResultsFile
259
# Function    : findAntResultsFile
234
#
260
#
235
# Description : Find a file matching the pattern '^Test-.*\.xml' below the
261
# Description : Find a file matching the pattern '^Test-.*\.xml' below the
236
#               current folder.
262
#               current folder.
237
#
263
#
Line 240... Line 266...
240
# Output      : none
266
# Output      : none
241
#
267
#
242
# Returns     : The complete path and filename of the first matching file.
268
# Returns     : The complete path and filename of the first matching file.
243
#
269
#
244
sub findAntResultsFile {
270
sub findAntResultsFile {
-
 
271
    my ($verbose) = @_;
245
    my $testResultsFile;
272
    my $testResultsFile;
246
    use File::Find;
-
 
247
 
273
 
248
    find(sub {
274
    find(sub {
249
            if (/^Test-.*\.xml/) {
275
            if (/^test-.*\.xml/i) {
250
                #   Get absolute path
276
                #   Get absolute path
251
                $testResultsFile = File::Spec->rel2abs("$_");
277
                $testResultsFile = File::Spec->rel2abs("$_");
252
                #   Exit once we've found one file (speed optimisation)
278
                #   Exit once we've found one file (speed optimisation)
253
                goto JATS_UTF_ANT_FOUND;
279
                goto JATS_UTF_ANT_FOUND;
254
            }
280
            }
255
        }, '.');
281
        }, '.');
256
JATS_UTF_ANT_FOUND:  # goto here from inside Find once the file is found
282
JATS_UTF_ANT_FOUND:  # goto here from inside Find once the file is found
257
 
283
 
-
 
284
    Error("Could not find an Ant Results file.\n".
-
 
285
          "Check that is has a filename of 'TEST-*.xml'")
-
 
286
        unless defined($testResultsFile);
-
 
287
 
-
 
288
    Information("Processing Ant Results file: $testResultsFile\n")
-
 
289
        if ($verbose gt 0);
-
 
290
 
258
    return $testResultsFile;
291
    return $testResultsFile;
259
}
292
}
260
 
293
 
261
#------------------------------------------------------------------------------
294
#------------------------------------------------------------------------------
262
# Function    : parseTestRun
295
# Function    : parseTestRun
Line 272... Line 305...
272
#               $passed - 1 if all tests passed
305
#               $passed - 1 if all tests passed
273
#                         0 if some tests failed
306
#                         0 if some tests failed
274
#               @test_results - A list of TestResult's (see above)
307
#               @test_results - A list of TestResult's (see above)
275
#          
308
#          
276
sub parseTestRun {
309
sub parseTestRun {
277
    my ($filename, $target) = @_;
310
    my ($verbose, $filename, $target) = @_;
278
    my ($passed, @test_results);
311
    my ($passed, @test_results);
279
    my ($project_name, $package_name, $package_version, $timestamp);
312
    my ($project_name, $package_name, $package_version, $timestamp);
280
    $passed = 1;
313
    $passed = 1;
281
 
314
 
282
    open( my $infile, "<$filename" ) || Error ( "Cannot read from $filename", $! );
315
    open( my $infile, "<$filename" ) || Error ( "Cannot read from $filename", $! );
Line 285... Line 318...
285
 
318
 
286
        #   Extract one test case
319
        #   Extract one test case
287
        #
320
        #
288
        #   This may progress the file pointer if <testcase>...</testcase>
321
        #   This may progress the file pointer if <testcase>...</testcase>
289
        #   is multiline
322
        #   is multiline
290
        my @test_case = getTestCase($_, $infile) if /\<testcase/;
323
        my @test_case = getTestCase($verbose, $_, $infile) if /\<testcase/;
291
 
324
 
292
        #   Parse the test case creating a hash
325
        #   Parse the test case creating a hash
293
        my %test_run = parseTestCase($target, @test_case) if (@test_case);
326
        my %test_run = parseTestCase($verbose, $target, @test_case) if (@test_case);
294
 
327
 
295
        #   Save the test result in the array
328
        #   Save the test result in the array
296
        push(@test_results, {%test_run}) if (%test_run);
329
        push(@test_results, {%test_run}) if (%test_run);
297
 
330
 
298
        #   Record that there was at least one failed test
331
        #   Record that there was at least one failed test
Line 300... Line 333...
300
    }
333
    }
301
    return ($passed, @test_results);
334
    return ($passed, @test_results);
302
}
335
}
303
 
336
 
304
#------------------------------------------------------------------------------
337
#------------------------------------------------------------------------------
-
 
338
# Function    : containsClosingTag
-
 
339
#
-
 
340
# Description : Handles the determination of checking for the closing
-
 
341
#               '</testcase>' or '/>' tag.
-
 
342
#
-
 
343
# Inputs      : $line - the current line in the results.xml file.
-
 
344
#
-
 
345
# Output      : none
-
 
346
#
-
 
347
# Returns     : 1 - closing tag found
-
 
348
#               0 - o closing tag found
-
 
349
#
-
 
350
sub containsClosingTag {
-
 
351
    my ($verbose, $line) = @_;
-
 
352
 
-
 
353
    return 1 if ($line =~ /\<\/testcase\>/);
-
 
354
    return 1 if ($line =~ /\/\>$/);
-
 
355
 
-
 
356
    return 0;
-
 
357
}
-
 
358
 
-
 
359
#------------------------------------------------------------------------------
305
# Function    : getTestCase
360
# Function    : getTestCase
306
#
361
#
307
# Description : Reads from the file, and advances the file pointer, until the
362
# Description : Reads from the file, and advances the file pointer, until the
308
#               closing '</testcase>' tag is read.
363
#               closing '</testcase>' or '/>' tag is read.
309
#
364
#
310
# Inputs      : $line - the current line in the results.xml file. This line
365
# Inputs      : $line - the current line in the results.xml file. This line
311
#                       will contain '<testcase'.
366
#                       will contain '<testcase'.
312
#               $file - the file handle of the results.xml file.
367
#               $file - the file handle of the results.xml file.
313
#
368
#
Line 315... Line 370...
315
#
370
#
316
# Returns     : A string array of all lines read, including the start and end
371
# Returns     : A string array of all lines read, including the start and end
317
#               'testcase' tag.
372
#               'testcase' tag.
318
#
373
#
319
sub getTestCase {
374
sub getTestCase {
320
    my ($line, $file) = @_;
375
    my ($verbose, $line, $file) = @_;
321
    my (@result);
376
    my (@result);
322
 
377
 
323
    #   Save the first line, containing the opening <testcase> tag
378
    #   Save the first line, containing the opening <testcase> tag
324
    push(@result, $line);
379
    push(@result, $line);
325
    
380
    
326
    #   No more to do if it's all on one line
381
    #   No more to do if it's all on one line
327
    return @result if ($line =~/\<\/testcase\>/);
382
    return @result if containsClosingTag($verbose, $line);
328
 
383
 
329
    #   Save subsequent lines up to and including the closing </testcase> tag
384
    #   Save subsequent lines up to and including the closing </testcase> tag
330
    while (<$file>)
385
    while (<$file>)
331
    {
386
    {
332
        push (@result, $_);
387
        push (@result, $_);
333
        last if /\<\/testcase\>/;
388
        last if /\<\/testcase\>/;
-
 
389
        # don't check for '/>' here as we're multi-line.
334
    }
390
    }
335
 
391
 
336
    return @result;
392
    return @result;
337
}
393
}
338
 
394
 
Line 350... Line 406...
350
#               $name - The test name, concatenated from the 'classname' and
406
#               $name - The test name, concatenated from the 'classname' and
351
#                       'name' attributes.
407
#                       'name' attributes.
352
#               $duration - The test duration, in seconds, from the 'time'
408
#               $duration - The test duration, in seconds, from the 'time'
353
#                           attribute.
409
#                           attribute.
354
#               $outcome - The test outcome (= 'PASS') if we know it (i.e. the
410
#               $outcome - The test outcome (= 'PASS') if we know it (i.e. the
355
#                          closing </testcase> tag is on the same line).
411
#                          closing '</testcase>' or '/>' tag is on the same
-
 
412
#                          line).
356
#                          Otherwise, if we don't know it, return undef.
413
#                          Otherwise, if we don't know it, return undef.
357
#
414
#
358
sub getDetails {
415
sub getDetails {
359
    my ($line) = shift; 
416
    my ($verbose, $line) = @_;
360
 
417
 
361
    #   Pattern to extract a thing between two quotes (' or ").
418
    #   Pattern to extract a thing between two quotes (' or ").
362
    my ($xml_value) = qr/["\']([^"\']*)["\']/;
419
    my ($xml_value) = qr/["\']([^"\']*)["\']/;
363
 
420
 
364
    my ($name, $duration, $outcome);
421
    my ($name, $duration, $outcome);
365
 
422
 
366
    if ($line =~ /\sclassname=${xml_value}\s*name=${xml_value}\s*time=${xml_value}/) {
423
    if ($line =~ /\sclassname=${xml_value}\s*name=${xml_value}\s*time=${xml_value}/) {
367
        $name = $1.'::'.$2;
424
        $name = $1.'::'.$2;
368
        $duration = $3;
425
        $duration = $3;
369
        $outcome = 'PASS' if $line =~ /\<\/testcase\>/;
426
        $outcome = 'PASS' if containsClosingTag($verbose, $line);
370
    }
427
    }
371
 
428
 
372
    return ($name, $duration, $outcome);
429
    return ($name, $duration, $outcome);
373
}
430
}
374
 
431
 
375
#------------------------------------------------------------------------------
432
#------------------------------------------------------------------------------
376
# Function    : parseMessage
433
# Function    : parseMessage
377
#
434
#
378
# Description :
435
# Description : parse the given element tag, and return its contents.
379
#
436
#
380
# Inputs      : $pattern - The XML element name from which to extract the
437
# Inputs      : $pattern - The XML element name from which to extract the
381
#                          message.
438
#                          message.
382
#               $line - The line with the open tag. E.g.
439
#               $line - The line with the open tag. E.g.
383
#                          <error ...>
440
#                          <error ...>
Line 389... Line 446...
389
# Output      : none
446
# Output      : none
390
#
447
#
391
# Returns     : The value of the matched element.
448
# Returns     : The value of the matched element.
392
#
449
#
393
sub parseMessage {
450
sub parseMessage {
394
    my ($pattern, $line, @lines) = @_;
451
    my ($verbose, $pattern, $line, @lines) = @_;
395
    my ($message);
452
    my ($message);
396
 
453
 
397
    if ($line =~ /\<${pattern} /) {
454
    if ($line =~ /\<${pattern} /) {
398
        my $temp_message = $line;
455
        my $temp_message = $line;
399
 
456
 
Line 428... Line 485...
428
#                 NAME - the test method name.
485
#                 NAME - the test method name.
429
#                 DURATION - the test duration, in seconds.
486
#                 DURATION - the test duration, in seconds.
430
#                 OUTCOME - one of 'PASS', 'FAILED', 'ERROR'
487
#                 OUTCOME - one of 'PASS', 'FAILED', 'ERROR'
431
#
488
#
432
sub parseTestCase {
489
sub parseTestCase {
-
 
490
    # using shift, since we're shifting in the while loop later too.
-
 
491
    my $verbose = shift;
433
    my $testTarget = shift;
492
    my $testTarget = shift;
-
 
493
 
434
    my %testRun;
494
    my %testRun;
435
    $testRun{TARGET_PLATFORM} = $testTarget;
495
    $testRun{TARGET_PLATFORM} = $testTarget;
436
    while (my $line = shift @_) {
496
    while (my $line = shift @_) {
437
        my ($name, $duration, $outcome, $message);
497
        my ($name, $duration, $outcome, $message);
438
        ($name, $duration, $outcome) = getDetails($line);
498
        ($name, $duration, $outcome) = getDetails($verbose, $line);
439
        if (defined($name) && defined($duration)) {
499
        if (defined($name) && defined($duration)) {
440
            $testRun{NAME} = $name;
500
            $testRun{NAME} = $name;
441
            $testRun{DURATION} = $duration;
501
            $testRun{DURATION} = $duration;
442
            $testRun{OUTCOME} = $outcome if (defined($outcome));
502
            $testRun{OUTCOME} = $outcome if (defined($outcome));
443
            next;
503
            next;
444
        }
504
        }
445
        last if $line =~ /\<\/testcase\>/;
505
        last if containsClosingTag($verbose, $line);
446
        ($message) = parseMessage(qr/error/  , $line, @_);
506
        ($message) = parseMessage($verbose, qr/error/  , $line, @_);
447
        if (defined($message)) {
507
        if (defined($message)) {
448
            $testRun{OUTCOME} = 'ERROR';
508
            $testRun{OUTCOME} = 'ERROR';
449
            $testRun{MESSAGE} = $message;
509
            $testRun{MESSAGE} = $message;
450
            next;
510
            next;
451
        }
511
        }
452
        ($message) = parseMessage(qr/failure/, $line, @_);
512
        ($message) = parseMessage($verbose, qr/failure/, $line, @_);
453
        if (defined($message)) {
513
        if (defined($message)) {
454
            $testRun{OUTCOME} = 'FAILURE';
514
            $testRun{OUTCOME} = 'FAILURE';
455
            $testRun{MESSAGE} = $message;
515
            $testRun{MESSAGE} = $message;
456
            next;
516
            next;
457
        }
517
        }