Mercurial
comparison third_party/sqlite3/autosetup/teaish/tester.tcl @ 167:589bab390fb4
[ThirdParty] Added sqlite3 to the third_party.
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Mon, 19 Jan 2026 16:28:45 -0800 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 166:78ea8d5ccc87 | 167:589bab390fb4 |
|---|---|
| 1 ######################################################################## | |
| 2 # 2025 April 5 | |
| 3 # | |
| 4 # The author disclaims copyright to this source code. In place of | |
| 5 # a legal notice, here is a blessing: | |
| 6 # | |
| 7 # * May you do good and not evil. | |
| 8 # * May you find forgiveness for yourself and forgive others. | |
| 9 # * May you share freely, never taking more than you give. | |
| 10 # | |
| 11 ######################################################################## | |
| 12 # | |
| 13 # Helper routines for running tests on teaish extensions | |
| 14 # | |
| 15 ######################################################################## | |
| 16 # ----- @module teaish/tester.tcl ----- | |
| 17 # | |
| 18 # @section TEA-ish Testing APIs. | |
| 19 # | |
| 20 # Though these are part of the autosup dir hierarchy, they are not | |
| 21 # intended to be run from autosetup code. Rather, they're for use | |
| 22 # with/via teaish.tester.tcl and target canonical Tcl only, not JimTcl | |
| 23 # (which the autosetup pieces do target). | |
| 24 | |
| 25 # | |
| 26 # @test-current-scope ?lvl? | |
| 27 # | |
| 28 # Returns the name of the _calling_ proc from ($lvl + 1) levels up the | |
| 29 # call stack (where the caller's level will be 1 up from _this_ | |
| 30 # call). If $lvl would resolve to global scope "global scope" is | |
| 31 # returned and if it would be negative then a string indicating such | |
| 32 # is returned (as opposed to throwing an error). | |
| 33 # | |
| 34 proc test-current-scope {{lvl 0}} { | |
| 35 #uplevel [expr {$lvl + 1}] {lindex [info level 0] 0} | |
| 36 set ilvl [info level] | |
| 37 set offset [expr {$ilvl - $lvl - 1}] | |
| 38 if { $offset < 0} { | |
| 39 return "invalid scope ($offset)" | |
| 40 } elseif { $offset == 0} { | |
| 41 return "global scope" | |
| 42 } else { | |
| 43 return [lindex [info level $offset] 0] | |
| 44 } | |
| 45 } | |
| 46 | |
| 47 # @test-msg | |
| 48 # | |
| 49 # Emits all arugments to stdout. | |
| 50 # | |
| 51 proc test-msg {args} { | |
| 52 puts "$args" | |
| 53 } | |
| 54 | |
| 55 # @test-warn | |
| 56 # | |
| 57 # Emits all arugments to stderr. | |
| 58 # | |
| 59 proc test-warn {args} { | |
| 60 puts stderr "WARNING: $args" | |
| 61 } | |
| 62 | |
| 63 # | |
| 64 # @test-error msg | |
| 65 # | |
| 66 # Triggers a test-failed error with a string describing the calling | |
| 67 # scope and the provided message. | |
| 68 # | |
| 69 proc test-fail {args} { | |
| 70 #puts stderr "ERROR: \[[test-current-scope 1]]: $msg" | |
| 71 #exit 1 | |
| 72 error "FAIL: \[[test-current-scope 1]]: $args" | |
| 73 } | |
| 74 | |
| 75 array set ::test__Counters {} | |
| 76 array set ::test__Config { | |
| 77 verbose-assert 0 verbose-affirm 0 | |
| 78 } | |
| 79 | |
| 80 # Internal impl for affirm and assert. | |
| 81 # | |
| 82 # $args = ?-v? script {msg-on-fail ""} | |
| 83 proc test__affert {failMode args} { | |
| 84 if {$failMode} { | |
| 85 set what assert | |
| 86 } else { | |
| 87 set what affirm | |
| 88 } | |
| 89 set verbose $::test__Config(verbose-$what) | |
| 90 if {"-v" eq [lindex $args 0]} { | |
| 91 lassign $args - script msg | |
| 92 if {1 == [llength $args]} { | |
| 93 # If -v is the only arg, toggle default verbose mode | |
| 94 set ::test__Config(verbose-$what) [expr {!$::test__Config(verbose-$what)}] | |
| 95 return | |
| 96 } | |
| 97 incr verbose | |
| 98 } else { | |
| 99 lassign $args script msg | |
| 100 } | |
| 101 incr ::test__Counters($what) | |
| 102 if {![uplevel 1 expr [list $script]]} { | |
| 103 if {"" eq $msg} { | |
| 104 set msg $script | |
| 105 } | |
| 106 set txt [join [list $what # $::test__Counters($what) "failed:" $msg]] | |
| 107 if {$failMode} { | |
| 108 puts stderr $txt | |
| 109 exit 1 | |
| 110 } else { | |
| 111 error $txt | |
| 112 } | |
| 113 } elseif {$verbose} { | |
| 114 puts stderr [join [list $what # $::test__Counters($what) "passed:" $script]] | |
| 115 } | |
| 116 } | |
| 117 | |
| 118 # | |
| 119 # @affirm ?-v? script ?msg? | |
| 120 # | |
| 121 # Works like a conventional assert method does, but reports failures | |
| 122 # using [error] instead of [exit]. If -v is used, it reports passing | |
| 123 # assertions to stderr. $script is evaluated in the caller's scope as | |
| 124 # an argument to [expr]. | |
| 125 # | |
| 126 proc affirm {args} { | |
| 127 tailcall test__affert 0 {*}$args | |
| 128 } | |
| 129 | |
| 130 # | |
| 131 # @assert ?-v? script ?msg? | |
| 132 # | |
| 133 # Works like [affirm] but exits on error. | |
| 134 # | |
| 135 proc assert {args} { | |
| 136 tailcall test__affert 1 {*}$args | |
| 137 } | |
| 138 | |
| 139 # | |
| 140 # @assert-matches ?-e? pattern ?-e? rhs ?msg? | |
| 141 # | |
| 142 # Equivalent to assert {[string match $pattern $rhs]} except that | |
| 143 # if either of those are prefixed with an -e flag, they are eval'd | |
| 144 # and their results are used. | |
| 145 # | |
| 146 proc assert-matches {args} { | |
| 147 set evalLhs 0 | |
| 148 set evalRhs 0 | |
| 149 if {"-e" eq [lindex $args 0]} { | |
| 150 incr evalLhs | |
| 151 set args [lassign $args -] | |
| 152 } | |
| 153 set args [lassign $args pattern] | |
| 154 if {"-e" eq [lindex $args 0]} { | |
| 155 incr evalRhs | |
| 156 set args [lassign $args -] | |
| 157 } | |
| 158 set args [lassign $args rhs msg] | |
| 159 | |
| 160 if {$evalLhs} { | |
| 161 set pattern [uplevel 1 $pattern] | |
| 162 } | |
| 163 if {$evalRhs} { | |
| 164 set rhs [uplevel 1 $rhs] | |
| 165 } | |
| 166 #puts "***pattern=$pattern\n***rhs=$rhs" | |
| 167 tailcall test__affert 1 \ | |
| 168 [join [list \[ string match [list $pattern] [list $rhs] \]]] $msg | |
| 169 # why does this not work? [list \[ string match [list $pattern] [list $rhs] \]] $msg | |
| 170 # "\[string match [list $pattern] [list $rhs]\]" | |
| 171 } | |
| 172 | |
| 173 # | |
| 174 # @test-assert testId script ?msg? | |
| 175 # | |
| 176 # Works like [assert] but emits $testId to stdout first. | |
| 177 # | |
| 178 proc test-assert {testId script {msg ""}} { | |
| 179 puts "test $testId" | |
| 180 tailcall test__affert 1 $script $msg | |
| 181 } | |
| 182 | |
| 183 # | |
| 184 # @test-expect testId script result | |
| 185 # | |
| 186 # Runs $script in the calling scope and compares its result to | |
| 187 # $result, minus any leading or trailing whitespace. If they differ, | |
| 188 # it triggers an [assert]. | |
| 189 # | |
| 190 proc test-expect {testId script result} { | |
| 191 puts "test $testId" | |
| 192 set x [string trim [uplevel 1 $script]] | |
| 193 set result [string trim $result] | |
| 194 tailcall test__affert 0 [list "{$x}" eq "{$result}"] \ | |
| 195 "\nEXPECTED: <<$result>>\nGOT: <<$x>>" | |
| 196 } | |
| 197 | |
| 198 # | |
| 199 # @test-catch cmd ?...args? | |
| 200 # | |
| 201 # Runs [cmd ...args], repressing any exception except to possibly log | |
| 202 # the failure. Returns 1 if it caught anything, 0 if it didn't. | |
| 203 # | |
| 204 proc test-catch {cmd args} { | |
| 205 if {[catch { | |
| 206 uplevel 1 $cmd {*}$args | |
| 207 } rc xopts]} { | |
| 208 puts "[test-current-scope] ignoring failure of: $cmd [lindex $args 0]: $rc" | |
| 209 return 1 | |
| 210 } | |
| 211 return 0 | |
| 212 } | |
| 213 | |
| 214 # | |
| 215 # @test-catch-matching pattern (script|cmd args...) | |
| 216 # | |
| 217 # Works like test-catch, but it expects its argument(s) to to throw an | |
| 218 # error matching the given string (checked with [string match]). If | |
| 219 # they do not throw, or the error does not match $pattern, this | |
| 220 # function throws, else it returns 1. | |
| 221 # | |
| 222 # If there is no second argument, the $cmd is assumed to be a script, | |
| 223 # and will be eval'd in the caller's scope. | |
| 224 # | |
| 225 # TODO: add -glob and -regex flags to control matching flavor. | |
| 226 # | |
| 227 proc test-catch-matching {pattern cmd args} { | |
| 228 if {[catch { | |
| 229 #puts "**** catch-matching cmd=$cmd args=$args" | |
| 230 if {0 == [llength $args]} { | |
| 231 uplevel 1 $cmd {*}$args | |
| 232 } else { | |
| 233 $cmd {*}$args | |
| 234 } | |
| 235 } rc xopts]} { | |
| 236 if {[string match $pattern $rc]} { | |
| 237 return 1 | |
| 238 } else { | |
| 239 error "[test-current-scope] exception does not match {$pattern}: {$rc}" | |
| 240 } | |
| 241 } | |
| 242 error "[test-current-scope] expecting to see an error matching {$pattern}" | |
| 243 } | |
| 244 | |
| 245 if {![array exists ::teaish__BuildFlags]} { | |
| 246 array set ::teaish__BuildFlags {} | |
| 247 } | |
| 248 | |
| 249 # | |
| 250 # @teaish-build-flag3 flag tgtVar ?dflt? | |
| 251 # | |
| 252 # If the current build has the configure-time flag named $flag set | |
| 253 # then tgtVar is assigned its value and 1 is returned, else tgtVal is | |
| 254 # assigned $dflt and 0 is returned. | |
| 255 # | |
| 256 # Caveat #1: only valid when called in the context of teaish's default | |
| 257 # "make test" recipe, e.g. from teaish.test.tcl. It is not valid from | |
| 258 # a teaish.tcl configure script because (A) the state it relies on | |
| 259 # doesn't fully exist at that point and (B) that level of the API has | |
| 260 # more direct access to the build state. This function requires that | |
| 261 # an external script have populated its internal state, which is | |
| 262 # normally handled via teaish.tester.tcl.in. | |
| 263 # | |
| 264 # Caveat #2: defines in the style of HAVE_FEATURENAME with a value of | |
| 265 # 0 are, by long-standing configure script conventions, treated as | |
| 266 # _undefined_ here. | |
| 267 # | |
| 268 proc teaish-build-flag3 {flag tgtVar {dflt ""}} { | |
| 269 upvar $tgtVar tgt | |
| 270 if {[info exists ::teaish__BuildFlags($flag)]} { | |
| 271 set tgt $::teaish__BuildFlags($flag) | |
| 272 return 1; | |
| 273 } elseif {0==[array size ::teaish__BuildFlags]} { | |
| 274 test-warn \ | |
| 275 "\[[test-current-scope]] was called from " \ | |
| 276 "[test-current-scope 1] without the build flags imported." | |
| 277 } | |
| 278 set tgt $dflt | |
| 279 return 0 | |
| 280 } | |
| 281 | |
| 282 # | |
| 283 # @teaish-build-flag flag ?dflt? | |
| 284 # | |
| 285 # Convenience form of teaish-build-flag3 which returns the | |
| 286 # configure-time-defined value of $flag or "" if it's not defined (or | |
| 287 # if it's an empty string). | |
| 288 # | |
| 289 proc teaish-build-flag {flag {dflt ""}} { | |
| 290 set tgt "" | |
| 291 teaish-build-flag3 $flag tgt $dflt | |
| 292 return $tgt | |
| 293 } |