blob: a2e963e555f1a3c910e632df99531431e18c9f9d [file] [log] [blame]
# Copyright (C) 2015-2016 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with GCC; see the file COPYING3. If not see
# <http://www.gnu.org/licenses/>.
# Testing of multiline output
# We have pre-existing testcases like this:
# |typedef struct _GMutex GMutex; // { dg-message "previously declared here"}
# (using "|" here to indicate the start of a line),
# generating output like this:
# |gcc/testsuite/g++.dg/diagnostic/wrong-tag-1.C:4:16: note: 'struct _GMutex' was previously declared here
# where the location of the dg-message determines the expected line at
# which the error should be reported.
#
# To handle rich error-reporting, we want to be able to verify that we
# get output like this:
# |gcc/testsuite/g++.dg/diagnostic/wrong-tag-1.C:4:16: note: 'struct _GMutex' was previously declared here
# | typedef struct _GMutex GMutex; // { dg-message "previously declared here"}
# | ^~~~~~~
# where the compiler's first line of output is as before, but in
# which it then echoes the source lines, adding annotations.
#
# We want to be able to write testcases that verify that the
# emitted source-and-annotations are sane.
#
# A complication here is that the source lines contain comments
# containing DejaGnu directives (such as the "dg-message" above).
#
# We punt this somewhat by only matching the beginnings of lines.
# so that we can write e.g.
# |/* { dg-begin-multiline-output "" }
# | typedef struct _GMutex GMutex;
# | ^~~~~~~
# | { dg-end-multiline-output "" } */
# to have the testsuite verify the expected output.
############################################################################
# Global variables.
############################################################################
# This is intended to only be used from within multiline.exp.
# The line number of the last dg-begin-multiline-output directive.
set _multiline_last_beginning_line -1
# A list of
# first-line-number, last-line-number, lines
# where each "lines" is a list of strings.
# This is cleared at the end of each test by gcc-dg.exp's wrapper for dg-test.
set multiline_expected_outputs []
############################################################################
# Exported functions.
############################################################################
# Mark the beginning of an expected multiline output
# All lines between this and the next dg-end-multiline-output are
# expected to be seen.
proc dg-begin-multiline-output { args } {
global _multiline_last_beginning_line
verbose "dg-begin-multiline-output: args: $args" 3
set line [expr [lindex $args 0] + 1]
set _multiline_last_beginning_line $line
}
# Mark the end of an expected multiline output
# All lines up to here since the last dg-begin-multiline-output are
# expected to be seen.
proc dg-end-multiline-output { args } {
global _multiline_last_beginning_line
verbose "dg-end-multiline-output: args: $args" 3
set line [expr [lindex $args 0] - 1]
verbose "multiline output lines: $_multiline_last_beginning_line-$line" 3
upvar 1 prog prog
verbose "prog: $prog" 3
# "prog" now contains the filename
# Load it and split it into lines
set lines [_get_lines $prog $_multiline_last_beginning_line $line]
verbose "lines: $lines" 3
# Create an entry of the form: first-line, last-line, lines
set entry [list $_multiline_last_beginning_line $line $lines]
global multiline_expected_outputs
lappend multiline_expected_outputs $entry
verbose "within dg-end-multiline-output: multiline_expected_outputs: $multiline_expected_outputs" 3
set _multiline_last_beginning_line -1
}
# Hook to be called by prune.exp's prune_gcc_output to
# look for the expected multiline outputs, pruning them,
# reporting PASS for those that are found, and FAIL for
# those that weren't found.
#
# It returns a pruned version of its output.
proc handle-multiline-outputs { text } {
global multiline_expected_outputs
global testname_with_flags
set index 0
foreach entry $multiline_expected_outputs {
verbose " entry: $entry" 3
set start_line [lindex $entry 0]
set end_line [lindex $entry 1]
set multiline [lindex $entry 2]
verbose " multiline: $multiline" 3
set rexp [_build_multiline_regex $multiline $index]
verbose "rexp: ${rexp}" 4
# Escape newlines in $rexp so that we can print them in
# pass/fail results.
set escaped_regex [string map {"\n" "\\n"} $rexp]
verbose "escaped_regex: ${escaped_regex}" 4
set title "$testname_with_flags expected multiline pattern lines $start_line-$end_line"
# Use "regsub" to attempt to prune the pattern from $text
if {[regsub -line $rexp $text "" text]} {
# Success; the multiline pattern was pruned.
pass "$title was found: \"$escaped_regex\""
} else {
fail "$title not found: \"$escaped_regex\""
}
set index [expr $index + 1]
}
return $text
}
############################################################################
# Internal functions
############################################################################
# Load FILENAME and extract the lines from FIRST_LINE
# to LAST_LINE (inclusive) as a list of strings.
proc _get_lines { filename first_line last_line } {
verbose "_get_lines" 3
verbose " filename: $filename" 3
verbose " first_line: $first_line" 3
verbose " last_line: $last_line" 3
set fp [open $filename r]
set file_data [read $fp]
close $fp
set data [split $file_data "\n"]
set linenum 1
set lines []
foreach line $data {
verbose "line $linenum: $line" 4
if { $linenum >= $first_line && $linenum <= $last_line } {
lappend lines $line
}
set linenum [expr $linenum + 1]
}
return $lines
}
# Convert $multiline from a list of strings to a multiline regex
# We need to support matching arbitrary followup text on each line,
# to deal with comments containing containing DejaGnu directives.
proc _build_multiline_regex { multiline index } {
verbose "_build_multiline_regex: $multiline $index" 4
set rexp ""
foreach line $multiline {
verbose " line: $line" 4
# We need to escape "^" and other regexp metacharacters.
set line [string map {"^" "\\^"
"(" "\\("
")" "\\)"
"[" "\\["
"]" "\\]"
"{" "\\{"
"}" "\\}"
"." "\\."
"\\" "\\\\"
"?" "\\?"
"+" "\\+"
"*" "\\*"
"|" "\\|"} $line]
append rexp $line
if {[string match "*^" $line] || [string match "*~" $line]} {
# Assume a line containing a caret/range. This must be
# an exact match.
} elseif {[string match "*\\|" $line]} {
# Assume a source line with a right-margin. Support
# arbitrary text in place of any whitespace before the
# right-margin, to deal with comments containing containing
# DejaGnu directives.
# Remove final "\|":
set rexp [string range $rexp 0 [expr [string length $rexp] - 3]]
# Trim off trailing whitespace:
set old_length [string length $rexp]
set rexp [string trimright $rexp]
set new_length [string length $rexp]
# Replace the trimmed whitespace with "." chars to match anything:
set ws [string repeat "." [expr $old_length - $new_length]]
set rexp "${rexp}${ws}"
# Add back the trailing '\|':
set rexp "${rexp}\\|"
} else {
# Assume that we have a quoted source line.
if {![string equal "" $line] } {
# Support arbitrary followup text on each non-empty line,
# to deal with comments containing containing DejaGnu
# directives.
append rexp ".*"
}
}
append rexp "\n"
}
# dg.exp's dg-test trims leading whitespace from the output
# in this line:
# set comp_output [string trimleft $comp_output]
# so we can't rely on the exact leading whitespace for the
# first line in the *first* multiline regex.
#
# Trim leading whitespace from the regexp, replacing it with
# a "\s*", to match zero or more whitespace characters.
if { $index == 0 } {
set rexp [string trimleft $rexp]
set rexp "\\s*$rexp"
}
verbose "rexp: $rexp" 4
return $rexp
}