#!/usr/bin/env perl
# Tests the C depender and its handling of dependency cache files.
package t::Correctness::CDepender;
use strict;
use warnings;
use base qw(Test::Class);
use File::Copy::Recursive qw(rcopy);
use Test::More;
use t::PBS;
my $t;
sub setup : Test(setup) {
$t = t::PBS->new(string => 'C depender');
$t->setup_test_data('c_depender');
$t->build_dir('build_dir');
$t->target('test-c.exe');
$t->write('post_pbs.pl', <<'_EOF_');
for my $node ( @{$dependency_tree->{__BUILD_SEQUENCE}}) {
print "Rebuild node $node->{__NAME}\n";
}
1;
_EOF_
$t->command_line_flags('--post_pbs=post_pbs.pl -dsi -dcdi -ndpb -no_colorization');
}
sub copy_from_pbsfiles_dir {
my ($src,
$dst) = @_;
$t->setup_test_data_file('c_depender', $src, $dst);
}
sub change_include_file : Test(8) {
# Build
$t->build_test;
$t->run_target_test(stdout => "ab");
$t->test_up_to_date;
# Modify the first include file and rebuild
copy_from_pbsfiles_dir('a2.h', 'a.h');
$t->build_test;
$t->run_target_test(stdout => "a2b");
$t->test_up_to_date;
}
sub abort_between_build_of_c_and_o_file : Test(4) {
copy_from_pbsfiles_dir('Pbsfile_abort.pl', 'Pbsfile.pl');
copy_from_pbsfiles_dir('3.c', '1.c');
$t->build_test;
# Modify the include file and turn on the aborting
# of the build of the .o-file.
copy_from_pbsfiles_dir('a2.h', 'a.h');
$ENV{'PBS_TEST_ABORT'} = '1';
# Rebuild.
$t->build_test_fail;
# Turn off the aborting of the build of the .o-file.
$ENV{'PBS_TEST_ABORT'} = '';
# Rebuild.
$t->build_test;
# Test that the rebuild was correct.
$t->run_target_test(stdout => "a2");
}
sub use_dependency_cache : Test(4) {
# Build
$t->build_test;
$t->run_target_test(stdout => "ab");
# Modify Pbsfile.
copy_from_pbsfiles_dir('Pbsfile2.pl', 'Pbsfile.pl');
# Rebuild.
#
# The C depend caches are up-to-date and will be used.
$t->build_test;
my $stdout = $t->stdout;
unlike($stdout, qr|\QC_depender:|, 'The dependency caches are used');
}
sub use_unsynchronized_cache : Test(10) {
# Build
$t->build_test;
# Introduce an error in the first C file and modify the second
# include file.
copy_from_pbsfiles_dir('1_error.c', '1.c');
copy_from_pbsfiles_dir('b2.h', 'b.h');
# Rebuild.
#
# The second C file will be redepended, but the C depend cache
# of the second C file will not be synchronized, because the
# build will already fail with the compilation of the first C
# file, and that is before the C depend cache of the second C
# file is going to be synchronized.
$t->build_test_fail;
my $stdout = $t->stdout;
unlike($stdout, qr|\QSynchronized C cache file for './1.c'|,
'Did not synchronize C cache for first C file');
like($stdout, qr|(?x)\QC_depender: '\E[^']*\Q2.c' [MD5 difference]\E\s*
\Q[\E[^\]]*\Qb.h].|, 'Second C file was redepended');
unlike($stdout, qr|\QSynchronized C cache file for './2.c'|,
'Did not synchronize C cache for second C file');
# Fix the error in the first C file.
copy_from_pbsfiles_dir('1_2.c', '1.c');
# Rebuild.
#
# The C depender will try to redepend the second C file, but it
# will find the unsynchronized cache, verify it, and use it.
$t->build_test;
$stdout = $t->stdout;
like($stdout, qr|\QSynchronized C cache file for './1.c'|,
'Synchronized C cache for first C file');
like($stdout, qr|(?x)\QC_depender: '\E[^']*\Q2.c' [MD5 difference]\E\s*
\Q[\E[^\]]*\Qb.h].\E\s*
\QVerifying unsynchronized cache ... Valid.|,
'Found unsynchronized valid cache for second C file');
like($stdout, qr|\QSynchronized C cache file for './2.c'|,
'Synchronized C cache for second C file');
$t->run_target_test(stdout => "ab2");
}
sub do_not_use_unsynchronized_cache : Test(9) {
# Build
$t->build_test;
# Introduce an error in the first C file and modify the second
# C file to include another include file.
copy_from_pbsfiles_dir('1_error.c', '1.c');
copy_from_pbsfiles_dir('2_2.c', '2.c');
# Rebuild.
#
# The second C file will be redepended, but the C depend cache
# of the second C file will not be synchronized, because the
# build will already fail with the compilation of the first C
# file, and that is before the C depend cache of the second C
# file is going to be synchronized.
$t->build_test_fail;
my $stdout = $t->stdout;
unlike($stdout, qr|\QSynchronized C cache file for './1.c'|,
'Did not synchronize C cache for first C file');
like($stdout, qr|(?x)\QC_depender: '\E[^']*\Q2.c' [difference]:\E\s*
\Q[__VARIABLE:C_FILE].|, 'Second C file was redepended');
unlike($stdout, qr|\QSynchronized C cache file for './2.c'|,
'Did not synchronize C cache for second C file');
# Fix the error in the first C file and modify the second C
# file again, now to include a third include file.
copy_from_pbsfiles_dir('1.c');
copy_from_pbsfiles_dir('2_3.c', '2.c');
# Rebuild.
#
# The C depender will try to redepend the second C file, but it
# will find the unsynchronized cache, verify it, and find
# that it is invalid (because we modified the second
# include file. So, it will still redepend the second C file.
$t->build_test;
$t->run_target_test(stdout => "ab3");
# Change the third include file.
copy_from_pbsfiles_dir('b.h', 'b3.h');
# Rebuild.
#
# Now, if the invalid unsynchronized cache would have been used,
# the C depend cache would have become wrong by not including
# the third include file. Then, PBS would erroneously think
# everything was up-to-date.
$t->build_test;
$t->run_target_test(stdout => "ab");
}
unless (caller()) {
$ENV{"TEST_VERBOSE"} = 1;
Test::Class->runtests;
}
1;