# Copyright 2020-2023 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 . # 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" }