Updated 2016-12-17 16:03:28 by dkf

The tcltest customMatch command allows the test developer to create a custom match routine, extending your options beyond exact, glob, and regexp.

RWT needed a way to reliably compare floating point numbers, which sometimes vary from platform to platform. One approach is to format with a fixed number of decimal places and use the usual -match exact semantics for the test. This works pretty well until you compare floating point numbers with three exponent digits to floating point numbers with two digits, as you will find on Windows and Linux, respectively.

A more reliable approach for comparing floating point numbers would be to define a customMatch script which performs a numeric comparison using expr != operator on double values.
    #----------------------------------------------------------------
    #
    #  customMatch matchDouble
    #
    #  Custom match routine that performs word-by-word comparisons
    #  using expr's floating point comparison operation (!=)
    #  for words that are [string is double].
    #
    #  This custom match routine is required for tests to
    #  run on Microsoft Windows (and perhaps other) platforms
    #  where floating point numbers get a three digit
    #  exponent (i.e. 3.0e008 instead of 3.0e08).
    #
    #  Note that this routines treats all white space (spaces,
    #  tabs and newlines) as equal.
    #
    #----------------------------------------------------------------
    customMatch matchDouble matchDoubleProc
    proc matchDoubleProc {expectedResult actualResult} {
        # split expected/actual into list of words
        set elist [split $expectedResult " \t\n"]
        set alist [split $actualResult   " \t\n"]
        # perform word-by-word comparison
        foreach eword $elist aword $alist {
            if { [string is double $eword] && ! [string is integer $eword] } {
                # perform numeric comparison
                if { $eword != $aword } {return 0}
            } else {
                # perform string comparison
                if { $eword ne $aword } {return 0}
            }
        }
        return 1
    }

jnc The above was not working in my test cases. What about this?
    proc matchDoubleProc {expectedResult actualResult} {
        set expectedFormatted [format $expectedResult]
        set decimalCount [string length [lindex [split $expectedFormatted "."] 1]]
        set fmtCode "%.${decimalCount}f"
        set actualFormatted [format $fmtCode $actualResult]
    
        if { $expectedFormatted eq $actualFormatted } {
            return 1
        }
        
        return 0
    }

An interesting variant on matchDouble would use a sigma parameter for approximate comparisons.

glennj I just started with tcltest and one use for customMatch jumped out at me. When I write functions that return a boolean, they might return the string "true" or "false", or they might return the result of an [expr] command (1/0). I wrote this customMatch that ensures the expected and actual values are either both true or both false (uses [apply], so required Tcl 8.5):
    # test passes if expected and actual are both true or both false
    customMatch boolean {apply {{e a} {expr {
        [string is boolean -strict $e] && 
        [string is boolean -strict $a] && 
        (($e && $a) || (!$e && !$a))
    }}}}

The applied [test] command looks like:
    test test-name {boolean command should return true} \
        -body {command that returns some boolean value} \
        -match boolean \
        -result true

EF The following customMatch will test equality of dictionaries. It also uses [apply], thus is for "modern" Tcl. Note that it assumes that the content of the keys are strings, which might not be appropriate in all cases.
customMatch dictionary {apply {{expected actual} {
    if {[dict size $expected] != [dict size $actual]} { return 0 }
    dict for {k v} $expected {
        if {![dict exists $actual $k]} { return 0 }
        if {[dict get $actual $k] ne $v} { return 0 }
    }
    return 1
}}}