mirror of
https://github.com/sqlite/sqlite.git
synced 2025-07-29 08:01:23 +03:00
294 lines
7.9 KiB
Tcl
294 lines
7.9 KiB
Tcl
########################################################################
|
|
# 2025 April 5
|
|
#
|
|
# The author disclaims copyright to this source code. In place of
|
|
# a legal notice, here is a blessing:
|
|
#
|
|
# * May you do good and not evil.
|
|
# * May you find forgiveness for yourself and forgive others.
|
|
# * May you share freely, never taking more than you give.
|
|
#
|
|
########################################################################
|
|
#
|
|
# Helper routines for running tests on teaish extensions
|
|
#
|
|
########################################################################
|
|
# ----- @module teaish/tester.tcl -----
|
|
#
|
|
# @section TEA-ish Testing APIs.
|
|
#
|
|
# Though these are part of the autosup dir hierarchy, they are not
|
|
# intended to be run from autosetup code. Rather, they're for use
|
|
# with/via teaish.tester.tcl and target canonical Tcl only, not JimTcl
|
|
# (which the autosetup pieces do target).
|
|
|
|
#
|
|
# @test-current-scope ?lvl?
|
|
#
|
|
# Returns the name of the _calling_ proc from ($lvl + 1) levels up the
|
|
# call stack (where the caller's level will be 1 up from _this_
|
|
# call). If $lvl would resolve to global scope "global scope" is
|
|
# returned and if it would be negative then a string indicating such
|
|
# is returned (as opposed to throwing an error).
|
|
#
|
|
proc test-current-scope {{lvl 0}} {
|
|
#uplevel [expr {$lvl + 1}] {lindex [info level 0] 0}
|
|
set ilvl [info level]
|
|
set offset [expr {$ilvl - $lvl - 1}]
|
|
if { $offset < 0} {
|
|
return "invalid scope ($offset)"
|
|
} elseif { $offset == 0} {
|
|
return "global scope"
|
|
} else {
|
|
return [lindex [info level $offset] 0]
|
|
}
|
|
}
|
|
|
|
# @test-msg
|
|
#
|
|
# Emits all arugments to stdout.
|
|
#
|
|
proc test-msg {args} {
|
|
puts "$args"
|
|
}
|
|
|
|
# @test-warn
|
|
#
|
|
# Emits all arugments to stderr.
|
|
#
|
|
proc test-warn {args} {
|
|
puts stderr "WARNING: $args"
|
|
}
|
|
|
|
#
|
|
# @test-error msg
|
|
#
|
|
# Triggers a test-failed error with a string describing the calling
|
|
# scope and the provided message.
|
|
#
|
|
proc test-fail {args} {
|
|
#puts stderr "ERROR: \[[test-current-scope 1]]: $msg"
|
|
#exit 1
|
|
error "FAIL: \[[test-current-scope 1]]: $args"
|
|
}
|
|
|
|
array set ::test__Counters {}
|
|
array set ::test__Config {
|
|
verbose-assert 0 verbose-affirm 0
|
|
}
|
|
|
|
# Internal impl for affirm and assert.
|
|
#
|
|
# $args = ?-v? script {msg-on-fail ""}
|
|
proc test__affert {failMode args} {
|
|
if {$failMode} {
|
|
set what assert
|
|
} else {
|
|
set what affirm
|
|
}
|
|
set verbose $::test__Config(verbose-$what)
|
|
if {"-v" eq [lindex $args 0]} {
|
|
lassign $args - script msg
|
|
if {1 == [llength $args]} {
|
|
# If -v is the only arg, toggle default verbose mode
|
|
set ::test__Config(verbose-$what) [expr {!$::test__Config(verbose-$what)}]
|
|
return
|
|
}
|
|
incr verbose
|
|
} else {
|
|
lassign $args script msg
|
|
}
|
|
incr ::test__Counters($what)
|
|
if {![uplevel 1 expr [list $script]]} {
|
|
if {"" eq $msg} {
|
|
set msg $script
|
|
}
|
|
set txt [join [list $what # $::test__Counters($what) "failed:" $msg]]
|
|
if {$failMode} {
|
|
puts stderr $txt
|
|
exit 1
|
|
} else {
|
|
error $txt
|
|
}
|
|
} elseif {$verbose} {
|
|
puts stderr [join [list $what # $::test__Counters($what) "passed:" $script]]
|
|
}
|
|
}
|
|
|
|
#
|
|
# @affirm ?-v? script ?msg?
|
|
#
|
|
# Works like a conventional assert method does, but reports failures
|
|
# using [error] instead of [exit]. If -v is used, it reports passing
|
|
# assertions to stderr. $script is evaluated in the caller's scope as
|
|
# an argument to [expr].
|
|
#
|
|
proc affirm {args} {
|
|
tailcall test__affert 0 {*}$args
|
|
}
|
|
|
|
#
|
|
# @assert ?-v? script ?msg?
|
|
#
|
|
# Works like [affirm] but exits on error.
|
|
#
|
|
proc assert {args} {
|
|
tailcall test__affert 1 {*}$args
|
|
}
|
|
|
|
#
|
|
# @assert-matches ?-e? pattern ?-e? rhs ?msg?
|
|
#
|
|
# Equivalent to assert {[string match $pattern $rhs]} except that
|
|
# if either of those are prefixed with an -e flag, they are eval'd
|
|
# and their results are used.
|
|
#
|
|
proc assert-matches {args} {
|
|
set evalLhs 0
|
|
set evalRhs 0
|
|
if {"-e" eq [lindex $args 0]} {
|
|
incr evalLhs
|
|
set args [lassign $args -]
|
|
}
|
|
set args [lassign $args pattern]
|
|
if {"-e" eq [lindex $args 0]} {
|
|
incr evalRhs
|
|
set args [lassign $args -]
|
|
}
|
|
set args [lassign $args rhs msg]
|
|
|
|
if {$evalLhs} {
|
|
set pattern [uplevel 1 $pattern]
|
|
}
|
|
if {$evalRhs} {
|
|
set rhs [uplevel 1 $rhs]
|
|
}
|
|
#puts "***pattern=$pattern\n***rhs=$rhs"
|
|
tailcall test__affert 1 \
|
|
[join [list \[ string match [list $pattern] [list $rhs] \]]] $msg
|
|
# why does this not work? [list \[ string match [list $pattern] [list $rhs] \]] $msg
|
|
# "\[string match [list $pattern] [list $rhs]\]"
|
|
}
|
|
|
|
#
|
|
# @test-assert testId script ?msg?
|
|
#
|
|
# Works like [assert] but emits $testId to stdout first.
|
|
#
|
|
proc test-assert {testId script {msg ""}} {
|
|
puts "test $testId"
|
|
tailcall test__affert 1 $script $msg
|
|
}
|
|
|
|
#
|
|
# @test-expect testId script result
|
|
#
|
|
# Runs $script in the calling scope and compares its result to
|
|
# $result, minus any leading or trailing whitespace. If they differ,
|
|
# it triggers an [assert].
|
|
#
|
|
proc test-expect {testId script result} {
|
|
puts "test $testId"
|
|
set x [string trim [uplevel 1 $script]]
|
|
set result [string trim $result]
|
|
tailcall test__affert 0 [list "{$x}" eq "{$result}"] \
|
|
"\nEXPECTED: <<$result>>\nGOT: <<$x>>"
|
|
}
|
|
|
|
#
|
|
# @test-catch cmd ?...args?
|
|
#
|
|
# Runs [cmd ...args], repressing any exception except to possibly log
|
|
# the failure. Returns 1 if it caught anything, 0 if it didn't.
|
|
#
|
|
proc test-catch {cmd args} {
|
|
if {[catch {
|
|
uplevel 1 $cmd {*}$args
|
|
} rc xopts]} {
|
|
puts "[test-current-scope] ignoring failure of: $cmd [lindex $args 0]: $rc"
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|
|
|
|
#
|
|
# @test-catch-matching pattern (script|cmd args...)
|
|
#
|
|
# Works like test-catch, but it expects its argument(s) to to throw an
|
|
# error matching the given string (checked with [string match]). If
|
|
# they do not throw, or the error does not match $pattern, this
|
|
# function throws, else it returns 1.
|
|
#
|
|
# If there is no second argument, the $cmd is assumed to be a script,
|
|
# and will be eval'd in the caller's scope.
|
|
#
|
|
# TODO: add -glob and -regex flags to control matching flavor.
|
|
#
|
|
proc test-catch-matching {pattern cmd args} {
|
|
if {[catch {
|
|
#puts "**** catch-matching cmd=$cmd args=$args"
|
|
if {0 == [llength $args]} {
|
|
uplevel 1 $cmd {*}$args
|
|
} else {
|
|
$cmd {*}$args
|
|
}
|
|
} rc xopts]} {
|
|
if {[string match $pattern $rc]} {
|
|
return 1
|
|
} else {
|
|
error "[test-current-scope] exception does not match {$pattern}: {$rc}"
|
|
}
|
|
}
|
|
error "[test-current-scope] expecting to see an error matching {$pattern}"
|
|
}
|
|
|
|
if {![array exists ::teaish__BuildFlags]} {
|
|
array set ::teaish__BuildFlags {}
|
|
}
|
|
|
|
#
|
|
# @teaish-build-flag3 flag tgtVar ?dflt?
|
|
#
|
|
# If the current build has the configure-time flag named $flag set
|
|
# then tgtVar is assigned its value and 1 is returned, else tgtVal is
|
|
# assigned $dflt and 0 is returned.
|
|
#
|
|
# Caveat #1: only valid when called in the context of teaish's default
|
|
# "make test" recipe, e.g. from teaish.test.tcl. It is not valid from
|
|
# a teaish.tcl configure script because (A) the state it relies on
|
|
# doesn't fully exist at that point and (B) that level of the API has
|
|
# more direct access to the build state. This function requires that
|
|
# an external script have populated its internal state, which is
|
|
# normally handled via teaish.tester.tcl.in.
|
|
#
|
|
# Caveat #2: defines in the style of HAVE_FEATURENAME with a value of
|
|
# 0 are, by long-standing configure script conventions, treated as
|
|
# _undefined_ here.
|
|
#
|
|
proc teaish-build-flag3 {flag tgtVar {dflt ""}} {
|
|
upvar $tgtVar tgt
|
|
if {[info exists ::teaish__BuildFlags($flag)]} {
|
|
set tgt $::teaish__BuildFlags($flag)
|
|
return 1;
|
|
} elseif {0==[array size ::teaish__BuildFlags]} {
|
|
test-warn \
|
|
"\[[test-current-scope]] was called from " \
|
|
"[test-current-scope 1] without the build flags imported."
|
|
}
|
|
set tgt $dflt
|
|
return 0
|
|
}
|
|
|
|
#
|
|
# @teaish-build-flag flag ?dflt?
|
|
#
|
|
# Convenience form of teaish-build-flag3 which returns the
|
|
# configure-time-defined value of $flag or "" if it's not defined (or
|
|
# if it's an empty string).
|
|
#
|
|
proc teaish-build-flag {flag {dflt ""}} {
|
|
set tgt ""
|
|
teaish-build-flag3 $flag tgt $dflt
|
|
return $tgt
|
|
}
|