| # Copyright 2020-2021 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 this program. If not, see <http://www.gnu.org/licenses/>. |
| |
| # This library provides some protection against the introduction of |
| # tests that include either the source of build paths in the test |
| # name. When a test includes the path in its test name it is harder |
| # to compare results between two runs of GDB from different trees. |
| |
| namespace eval ::CheckTestNames { |
| # An associative array of all test names to the number of times each |
| # name is seen. Used to detect duplicate test names. |
| variable all_test_names |
| array set all_test_names {} |
| |
| # An associative array of counts of tests that either include a path in |
| # their test name, or have a duplicate test name. There are two counts |
| # for each issue, 'count', which counts occurrences within a single |
| # variant run, and 'total', which counts across all variants. |
| variable counts |
| array set counts {} |
| foreach nm {paths duplicates} { |
| set counts($nm,count) 0 |
| set counts($nm,total) 0 |
| } |
| |
| # Increment the count, and total count for TYPE. |
| proc inc_count { type } { |
| variable counts |
| |
| incr counts($type,count) |
| incr counts($type,total) |
| } |
| |
| # Check if MESSAGE contains a build or source path, if it does increment |
| # the relevant counter and return true, otherwise, return false. |
| proc _check_paths { message } { |
| global srcdir objdir |
| |
| foreach path [list $srcdir $objdir] { |
| if { [ string first $path $message ] >= 0 } { |
| # Count each test just once. |
| inc_count paths |
| return true |
| } |
| } |
| |
| return false |
| } |
| |
| # Check if MESSAGE is a duplicate, if it is then increment the |
| # duplicates counter and return true, otherwise, return false. |
| proc _check_duplicates { message } { |
| variable all_test_names |
| |
| # Initialise a count, or increment the count for this test name. |
| if {![info exists all_test_names($message)]} { |
| set all_test_names($message) 0 |
| } else { |
| if {$all_test_names($message) == 0} { |
| inc_count duplicates |
| } |
| incr all_test_names($message) |
| return true |
| } |
| |
| return false |
| } |
| |
| # Remove the leading Dejagnu status marker from MESSAGE, and |
| # return the remainder of MESSAGE. A status marker is something |
| # like 'PASS: '. It is assumed that MESSAGE does contain such a |
| # marker. If it doesn't then MESSAGE is returned unmodified. |
| proc _strip_status { message } { |
| # Find the position of the first ': ' string. |
| set pos [string first ": " $message] |
| if { $pos > -1 } { |
| # The '+ 2' is so we skip the ': ' we found above. |
| return [string range $message [expr $pos + 2] end] |
| } |
| |
| return $message |
| } |
| |
| # Check if MESSAGE is a well-formed test name. |
| proc _check_well_formed_name { message } { |
| if { [regexp \n $message]} { |
| warning "Newline in test name" |
| } |
| } |
| |
| # Check if MESSAGE contains either the source path or the build path. |
| # This will result in test names that can't easily be compared between |
| # different runs of GDB. |
| # |
| # Any offending test names cause the corresponding count to be |
| # incremented, and an extra message to be printed into the log |
| # file. |
| proc check { message } { |
| set message [ _strip_status $message ] |
| |
| if [ _check_paths $message ] { |
| clone_output "PATH: $message" |
| } |
| |
| if [ _check_duplicates $message ] { |
| clone_output "DUPLICATE: $message" |
| } |
| |
| _check_well_formed_name $message |
| } |
| |
| # If COUNT is greater than zero, disply PREFIX followed by COUNT. |
| proc maybe_show_count { prefix count } { |
| if { $count > 0 } { |
| clone_output "$prefix$count" |
| } |
| } |
| |
| # Rename Dejagnu's log_summary procedure, and create do_log_summary to |
| # replace it. We arrange to have do_log_summary called later. |
| rename ::log_summary log_summary |
| proc do_log_summary { args } { |
| variable counts |
| |
| # If ARGS is the empty list then we don't want to pass a single |
| # empty string as a parameter here. |
| eval "CheckTestNames::log_summary $args" |
| |
| if { [llength $args] == 0 } { |
| set which "count" |
| } else { |
| set which [lindex $args 0] |
| } |
| |
| maybe_show_count "# of paths in test names\t" \ |
| $counts(paths,$which) |
| maybe_show_count "# of duplicate test names\t" \ |
| $counts(duplicates,$which) |
| } |
| |
| # Rename Dejagnu's reset_vars procedure, and create do_reset_vars to |
| # replace it. We arrange to have do_reset_vars called later. |
| rename ::reset_vars reset_vars |
| proc do_reset_vars {} { |
| variable all_test_names |
| variable counts |
| |
| CheckTestNames::reset_vars |
| |
| array unset all_test_names |
| foreach nm {paths duplicates} { |
| set counts($nm,count) 0 |
| } |
| } |
| } |
| |
| # Arrange for Dejagnu to call CheckTestNames::check for each test result. |
| foreach nm {pass fail xfail kfail xpass kpass unresolved untested \ |
| unsupported} { |
| set local_record_procs($nm) "CheckTestNames::check" |
| } |
| |
| # Create new global log_summary to replace Dejagnu's. |
| proc log_summary { args } { |
| eval "CheckTestNames::do_log_summary $args" |
| } |
| |
| # Create new global reset_vars to replace Dejagnu's. |
| proc reset_vars {} { |
| eval "CheckTestNames::do_reset_vars" |
| } |