blob: d216d2a08de9fed529b17506dfae98452725f4b4 [file] [log] [blame]
#!/usr/bin/tclsh
#
# Documentation for this script. This may be output to stderr
# if the script is invoked incorrectly. See the [process_options]
# proc below.
#
set ::USAGE_MESSAGE {
This Tcl script is used to test the various configurations required
before releasing a new version. Supported command line options (all
optional) are:
--buildonly (Just build testfixture - do not run)
--config CONFIGNAME (Run only CONFIGNAME)
--dryrun (Print what would have happened)
-f|--force (Run even if uncommitted changes)
--info (Show diagnostic info)
--jobs N (Use N processes - default 1)
--keep (Delete no files after each test run)
--msvc (Use MSVC as the compiler)
--platform PLATFORM (see below)
--progress (Show progress messages)
--quick (Run "veryquick.test" only)
--veryquick (Run "make smoketest" only)
--with-tcl=DIR (Use TCL build at DIR)
The script determines the default value for --platform using the
$tcl_platform(os) and $tcl_platform(machine) variables. Supported
platforms are "Linux-x86", "Linux-x86_64", "Darwin-i386",
"Darwin-x86_64", "Windows NT-intel", and "Windows NT-amd64".
Every test begins with a fresh run of the configure script at the top
of the SQLite source tree.
}
# Return a timestamp of the form HH:MM:SS
#
proc now {} {
return [clock format [clock seconds] -format %H:%M:%S]
}
# Omit comments (text between # and \n) in a long multi-line string.
#
proc strip_comments {in} {
regsub -all {#[^\n]*\n} $in {} out
return $out
}
array set ::Configs [strip_comments {
"Default" {
-O2
--disable-amalgamation --disable-shared
--enable-session
-DSQLITE_ENABLE_DESERIALIZE
}
"Sanitize" {
CC=clang -fsanitize=undefined
-DSQLITE_ENABLE_STAT4
--enable-session
}
"Stdcall" {
-DUSE_STDCALL=1
-O2
}
"Have-Not" {
# The "Have-Not" configuration sets all possible -UHAVE_feature options
# in order to verify that the code works even on platforms that lack
# these support services.
-DHAVE_FDATASYNC=0
-DHAVE_GMTIME_R=0
-DHAVE_ISNAN=0
-DHAVE_LOCALTIME_R=0
-DHAVE_LOCALTIME_S=0
-DHAVE_MALLOC_USABLE_SIZE=0
-DHAVE_STRCHRNUL=0
-DHAVE_USLEEP=0
-DHAVE_UTIME=0
}
"Unlock-Notify" {
-O2
-DSQLITE_ENABLE_UNLOCK_NOTIFY
-DSQLITE_THREADSAFE
-DSQLITE_TCL_DEFAULT_FULLMUTEX=1
}
"User-Auth" {
-O2
-DSQLITE_USER_AUTHENTICATION=1
}
"Secure-Delete" {
-O2
-DSQLITE_SECURE_DELETE=1
-DSQLITE_SOUNDEX=1
}
"Update-Delete-Limit" {
-O2
-DSQLITE_DEFAULT_FILE_FORMAT=4
-DSQLITE_ENABLE_UPDATE_DELETE_LIMIT=1
-DSQLITE_ENABLE_STMT_SCANSTATUS
-DSQLITE_LIKE_DOESNT_MATCH_BLOBS
-DSQLITE_ENABLE_CURSOR_HINTS
--enable-json1
}
"Check-Symbols" {
-DSQLITE_MEMDEBUG=1
-DSQLITE_ENABLE_FTS3_PARENTHESIS=1
-DSQLITE_ENABLE_FTS3=1
-DSQLITE_ENABLE_RTREE=1
-DSQLITE_ENABLE_MEMSYS5=1
-DSQLITE_ENABLE_MEMSYS3=1
-DSQLITE_ENABLE_COLUMN_METADATA=1
-DSQLITE_ENABLE_UPDATE_DELETE_LIMIT=1
-DSQLITE_SECURE_DELETE=1
-DSQLITE_SOUNDEX=1
-DSQLITE_ENABLE_ATOMIC_WRITE=1
-DSQLITE_ENABLE_MEMORY_MANAGEMENT=1
-DSQLITE_ENABLE_OVERSIZE_CELL_CHECK=1
-DSQLITE_ENABLE_STAT4
-DSQLITE_ENABLE_STMT_SCANSTATUS
--enable-json1 --enable-fts5 --enable-session
}
"Debug-One" {
--disable-shared
-O2 -funsigned-char
-DSQLITE_DEBUG=1
-DSQLITE_MEMDEBUG=1
-DSQLITE_MUTEX_NOOP=1
-DSQLITE_TCL_DEFAULT_FULLMUTEX=1
-DSQLITE_ENABLE_FTS3=1
-DSQLITE_ENABLE_RTREE=1
-DSQLITE_ENABLE_MEMSYS5=1
-DSQLITE_ENABLE_COLUMN_METADATA=1
-DSQLITE_ENABLE_STAT4
-DSQLITE_ENABLE_HIDDEN_COLUMNS
-DSQLITE_MAX_ATTACHED=125
-DSQLITE_MUTATION_TEST
--enable-fts5 --enable-json1
}
"Fast-One" {
-O6
-DSQLITE_ENABLE_FTS4=1
-DSQLITE_ENABLE_RTREE=1
-DSQLITE_ENABLE_STAT4
-DSQLITE_ENABLE_RBU
-DSQLITE_MAX_ATTACHED=125
-DLONGDOUBLE_TYPE=double
--enable-session
}
"Device-One" {
-O2
-DSQLITE_DEBUG=1
-DSQLITE_DEFAULT_AUTOVACUUM=1
-DSQLITE_DEFAULT_CACHE_SIZE=64
-DSQLITE_DEFAULT_PAGE_SIZE=1024
-DSQLITE_DEFAULT_TEMP_CACHE_SIZE=32
-DSQLITE_DISABLE_LFS=1
-DSQLITE_ENABLE_ATOMIC_WRITE=1
-DSQLITE_ENABLE_IOTRACE=1
-DSQLITE_ENABLE_MEMORY_MANAGEMENT=1
-DSQLITE_MAX_PAGE_SIZE=4096
-DSQLITE_OMIT_LOAD_EXTENSION=1
-DSQLITE_OMIT_PROGRESS_CALLBACK=1
-DSQLITE_OMIT_VIRTUALTABLE=1
-DSQLITE_ENABLE_HIDDEN_COLUMNS
-DSQLITE_TEMP_STORE=3
--enable-json1
}
"Device-Two" {
-DSQLITE_4_BYTE_ALIGNED_MALLOC=1
-DSQLITE_DEFAULT_AUTOVACUUM=1
-DSQLITE_DEFAULT_CACHE_SIZE=1000
-DSQLITE_DEFAULT_LOCKING_MODE=0
-DSQLITE_DEFAULT_PAGE_SIZE=1024
-DSQLITE_DEFAULT_TEMP_CACHE_SIZE=1000
-DSQLITE_DISABLE_LFS=1
-DSQLITE_ENABLE_FTS3=1
-DSQLITE_ENABLE_MEMORY_MANAGEMENT=1
-DSQLITE_ENABLE_RTREE=1
-DSQLITE_MAX_COMPOUND_SELECT=50
-DSQLITE_MAX_PAGE_SIZE=32768
-DSQLITE_OMIT_TRACE=1
-DSQLITE_TEMP_STORE=3
-DSQLITE_THREADSAFE=2
-DSQLITE_ENABLE_DESERIALIZE=1
--enable-json1 --enable-fts5 --enable-session
}
"Locking-Style" {
-O2
-DSQLITE_ENABLE_LOCKING_STYLE=1
}
"Apple" {
-Os
-DHAVE_GMTIME_R=1
-DHAVE_ISNAN=1
-DHAVE_LOCALTIME_R=1
-DHAVE_PREAD=1
-DHAVE_PWRITE=1
-DHAVE_USLEEP=1
-DHAVE_USLEEP=1
-DHAVE_UTIME=1
-DSQLITE_DEFAULT_CACHE_SIZE=1000
-DSQLITE_DEFAULT_CKPTFULLFSYNC=1
-DSQLITE_DEFAULT_MEMSTATUS=1
-DSQLITE_DEFAULT_PAGE_SIZE=1024
-DSQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS=1
-DSQLITE_ENABLE_API_ARMOR=1
-DSQLITE_ENABLE_AUTO_PROFILE=1
-DSQLITE_ENABLE_FLOCKTIMEOUT=1
-DSQLITE_ENABLE_FTS3=1
-DSQLITE_ENABLE_FTS3_PARENTHESIS=1
-DSQLITE_ENABLE_FTS3_TOKENIZER=1
if:os=="Darwin" -DSQLITE_ENABLE_LOCKING_STYLE=1
-DSQLITE_ENABLE_PERSIST_WAL=1
-DSQLITE_ENABLE_PURGEABLE_PCACHE=1
-DSQLITE_ENABLE_RTREE=1
-DSQLITE_ENABLE_SNAPSHOT=1
# -DSQLITE_ENABLE_SQLLOG=1
-DSQLITE_ENABLE_UPDATE_DELETE_LIMIT=1
-DSQLITE_MAX_LENGTH=2147483645
-DSQLITE_MAX_VARIABLE_NUMBER=500000
# -DSQLITE_MEMDEBUG=1
-DSQLITE_NO_SYNC=1
-DSQLITE_OMIT_AUTORESET=1
-DSQLITE_OMIT_LOAD_EXTENSION=1
-DSQLITE_PREFER_PROXY_LOCKING=1
-DSQLITE_SERIES_CONSTRAINT_VERIFY=1
-DSQLITE_THREADSAFE=2
-DSQLITE_USE_URI=1
-DSQLITE_WRITE_WALFRAME_PREBUFFERED=1
-DUSE_GUARDED_FD=1
-DUSE_PREAD=1
--enable-json1 --enable-fts5
}
"Extra-Robustness" {
-DSQLITE_ENABLE_OVERSIZE_CELL_CHECK=1
-DSQLITE_MAX_ATTACHED=62
}
"Devkit" {
-DSQLITE_DEFAULT_FILE_FORMAT=4
-DSQLITE_MAX_ATTACHED=30
-DSQLITE_ENABLE_COLUMN_METADATA
-DSQLITE_ENABLE_FTS4
-DSQLITE_ENABLE_FTS5
-DSQLITE_ENABLE_FTS4_PARENTHESIS
-DSQLITE_DISABLE_FTS4_DEFERRED
-DSQLITE_ENABLE_RTREE
--enable-json1 --enable-fts5
}
"No-lookaside" {
-DSQLITE_TEST_REALLOC_STRESS=1
-DSQLITE_OMIT_LOOKASIDE=1
-DHAVE_USLEEP=1
}
"Valgrind" {
-DSQLITE_ENABLE_STAT4
-DSQLITE_ENABLE_FTS4
-DSQLITE_ENABLE_RTREE
-DSQLITE_ENABLE_HIDDEN_COLUMNS
--enable-json1
}
# The next group of configurations are used only by the
# Failure-Detection platform. They are all the same, but we need
# different names for them all so that they results appear in separate
# subdirectories.
#
Fail0 {-O0}
Fail2 {-O0}
Fail3 {-O0}
Fail4 {-O0}
FuzzFail1 {-O0}
FuzzFail2 {-O0}
}]
array set ::Platforms [strip_comments {
Linux-x86_64 {
"Check-Symbols" checksymbols
"Fast-One" "fuzztest test"
"Debug-One" "mptest test"
"Have-Not" test
"Secure-Delete" test
"Unlock-Notify" "QUICKTEST_INCLUDE=notify2.test test"
"User-Auth" tcltest
"Update-Delete-Limit" test
"Extra-Robustness" test
"Device-Two" test
"No-lookaside" test
"Devkit" test
"Apple" test
"Sanitize" {QUICKTEST_OMIT=func4.test,nan.test test}
"Device-One" fulltest
"Default" "threadtest fulltest"
"Valgrind" valgrindtest
}
Linux-i686 {
"Devkit" test
"Have-Not" test
"Unlock-Notify" "QUICKTEST_INCLUDE=notify2.test test"
"Device-One" test
"Device-Two" test
"Default" "threadtest fulltest"
}
Darwin-i386 {
"Locking-Style" "mptest test"
"Have-Not" test
"Apple" "threadtest fulltest"
}
Darwin-x86_64 {
"Locking-Style" "mptest test"
"Have-Not" test
"Apple" "threadtest fulltest"
}
"Windows NT-intel" {
"Stdcall" test
"Have-Not" test
"Default" "mptest fulltestonly"
}
"Windows NT-amd64" {
"Stdcall" test
"Have-Not" test
"Default" "mptest fulltestonly"
}
# The Failure-Detection platform runs various tests that deliberately
# fail. This is used as a test of this script to verify that this script
# correctly identifies failures.
#
Failure-Detection {
Fail0 "TEST_FAILURE=0 test"
Sanitize "TEST_FAILURE=1 test"
Fail2 "TEST_FAILURE=2 valgrindtest"
Fail3 "TEST_FAILURE=3 valgrindtest"
Fail4 "TEST_FAILURE=4 test"
FuzzFail1 "TEST_FAILURE=5 test"
FuzzFail2 "TEST_FAILURE=5 valgrindtest"
}
}]
# End of configuration section.
#########################################################################
#########################################################################
# Configuration verification: Check that each entry in the list of configs
# specified for each platforms exists.
#
foreach {key value} [array get ::Platforms] {
foreach {v t} $value {
if {0==[info exists ::Configs($v)]} {
puts stderr "No such configuration: \"$v\""
exit -1
}
}
}
# Output log. Disabled for slave interpreters.
#
if {[lindex $argv end]!="--slave"} {
set LOG [open releasetest-out.txt w]
proc PUTS {txt} {
puts $txt
puts $::LOG $txt
flush $::LOG
}
proc PUTSNNL {txt} {
puts -nonewline $txt
puts -nonewline $::LOG $txt
flush $::LOG
}
proc PUTSERR {txt} {
puts stderr $txt
puts $::LOG $txt
flush $::LOG
}
puts $LOG "$argv0 $argv"
set tm0 [clock format [clock seconds] -format {%Y-%m-%d %H:%M:%S} -gmt 1]
puts $LOG "start-time: $tm0 UTC"
} else {
proc PUTS {txt} {
puts $txt
}
proc PUTSNNL {txt} {
puts -nonewline $txt
}
proc PUTSERR {txt} {
puts stderr $txt
}
}
# Open the file $logfile and look for a report on the number of errors
# and the number of test cases run. Add these values to the global
# $::NERRCASE and $::NTESTCASE variables.
#
# If any errors occur, then write into $errmsgVar the text of an appropriate
# one-line error message to show on the output.
#
proc count_tests_and_errors {logfile rcVar errmsgVar} {
if {$::DRYRUN} return
upvar 1 $rcVar rc $errmsgVar errmsg
set fd [open $logfile rb]
set seen 0
while {![eof $fd]} {
set line [gets $fd]
if {[regexp {(\d+) errors out of (\d+) tests} $line all nerr ntest]} {
incr ::NERRCASE $nerr
incr ::NTESTCASE $ntest
set seen 1
if {$nerr>0} {
set rc 1
set errmsg $line
}
}
if {[regexp {runtime error: +(.*)} $line all msg]} {
# skip over "value is outside range" errors
if {[regexp {value .* is outside the range of representable} $line]} {
# noop
} else {
incr ::NERRCASE
if {$rc==0} {
set rc 1
set errmsg $msg
}
}
}
if {[regexp {fatal error +(.*)} $line all msg]} {
incr ::NERRCASE
if {$rc==0} {
set rc 1
set errmsg $msg
}
}
if {[regexp {ERROR SUMMARY: (\d+) errors.*} $line all cnt] && $cnt>0} {
incr ::NERRCASE
if {$rc==0} {
set rc 1
set errmsg $all
}
}
if {[regexp {^VERSION: 3\.\d+.\d+} $line]} {
set v [string range $line 9 end]
if {$::SQLITE_VERSION eq ""} {
set ::SQLITE_VERSION $v
} elseif {$::SQLITE_VERSION ne $v} {
set rc 1
set errmsg "version conflict: {$::SQLITE_VERSION} vs. {$v}"
}
}
}
close $fd
if {$::BUILDONLY} {
incr ::NTESTCASE
if {$rc!=0} {
set errmsg "Build failed"
}
} elseif {!$seen} {
set rc 1
set errmsg "Test did not complete"
if {[file readable core]} {
append errmsg " - core file exists"
}
}
}
#--------------------------------------------------------------------------
# This command is invoked as the [main] routine for scripts run with the
# "--slave" option.
#
# For each test (i.e. "configure && make test" execution), the master
# process spawns a process with the --slave option. It writes two lines
# to the slaves stdin. The first contains a single boolean value - the
# value of ::TRACE to use in the slave script. The second line contains a
# list in the same format as each element of the list passed to the
# [run_all_test_suites] command in the master process.
#
# The slave then runs the "configure && make test" commands specified. It
# exits successfully if the tests passes, or with a non-zero error code
# otherwise.
#
proc run_slave_test {} {
# Read global vars configuration from stdin.
set V [gets stdin]
foreach {::TRACE ::MSVC ::DRYRUN ::KEEPFILES} $V {}
# Read the test-suite configuration from stdin.
set T [gets stdin]
foreach {title dir configOpts testtarget makeOpts cflags opts} $T {}
# Create and switch to the test directory.
set normaldir [file normalize $dir]
set ::env(SQLITE_TMPDIR) $normaldir
trace_cmd file mkdir $dir
trace_cmd cd $dir
catch {file delete core}
catch {file delete test.log}
# Run the "./configure && make" commands.
set rc 0
set rc [catch [configureCommand $configOpts]]
if {!$rc} {
if {[info exists ::env(TCLSH_CMD)]} {
set savedEnv(TCLSH_CMD) $::env(TCLSH_CMD)
} else {
unset -nocomplain savedEnv(TCLSH_CMD)
}
set ::env(TCLSH_CMD) [file nativename [info nameofexecutable]]
# Create a file called "makecommand.sh" containing the text of
# the make command line.
catch {
set cmd [makeCommand $testtarget $makeOpts $cflags $opts]
set fd [open makecommand.sh w]
foreach e $cmd {
if {[string first " " $e]>=0} {
puts -nonewline $fd "\"$e\""
} else {
puts -nonewline $fd $e
}
puts -nonewline $fd " "
}
puts $fd ""
close $fd
} msg
# Run the make command.
set rc [catch {trace_cmd exec {*}$cmd >>& test.log} msg]
if {[info exists savedEnv(TCLSH_CMD)]} {
set ::env(TCLSH_CMD) $savedEnv(TCLSH_CMD)
} else {
unset -nocomplain ::env(TCLSH_CMD)
}
}
# Clean up lots of extra files if --keep was not specified.
if {$::KEEPFILES==0} { cleanup $normaldir }
# Exis successfully if the test passed, or with a non-zero error code
# otherwise.
exit $rc
}
# This command is invoked in the master process each time a slave
# file-descriptor is readable.
#
proc slave_fileevent {fd T tm1} {
global G
foreach {title dir configOpts testtarget makeOpts cflags opts} $T {}
if {[eof $fd]} {
fconfigure $fd -blocking 1
set rc [catch { close $fd }]
set errmsg {}
set logfile [file join $dir test.log]
if {[file exists $logfile]} {
count_tests_and_errors [file join $dir test.log] rc errmsg
} elseif {$rc==0 && !$::DRYRUN} {
set rc 1
set errmsg "no test.log file..."
}
if {!$::TRACE} {
set tm2 [clock seconds]
set hours [expr {($tm2-$tm1)/3600}]
set minutes [expr {(($tm2-$tm1)/60)%60}]
set seconds [expr {($tm2-$tm1)%60}]
set tm [format (%02d:%02d:%02d) $hours $minutes $seconds]
if {$rc} {
set status FAIL
incr ::NERR
} else {
set status Ok
}
set n [string length $title]
if {$::PROGRESS_MSGS} {
PUTS "finished: ${title}[string repeat . [expr {53-$n}]] $status $tm"
} else {
PUTS "${title}[string repeat . [expr {63-$n}]] $status $tm"
}
if {$errmsg!=""} {PUTS " $errmsg"}
flush stdout
}
incr G(nJob) -1
} else {
set line [gets $fd]
if {[string trim $line] != ""} {
puts "Trace : $title - \"$line\""
}
}
}
#--------------------------------------------------------------------------
# The only argument passed to this function is a list of test-suites to
# run. Each "test-suite" is itself a list consisting of the following
# elements:
#
# * Test title (for display).
# * The name of the directory to run the test in.
# * The argument for [configureCommand]
# * The first argument for [makeCommand]
# * The second argument for [makeCommand]
# * The third argument for [makeCommand]
#
proc run_all_test_suites {alltests} {
global G
set tests $alltests
set G(nJob) 0
while {[llength $tests]>0 || $G(nJob)>0} {
if {$G(nJob)>=$::JOBS || [llength $tests]==0} {
vwait G(nJob)
}
if {[llength $tests]>0} {
set T [lindex $tests 0]
set tests [lrange $tests 1 end]
foreach {title dir configOpts testtarget makeOpts cflags opts} $T {}
if {$::PROGRESS_MSGS && !$::TRACE} {
set n [string length $title]
PUTS "starting: ${title} at [now]"
flush stdout
}
# Run the job.
#
set tm1 [clock seconds]
incr G(nJob)
set script [file normalize [info script]]
set fd [open "|[info nameofexecutable] $script --slave" r+]
fconfigure $fd -blocking 0
fileevent $fd readable [list slave_fileevent $fd $T $tm1]
puts $fd [list $::TRACE $::MSVC $::DRYRUN $::KEEPFILES]
puts $fd [list {*}$T]
flush $fd
}
}
}
proc add_test_suite {listvar name testtarget config} {
upvar $listvar alltests
# Tcl variable $opts is used to build up the value used to set the
# OPTS Makefile variable. Variable $cflags holds the value for
# CFLAGS. The makefile will pass OPTS to both gcc and lemon, but
# CFLAGS is only passed to gcc.
#
set makeOpts ""
set cflags [expr {$::MSVC ? "-Zi" : "-g"}]
set opts ""
set title ${name}($testtarget)
set configOpts $::WITHTCL
set skip 0
regsub -all {#[^\n]*\n} $config \n config
foreach arg $config {
if {$skip} {
set skip 0
continue
}
if {[regexp {^-[UD]} $arg]} {
lappend opts $arg
} elseif {[regexp {^[A-Z]+=} $arg]} {
lappend testtarget $arg
} elseif {[regexp {^if:([a-z]+)(.*)} $arg all key tail]} {
# Arguments of the form 'if:os=="Linux"' will cause the subsequent
# argument to be skipped if the $tcl_platform(os) is not "Linux", for
# example...
set skip [expr !(\$::tcl_platform($key)$tail)]
} elseif {[regexp {^--(enable|disable)-} $arg]} {
if {$::MSVC} {
if {$arg eq "--disable-amalgamation"} {
lappend makeOpts USE_AMALGAMATION=0
continue
}
if {$arg eq "--disable-shared"} {
lappend makeOpts USE_CRT_DLL=0 DYNAMIC_SHELL=0
continue
}
if {$arg eq "--enable-fts5"} {
lappend opts -DSQLITE_ENABLE_FTS5
continue
}
if {$arg eq "--enable-json1"} {
lappend opts -DSQLITE_ENABLE_JSON1
continue
}
if {$arg eq "--enable-shared"} {
lappend makeOpts USE_CRT_DLL=1 DYNAMIC_SHELL=1
continue
}
}
lappend configOpts $arg
} else {
if {$::MSVC} {
if {$arg eq "-g"} {
lappend cflags -Zi
continue
}
if {[regexp -- {^-O(\d+)$} $arg all level]} then {
lappend makeOpts OPTIMIZATIONS=$level
continue
}
}
lappend cflags $arg
}
}
# Disable sync to make testing faster.
#
lappend opts -DSQLITE_NO_SYNC=1
# Some configurations already set HAVE_USLEEP; in that case, skip it.
#
if {[lsearch -regexp $opts {^-DHAVE_USLEEP(?:=|$)}]==-1} {
lappend opts -DHAVE_USLEEP=1
}
# Add the define for this platform.
#
if {$::tcl_platform(platform)=="windows"} {
lappend opts -DSQLITE_OS_WIN=1
} else {
lappend opts -DSQLITE_OS_UNIX=1
}
# Set the sub-directory to use.
#
set dir [string tolower [string map {- _ " " _} $name]]
# Join option lists into strings, using space as delimiter.
#
set makeOpts [join $makeOpts " "]
set cflags [join $cflags " "]
set opts [join $opts " "]
lappend alltests [list \
$title $dir $configOpts $testtarget $makeOpts $cflags $opts]
}
# The following procedure returns the "configure" command to be exectued for
# the current platform, which may be Windows (via MinGW, etc).
#
proc configureCommand {opts} {
if {$::MSVC} return [list]; # This is not needed for MSVC.
set result [list trace_cmd exec]
if {$::tcl_platform(platform)=="windows"} {
lappend result sh
}
lappend result $::SRCDIR/configure --enable-load-extension
foreach x $opts {lappend result $x}
lappend result >& test.log
}
# The following procedure returns the "make" command to be executed for the
# specified targets, compiler flags, and options.
#
proc makeCommand { targets makeOpts cflags opts } {
set result [list]
if {$::MSVC} {
set nmakeDir [file nativename $::SRCDIR]
set nmakeFile [file nativename [file join $nmakeDir Makefile.msc]]
lappend result nmake /f $nmakeFile TOP=$nmakeDir
set tclDir [file nativename [file normalize \
[file dirname [file dirname [info nameofexecutable]]]]]
lappend result "TCLDIR=$tclDir"
if {[regexp {USE_STDCALL=1} $cflags]} {
lappend result USE_STDCALL=1
}
} else {
lappend result make
}
foreach makeOpt $makeOpts {
lappend result $makeOpt
}
lappend result clean
foreach target $targets {
lappend result $target
}
lappend result CFLAGS=$cflags OPTS=$opts
}
# The following procedure prints its arguments if ::TRACE is true.
# And it executes the command of its arguments in the calling context
# if ::DRYRUN is false.
#
proc trace_cmd {args} {
if {$::TRACE} {
PUTS $args
}
set res ""
if {!$::DRYRUN} {
set res [uplevel 1 $args]
}
return $res
}
# This proc processes the command line options passed to this script.
# Currently the only option supported is "-makefile", default
# "releasetest.mk". Set the ::MAKEFILE variable to the value of this
# option.
#
proc process_options {argv} {
set ::SRCDIR [file normalize [file dirname [file dirname $::argv0]]]
set ::QUICK 0
set ::MSVC 0
set ::BUILDONLY 0
set ::DRYRUN 0
set ::TRACE 0
set ::JOBS 1
set ::PROGRESS_MSGS 0
set ::WITHTCL {}
set ::FORCE 0
set ::KEEPFILES 0 ;# Keep extra files after test run
set config {}
set platform $::tcl_platform(os)-$::tcl_platform(machine)
for {set i 0} {$i < [llength $argv]} {incr i} {
set x [lindex $argv $i]
if {[regexp {^--[a-z]} $x]} {set x [string range $x 1 end]}
switch -glob -- $x {
-slave {
run_slave_test
exit
}
# Undocumented legacy option: --srcdir DIRECTORY
#
# DIRECTORY is the root of the SQLite checkout. This sets the
# SRCDIR global variable. But that variable is already set
# automatically so there really is no reason to have this option.
#
-srcdir {
incr i
set ::SRCDIR [file normalize [lindex $argv $i]]
}
-platform {
incr i
set platform [lindex $argv $i]
}
-jobs {
incr i
set ::JOBS [lindex $argv $i]
}
-progress {
set ::PROGRESS_MSGS 1
}
-quick {
set ::QUICK 1
}
-veryquick {
set ::QUICK 2
}
-config {
incr i
set config [lindex $argv $i]
}
-msvc {
set ::MSVC 1
}
-buildonly {
set ::BUILDONLY 1
}
-dryrun {
set ::DRYRUN 1
}
-force -
-f {
set ::FORCE 1
}
-trace {
set ::TRACE 1
}
-info {
PUTS "Command-line Options:"
PUTS " --srcdir $::SRCDIR"
PUTS " --platform [list $platform]"
PUTS " --config [list $config]"
if {$::QUICK} {
if {$::QUICK==1} {PUTS " --quick"}
if {$::QUICK==2} {PUTS " --veryquick"}
}
if {$::MSVC} {PUTS " --msvc"}
if {$::BUILDONLY} {PUTS " --buildonly"}
if {$::DRYRUN} {PUTS " --dryrun"}
if {$::TRACE} {PUTS " --trace"}
PUTS "\nAvailable --platform options:"
foreach y [lsort [array names ::Platforms]] {
PUTS " [list $y]"
}
PUTS "\nAvailable --config options:"
foreach y [lsort [array names ::Configs]] {
PUTS " [list $y]"
}
exit
}
-g {
lappend ::EXTRACONFIG [lindex $argv $i]
}
-keep {
set ::KEEPFILES 1
}
-with-tcl=* {
set ::WITHTCL -$x
}
-D* -
-O* -
-enable-* -
-disable-* -
*=* {
lappend ::EXTRACONFIG [lindex $argv $i]
}
default {
PUTSERR ""
PUTSERR [string trim $::USAGE_MESSAGE]
exit -1
}
}
}
if {0==[info exists ::Platforms($platform)]} {
PUTS "Unknown platform: $platform"
PUTSNNL "Set the -platform option to "
set print [list]
foreach p [array names ::Platforms] {
lappend print "\"$p\""
}
lset print end "or [lindex $print end]"
PUTS "[join $print {, }]."
exit
}
if {$config!=""} {
if {[llength $config]==1} {lappend config fulltest}
set ::CONFIGLIST $config
} else {
if {$::JOBS>1} {
set ::CONFIGLIST {}
foreach {target zConfig} [lreverse $::Platforms($platform)] {
append ::CONFIGLIST [format " %-25s %s\n" \
[list $zConfig] [list $target]]
}
} else {
set ::CONFIGLIST $::Platforms($platform)
}
}
PUTS "Running the following test configurations for $platform:"
PUTS " [string trim $::CONFIGLIST]"
PUTSNNL "Flags:"
if {$::PROGRESS_MSGS} {PUTSNNL " --progress"}
if {$::DRYRUN} {PUTSNNL " --dryrun"}
if {$::BUILDONLY} {PUTSNNL " --buildonly"}
if {$::MSVC} {PUTSNNL " --msvc"}
switch -- $::QUICK {
1 {PUTSNNL " --quick"}
2 {PUTSNNL " --veryquick"}
}
if {$::JOBS>1} {PUTSNNL " --jobs $::JOBS"}
PUTS ""
}
# Check to see if there are uncommitted changes in the SQLite source
# checkout. Exit if there are. Except: Do nothing if the --force
# flag is used. Also, ignore this test if the fossil binary is
# unavailable, or if the source tree is not a valid fossil checkout.
#
proc check_uncommitted {} {
if {$::FORCE} return
set pwd [pwd]
cd $::SRCDIR
if {[catch {exec fossil changes} res]==0 && [string trim $res]!=""} {
puts "ERROR: The check-out contains uncommitted changes:"
puts $res
puts "Use the -f or --force options to override"
exit 1
}
cd $pwd
}
# A test run has just finished in directory $dir. This command deletes all
# non-essential files from the directory. Specifically, everything except
#
# * The "testfixture" and "sqlite3" binaries,
# * The "test-out.log" and "test.log" log files.
#
proc cleanup {dir} {
set K(testfixture) 1
set K(testfixture.exe) 1
set K(sqlite3) 1
set K(sqlite3.exe) 1
set K(test-out.txt) 1
set K(test.log) 1
foreach f [glob -nocomplain [file join $dir *]] {
set tail [file tail $f]
if {[info exists K($tail)]==0} {
file delete -force $f
}
}
}
# Main routine.
#
proc main {argv} {
# Process any command line options.
set ::EXTRACONFIG {}
process_options $argv
if {!$::DRYRUN} check_uncommitted
PUTS [string repeat * 79]
set ::NERR 0
set ::NTEST 0
set ::NTESTCASE 0
set ::NERRCASE 0
set ::SQLITE_VERSION {}
set STARTTIME [clock seconds]
foreach {zConfig target} $::CONFIGLIST {
if {$::MSVC && ($zConfig eq "Sanitize" || "checksymbols" in $target
|| "valgrindtest" in $target)} {
PUTS "Skipping $zConfig / $target for MSVC..."
continue
}
if {$target ne "checksymbols"} {
switch -- $::QUICK {
1 {set target quicktest}
2 {set target smoketest}
}
if {$::BUILDONLY} {
set target testfixture
if {$::tcl_platform(platform)=="windows"} {
append target .exe
}
}
}
set config_options [concat $::Configs($zConfig) $::EXTRACONFIG]
incr NTEST
add_test_suite all $zConfig $target $config_options
# If the configuration included the SQLITE_DEBUG option, then remove
# it and run veryquick.test. If it did not include the SQLITE_DEBUG option
# add it and run veryquick.test.
if {$target!="checksymbols" && $target!="valgrindtest"
&& $target!="fuzzoomtest" && !$::BUILDONLY && $::QUICK<2} {
set debug_idx [lsearch -glob $config_options -DSQLITE_DEBUG*]
set xtarget $target
regsub -all {fulltest[a-z]*} $xtarget test xtarget
regsub -all {fuzzoomtest} $xtarget fuzztest xtarget
if {$debug_idx < 0} {
incr NTEST
append config_options " -DSQLITE_DEBUG=1 -DSQLITE_EXTRA_IFNULLROW=1"
add_test_suite all "${zConfig}_debug" $xtarget $config_options
} else {
incr NTEST
regsub { *-DSQLITE_MEMDEBUG[^ ]* *} $config_options { } config_options
regsub { *-DSQLITE_DEBUG[^ ]* *} $config_options { } config_options
add_test_suite all "${zConfig}_ndebug" $xtarget $config_options
}
}
}
run_all_test_suites $all
set elapsetime [expr {[clock seconds]-$STARTTIME}]
set hr [expr {$elapsetime/3600}]
set min [expr {($elapsetime/60)%60}]
set sec [expr {$elapsetime%60}]
set etime [format (%02d:%02d:%02d) $hr $min $sec]
if {$::JOBS>1} {append etime " $::JOBS cores"}
if {[catch {exec hostname} HNAME]==0} {append etime " on $HNAME"}
PUTS [string repeat * 79]
incr ::NERRCASE $::NERR
PUTS "$::NERRCASE failures out of $::NTESTCASE tests in $etime"
if {$::SQLITE_VERSION ne ""} {
PUTS "SQLite $::SQLITE_VERSION"
}
}
main $argv