The Perl Toolchain Summit 2025 Needs You: You can help 🙏 Learn more

#!./perl -w
# Verify that all files generated by perl scripts are up to date.
BEGIN {
if (-f "./TestInit.pm") {
push @INC, ".";
} elsif (-f '../TestInit.pm') {
push @INC, "..";
}
}
use TestInit qw(T A); # T is chdir to the top level, A makes paths absolute
use strict;
# this tests the functions in HeaderParser.pm which we use for make regen.
require './t/test.pl';
require './regen/HeaderParser.pm';
skip_all_if_miniperl("needs Data::Dumper");
require Data::Dumper;
sub show_text {
my ($as_text)= @_;
print STDERR $as_text=~s/^/" " x 8/mger;
}
my $hp= HeaderParser->new();
$hp->parse_text(<<~'EOF');
#ifdef A
#ifdef B
#define AB
content 1
#endif
content 2
#define A
#endif
/*comment
line */
#define C /* this is
a hidden line continuation */ D
EOF
my $normal= $hp->lines_as_str();
my $lines= $hp->lines();
my $lines_as_str= Data::Dumper->new([$lines])->Sortkeys(1)->Useqq(1)->Indent(1)->Dump();
is($lines_as_str,<<~'DUMP_EOF', "Simple data structure as expected") or show_text($lines_as_str);
$VAR1 = [
bless( {
"cond" => [
[
"defined(A)"
]
],
"flat" => "#if defined(A)",
"level" => 0,
"line" => "#if defined(A)\n",
"n_lines" => 1,
"raw" => "#ifdef A\n",
"source" => "(buffer)",
"start_line_num" => 1,
"sub_type" => "#if",
"type" => "cond"
}, 'HeaderLine' ),
bless( {
"cond" => [
[
"defined(A)"
],
[
"defined(B)"
]
],
"flat" => "#if defined(B)",
"level" => 1,
"line" => "# if defined(B)\n",
"n_lines" => 1,
"raw" => "#ifdef B\n",
"source" => "(buffer)",
"start_line_num" => 2,
"sub_type" => "#if",
"type" => "cond"
}, 'HeaderLine' ),
bless( {
"cond" => [
[
"defined(A)"
],
[
"defined(B)"
]
],
"flat" => "#define AB",
"level" => 2,
"line" => "# define AB\n",
"n_lines" => 1,
"raw" => "#define AB\n",
"source" => "(buffer)",
"start_line_num" => 3,
"sub_type" => "#define",
"type" => "content"
}, 'HeaderLine' ),
bless( {
"cond" => [
[
"defined(A)"
],
[
"defined(B)"
]
],
"flat" => "content 1",
"level" => 2,
"line" => "content 1\n",
"n_lines" => 1,
"raw" => "content 1\n",
"source" => "(buffer)",
"start_line_num" => 4,
"sub_type" => "text",
"type" => "content"
}, 'HeaderLine' ),
bless( {
"cond" => [
[
"defined(A)"
],
[
"defined(B)"
]
],
"flat" => "#endif",
"inner_lines" => 3,
"level" => 1,
"line" => "# endif\n",
"n_lines" => 1,
"raw" => "#endif\n",
"source" => "(buffer)",
"start_line_num" => 5,
"sub_type" => "#endif",
"type" => "cond"
}, 'HeaderLine' ),
bless( {
"cond" => [
[
"defined(A)"
]
],
"flat" => "content 2",
"level" => 1,
"line" => "content 2\n",
"n_lines" => 1,
"raw" => "content 2\n",
"source" => "(buffer)",
"start_line_num" => 6,
"sub_type" => "text",
"type" => "content"
}, 'HeaderLine' ),
bless( {
"cond" => [
[
"defined(A)"
]
],
"flat" => "#define A",
"level" => 1,
"line" => "# define A\n",
"n_lines" => 1,
"raw" => "#define A\n",
"source" => "(buffer)",
"start_line_num" => 7,
"sub_type" => "#define",
"type" => "content"
}, 'HeaderLine' ),
bless( {
"cond" => [
[
"defined(A)"
]
],
"flat" => "#endif",
"inner_lines" => 7,
"level" => 0,
"line" => "#endif\n",
"n_lines" => 1,
"raw" => "#endif\n",
"source" => "(buffer)",
"start_line_num" => 8,
"sub_type" => "#endif",
"type" => "cond"
}, 'HeaderLine' ),
bless( {
"cond" => [],
"flat" => "",
"level" => 0,
"line" => "/*comment\n line */\n",
"n_lines" => 2,
"raw" => "/*comment\n line */\n",
"source" => "(buffer)",
"start_line_num" => 9,
"sub_type" => "text",
"type" => "content"
}, 'HeaderLine' ),
bless( {
"cond" => [],
"flat" => "#define C D",
"level" => 0,
"line" => "#define C /* this is\n a hidden line continuation */ D\n",
"n_lines" => 2,
"raw" => "#define C /* this is\n a hidden line continuation */ D\n",
"source" => "(buffer)",
"start_line_num" => 11,
"sub_type" => "#define",
"type" => "content"
}, 'HeaderLine' )
];
DUMP_EOF
is($normal,<<~'EOF',"Normalized text as expected");
#if defined(A)
# if defined(B)
# define AB
content 1
# endif
content 2
# define A
#endif
/*comment
line */
#define C /* this is
a hidden line continuation */ D
EOF
{
my @warn;
local $SIG{__WARN__}= sub { push @warn, $_[0]; warn $_[0] };
my $ok= eval {
HeaderParser->new(add_commented_expr_after=>0)->parse_text(<<~'EOF'); 1
#ifdef A
#ifdef B
#endif
EOF
};
my $err= !$ok ? $@ : "";
ok(!$ok,"Should throw an error");
like($err,qr/Unterminated conditional block starting line 1 with last conditional operation at line 3/,
"Got expected error message");
}
{
my @warn;
local $SIG{__WARN__}= sub { push @warn, $_[0]; warn $_[0] };
my $ok= eval {
HeaderParser->new(add_commented_expr_after=>0)->parse_text(<<~'EOF'); 1
#ifdef A
#ifdef B
#elif C
EOF
};
my $err= !$ok ? $@ : "";
ok(!$ok,"Should throw an error");
like($err,qr/Unterminated conditional block starting line 3/,
"Unterminated block detected");
}
{
my @warn;
local $SIG{__WARN__}= sub { push @warn, $_[0]; warn $_[0] };
my $ok= eval {
HeaderParser->new(add_commented_expr_after=>0)->parse_text(<<~'EOF'); 1
#if 1 * * 10 > 5
#elifdef C
EOF
};
my $err= !$ok ? $@ : "";
ok(!$ok,"Should throw an error");
is($err,
"Error at line 1\n" .
"Line 1: #if 1 * * 10 > 5\n" .
"Error in multiplication expression: " .
"Unexpected token '*', expecting literal, unary, or expression.\n",
"Expected token error") or warn $err;
}
{
my $hp= HeaderParser->new(debug=>0,add_commented_expr_after=>0);
$hp->parse_text(<<~'EOF');
#ifdef A
# ifdef B
# define P
# else
# define Q
# endif
# if !defined B
# define R
# else
# define S
# endif
#endif
EOF
my $grouped= $hp->group_content();
my $as_text= $hp->lines_as_str($grouped);
is($as_text,<<~'EOF',"inverted simple clauses get merged properly") or show_text($as_text);
#if defined(A)
# if defined(B)
# define P
# define S
# else /* if !defined(B) */
# define Q
# define R
# endif /* !defined(B) */
#endif /* defined(A) */
EOF
}
{
my $hp= HeaderParser->new(debug=>0,add_commented_expr_after=>0);
$hp->parse_text(<<~'EOF');
#if defined(A) && defined(B)
# if (defined(C) && defined(D))
# define P
# else
# define Q
# endif
# if !(defined C && defined D)
# define R
# else
# define S
# endif
#endif
EOF
my $grouped= $hp->group_content();
my $as_text= $hp->lines_as_str($grouped);
is($as_text,<<~'EOF',"inverted complex clauses get merged properly") or show_text($as_text);
#if defined(A) && defined(B)
# if defined(C) && defined(D)
# define P
# define S
# else /* if !( defined(C) && defined(D) ) */
# define Q
# define R
# endif /* !( defined(C) && defined(D) ) */
#endif /* defined(A) && defined(B) */
EOF
}
{
my $hp= HeaderParser->new(debug=>0,add_commented_expr_after=>0);
$hp->parse_text(<<~'EOF');
#if defined(A)
#define HAS_A
#elif defined(B)
#define HAS_B
#elif defined(C)
#define HAS_C
#else
#define HAS_D
#endif
EOF
my $grouped= $hp->group_content();
my $as_text= $hp->lines_as_str($grouped);
is($as_text,<<~'EOF',"test nested elif round trip") or show_text($as_text);
#if defined(A)
# define HAS_A
#elif defined(B) /* && !defined(A) */
# define HAS_B
#elif defined(C) /* && !defined(A) && !defined(B) */
# define HAS_C
#else /* if !defined(A) && !defined(B) && !defined(C) */
# define HAS_D
#endif /* !defined(A) && !defined(B) && !defined(C) */
EOF
}
{
my $hp= HeaderParser->new(debug=>0,add_commented_expr_after=>0);
$hp->parse_text(<<~'EOF');
#if defined(A)
#define HAS_A
#endif
#if !defined(A) && defined(B)
#define HAS_B
#endif
#if defined(C)
#if !defined(A)
#if !defined(B)
#define HAS_C
#endif
#endif
#endif
#if !defined(B) && !defined(A) && !defined(C)
#define HAS_D
#endif
EOF
my $grouped= $hp->group_content();
my $as_text= $hp->lines_as_str($grouped);
is($as_text,<<~'EOF',"test elif composition from disparate statements") or show_text($as_text);
#if defined(A)
# define HAS_A
#elif defined(B) /* && !defined(A) */
# define HAS_B
#elif defined(C) /* && !defined(A) && !defined(B) */
# define HAS_C
#else /* if !defined(A) && !defined(B) && !defined(C) */
# define HAS_D
#endif /* !defined(A) && !defined(B) && !defined(C) */
EOF
}
{
my $hp= HeaderParser->new(debug=>0,add_commented_expr_after=>0);
$hp->parse_text(<<~'EOF');
#if defined(A)
#define HAS_A
#endif
#if !defined(A)
#define HAS_NOT_A
#if !defined(C)
#define HAS_A_NOT_C
#endif
#endif
#if defined(C)
#define HAS_C
#if defined(A)
#define HAS_A_C
#endif
#else
#if defined(A)
#define HAS_NOT_C_A
#endif
#endif
EOF
my $grouped= $hp->group_content();
my $as_text= $hp->lines_as_str($grouped);
is($as_text,<<~'EOF',"test else composition") or show_text($as_text);
#if defined(A)
# define HAS_A
# if defined(C)
# define HAS_A_C
# else /* if !defined(C) */
# define HAS_NOT_C_A
# endif /* !defined(C) */
#else /* if !defined(A) */
# define HAS_NOT_A
# if !defined(C)
# define HAS_A_NOT_C
# endif /* !defined(C) */
#endif /* !defined(A) */
#if defined(C)
# define HAS_C
#endif /* defined(C) */
EOF
}
{
my $hp= HeaderParser->new(debug=>0,add_commented_expr_after=>0);
$hp->parse_text(<<~'EOF');
#if !defined(A)
#define NOT_A1
#else
#define A1
#endif
#if !!!!defined(A)
#define A2
#else
#define NOT_A2
#endif
EOF
my $grouped= $hp->group_content();
my $as_text= $hp->lines_as_str($grouped);
is($as_text,<<~'EOF',"normalization into if/else") or show_text($as_text);
#if defined(A)
# define A1
# define A2
#else /* if !defined(A) */
# define NOT_A1
# define NOT_A2
#endif /* !defined(A) */
EOF
}
{
my $hp= HeaderParser->new(debug=>0,add_commented_expr_after=>0);
$hp->parse_text(<<~'EOF');
#if !!!(defined(A) && defined(B))
#define NOT_A_AND_B
#endif
#if defined(A)
#if defined(B)
#define A_AND_B
#endif
#endif
EOF
my $grouped= $hp->group_content();
my $as_text= $hp->lines_as_str($grouped);
is($as_text,<<~'EOF',"normalization with complex else") or show_text($as_text);
#if defined(A) && defined(B)
# define A_AND_B
#else /* if !( defined(A) && defined(B) ) */
# define NOT_A_AND_B
#endif /* !( defined(A) && defined(B) ) */
EOF
}
{
my $hp= HeaderParser->new(debug=>0,add_commented_expr_after=>0);
$hp->parse_text(<<~'EOF');
#if defined(A) && !!defined(A) && !!!!defined(A)
#define HAS_A
#endif
EOF
my $grouped= $hp->group_content();
my $as_text= $hp->lines_as_str($grouped);
is($as_text,<<~'EOF',"simplification") or show_text($as_text);
#if defined(A)
# define HAS_A
#endif /* defined(A) */
EOF
}
{
local $::TODO;
$::TODO= "Absorbtion not implemented yet";
# currently we don't handle absorbtion: (A && (A || B || C ...)) == A
my $hp= HeaderParser->new(debug=>0,add_commented_expr_after=>0);
$hp->parse_text(<<~'EOF');
#if defined(X) && (defined(X) || defined(Y))
#define HAS_X
#endif
EOF
my $grouped= $hp->group_content();
my $as_text= $hp->lines_as_str($grouped);
is($as_text,<<~'EOF',"simplification by absorbtion"); # or show_text($as_text);
#if defined(X)
# define HAS_X
#endif /* defined(X) */
EOF
}
{
my $hp= HeaderParser->new(debug=>0,add_commented_expr_after=>0);
$hp->parse_text(<<~'EOF');
#if defined(A) && (defined(B) && defined(C))
#define HAS_A
#endif
EOF
my $grouped= $hp->group_content();
my $as_text= $hp->lines_as_str($grouped);
is($as_text,<<~'EOF',"expression flattening") or show_text($as_text);
#if defined(A) && defined(B) && defined(C)
# define HAS_A
#endif /* defined(A) && defined(B) && defined(C) */
EOF
}
{
my $hp= HeaderParser->new(debug=>0,add_commented_expr_after=>3);
$hp->parse_text(<<~'EOF');
#if defined(A)
#define HAS_A1
#define HAS_A2
#define HAS_A3
#endif
#if defined(B)
#define HAS_B1
#else
#define HAS_B1e
#define HAS_B2e
#define HAS_B3e
#endif
#if defined(C)
#if defined(D)
#define HAS_D1
#endif
#elif defined(CC)
#define HAS_CC1
#define HAS_CC2
#define HAS_CC3
#endif
EOF
my $grouped= $hp->group_content();
my $as_text= $hp->lines_as_str($grouped);
is($as_text,<<~'EOF',"auto-comments") or show_text($as_text);
#if defined(A)
# define HAS_A1
# define HAS_A2
# define HAS_A3
#endif /* defined(A) */
#if defined(B)
# define HAS_B1
#else
# define HAS_B1e
# define HAS_B2e
# define HAS_B3e
#endif /* !defined(B) */
#if defined(C)
# if defined(D)
# define HAS_D1
# endif
#elif defined(CC) /* && !defined(C) */
# define HAS_CC1
# define HAS_CC2
# define HAS_CC3
#endif /* !defined(C) && defined(CC) */
EOF
}
{
my $hp= HeaderParser->new(debug=>0,add_commented_expr_after=>0);
$hp->parse_text(<<~'EOF');
#if defined(DEBUGGING) \
|| (defined(USE_LOCALE) && ( defined(USE_THREADS) \
|| defined(HAS_IGNORED_LOCALE_CATEGORIES)\
|| defined(USE_POSIX_2008_LOCALE) \
|| ! defined(LC_ALL)))
# define X
#endif
EOF
my $grouped= $hp->group_content();
my $as_text= $hp->lines_as_str($grouped);
is($as_text,<<~'EOF',"Karls example") or show_text($as_text);
#if defined(DEBUGGING) || \
( defined(USE_LOCALE) && \
( defined(HAS_IGNORED_LOCALE_CATEGORIES) || !defined(LC_ALL) || \
defined(USE_POSIX_2008_LOCALE) || defined(USE_THREADS) ) )
# define X
#endif /* defined(DEBUGGING) ||
( defined(USE_LOCALE) &&
( defined(HAS_IGNORED_LOCALE_CATEGORIES) || !defined(LC_ALL) ||
defined(USE_POSIX_2008_LOCALE) || defined(USE_THREADS) ) ) */
EOF
}
done_testing();