github.com/jdgcs/sqlite3@v1.12.1-0.20210908114423-bc5f96e4dd51/testdata/tcl/releasetest.tcl (about)

     1  #!/usr/bin/tclsh
     2  #
     3  # Documentation for this script. This may be output to stderr
     4  # if the script is invoked incorrectly. See the [process_options]
     5  # proc below.
     6  #
     7  set ::USAGE_MESSAGE {
     8  This Tcl script is used to test the various configurations required
     9  before releasing a new version. Supported command line options (all
    10  optional) are:
    11  
    12      --buildonly                        (Just build testfixture - do not run)
    13      --config   CONFIGNAME              (Run only CONFIGNAME)
    14      --dryrun                           (Print what would have happened)
    15      -f|--force                         (Run even if uncommitted changes)
    16      --info                             (Show diagnostic info)
    17      --jobs     N                       (Use N processes - default 1)
    18      --keep                             (Delete no files after each test run)
    19      --msvc                             (Use MSVC as the compiler)
    20      --platform PLATFORM                (see below)
    21      --progress                         (Show progress messages)
    22      --quick                            (Run "veryquick.test" only)
    23      --veryquick                        (Run "make smoketest" only)
    24      --with-tcl=DIR                     (Use TCL build at DIR)
    25  
    26  The script determines the default value for --platform using the
    27  $tcl_platform(os) and $tcl_platform(machine) variables.  Supported
    28  platforms are "Linux-x86", "Linux-x86_64", "Darwin-i386",
    29  "Darwin-x86_64", "Windows NT-intel", and "Windows NT-amd64".
    30  
    31  Every test begins with a fresh run of the configure script at the top
    32  of the SQLite source tree.
    33  }
    34  
    35  # Return a timestamp of the form HH:MM:SS
    36  #
    37  proc now {} {
    38    return [clock format [clock seconds] -format %H:%M:%S]
    39  }
    40  
    41  # Omit comments (text between # and \n) in a long multi-line string.
    42  #
    43  proc strip_comments {in} {
    44    regsub -all {#[^\n]*\n} $in {} out
    45    return $out
    46  }
    47  
    48  array set ::Configs [strip_comments {
    49    "Default" {
    50      -O2
    51      --disable-amalgamation --disable-shared
    52      --enable-session
    53    }
    54    "Sanitize" {
    55      CC=clang -fsanitize=undefined
    56      -DSQLITE_ENABLE_STAT4
    57      -DCONFIG_SLOWDOWN_FACTOR=5.0
    58      --enable-session
    59    }
    60    "Stdcall" {
    61      -DUSE_STDCALL=1
    62      -O2
    63    }
    64    "Have-Not" {
    65      # The "Have-Not" configuration sets all possible -UHAVE_feature options
    66      # in order to verify that the code works even on platforms that lack
    67      # these support services.
    68      -DHAVE_FDATASYNC=0
    69      -DHAVE_GMTIME_R=0
    70      -DHAVE_ISNAN=0
    71      -DHAVE_LOCALTIME_R=0
    72      -DHAVE_LOCALTIME_S=0
    73      -DHAVE_MALLOC_USABLE_SIZE=0
    74      -DHAVE_STRCHRNUL=0
    75      -DHAVE_USLEEP=0
    76      -DHAVE_UTIME=0
    77    }
    78    "Unlock-Notify" {
    79      -O2
    80      -DSQLITE_ENABLE_UNLOCK_NOTIFY
    81      -DSQLITE_THREADSAFE
    82      -DSQLITE_TCL_DEFAULT_FULLMUTEX=1
    83    }
    84    "User-Auth" {
    85      -O2
    86      -DSQLITE_USER_AUTHENTICATION=1
    87    }
    88    "Secure-Delete" {
    89      -O2
    90      -DSQLITE_SECURE_DELETE=1
    91      -DSQLITE_SOUNDEX=1
    92    }
    93    "Update-Delete-Limit" {
    94      -O2
    95      -DSQLITE_DEFAULT_FILE_FORMAT=4
    96      -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT=1
    97      -DSQLITE_ENABLE_STMT_SCANSTATUS
    98      -DSQLITE_LIKE_DOESNT_MATCH_BLOBS
    99      -DSQLITE_ENABLE_CURSOR_HINTS
   100      --enable-json1
   101    }
   102    "Check-Symbols" {
   103      -DSQLITE_MEMDEBUG=1
   104      -DSQLITE_ENABLE_FTS3_PARENTHESIS=1
   105      -DSQLITE_ENABLE_FTS3=1
   106      -DSQLITE_ENABLE_RTREE=1
   107      -DSQLITE_ENABLE_MEMSYS5=1
   108      -DSQLITE_ENABLE_MEMSYS3=1
   109      -DSQLITE_ENABLE_COLUMN_METADATA=1
   110      -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT=1
   111      -DSQLITE_SECURE_DELETE=1
   112      -DSQLITE_SOUNDEX=1
   113      -DSQLITE_ENABLE_ATOMIC_WRITE=1
   114      -DSQLITE_ENABLE_MEMORY_MANAGEMENT=1
   115      -DSQLITE_ENABLE_OVERSIZE_CELL_CHECK=1
   116      -DSQLITE_ENABLE_STAT4
   117      -DSQLITE_ENABLE_STMT_SCANSTATUS
   118      --enable-json1 --enable-fts5 --enable-session
   119    }
   120    "Debug-One" {
   121      --disable-shared
   122      -O2 -funsigned-char
   123      -DSQLITE_DEBUG=1
   124      -DSQLITE_MEMDEBUG=1
   125      -DSQLITE_MUTEX_NOOP=1
   126      -DSQLITE_TCL_DEFAULT_FULLMUTEX=1
   127      -DSQLITE_ENABLE_FTS3=1
   128      -DSQLITE_ENABLE_RTREE=1
   129      -DSQLITE_ENABLE_MEMSYS5=1
   130      -DSQLITE_ENABLE_COLUMN_METADATA=1
   131      -DSQLITE_ENABLE_STAT4
   132      -DSQLITE_ENABLE_HIDDEN_COLUMNS
   133      -DSQLITE_MAX_ATTACHED=125
   134      -DSQLITE_MUTATION_TEST
   135      --enable-fts5 --enable-json1
   136    }
   137    "Fast-One" {
   138      -O6
   139      -DSQLITE_ENABLE_FTS4=1
   140      -DSQLITE_ENABLE_RTREE=1
   141      -DSQLITE_ENABLE_STAT4
   142      -DSQLITE_ENABLE_RBU
   143      -DSQLITE_MAX_ATTACHED=125
   144      -DLONGDOUBLE_TYPE=double
   145      --enable-session
   146    }
   147    "Device-One" {
   148      -O2
   149      -DSQLITE_DEBUG=1
   150      -DSQLITE_DEFAULT_AUTOVACUUM=1
   151      -DSQLITE_DEFAULT_CACHE_SIZE=64
   152      -DSQLITE_DEFAULT_PAGE_SIZE=1024
   153      -DSQLITE_DEFAULT_TEMP_CACHE_SIZE=32
   154      -DSQLITE_DISABLE_LFS=1
   155      -DSQLITE_ENABLE_ATOMIC_WRITE=1
   156      -DSQLITE_ENABLE_IOTRACE=1
   157      -DSQLITE_ENABLE_MEMORY_MANAGEMENT=1
   158      -DSQLITE_MAX_PAGE_SIZE=4096
   159      -DSQLITE_OMIT_LOAD_EXTENSION=1
   160      -DSQLITE_OMIT_PROGRESS_CALLBACK=1
   161      -DSQLITE_OMIT_VIRTUALTABLE=1
   162      -DSQLITE_ENABLE_HIDDEN_COLUMNS
   163      -DSQLITE_TEMP_STORE=3
   164      --enable-json1
   165    }
   166    "Device-Two" {
   167      -DSQLITE_4_BYTE_ALIGNED_MALLOC=1
   168      -DSQLITE_DEFAULT_AUTOVACUUM=1
   169      -DSQLITE_DEFAULT_CACHE_SIZE=1000
   170      -DSQLITE_DEFAULT_LOCKING_MODE=0
   171      -DSQLITE_DEFAULT_PAGE_SIZE=1024
   172      -DSQLITE_DEFAULT_TEMP_CACHE_SIZE=1000
   173      -DSQLITE_DISABLE_LFS=1
   174      -DSQLITE_ENABLE_FTS3=1
   175      -DSQLITE_ENABLE_MEMORY_MANAGEMENT=1
   176      -DSQLITE_ENABLE_RTREE=1
   177      -DSQLITE_MAX_COMPOUND_SELECT=50
   178      -DSQLITE_MAX_PAGE_SIZE=32768
   179      -DSQLITE_OMIT_TRACE=1
   180      -DSQLITE_TEMP_STORE=3
   181      -DSQLITE_THREADSAFE=2
   182      --enable-json1 --enable-fts5 --enable-session
   183    }
   184    "Locking-Style" {
   185      -O2
   186      -DSQLITE_ENABLE_LOCKING_STYLE=1
   187    }
   188    "Apple" {
   189      -Os
   190      -DHAVE_GMTIME_R=1
   191      -DHAVE_ISNAN=1
   192      -DHAVE_LOCALTIME_R=1
   193      -DHAVE_PREAD=1
   194      -DHAVE_PWRITE=1
   195      -DHAVE_USLEEP=1
   196      -DHAVE_USLEEP=1
   197      -DHAVE_UTIME=1
   198      -DSQLITE_DEFAULT_CACHE_SIZE=1000
   199      -DSQLITE_DEFAULT_CKPTFULLFSYNC=1
   200      -DSQLITE_DEFAULT_MEMSTATUS=1
   201      -DSQLITE_DEFAULT_PAGE_SIZE=1024
   202      -DSQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS=1
   203      -DSQLITE_ENABLE_API_ARMOR=1
   204      -DSQLITE_ENABLE_AUTO_PROFILE=1
   205      -DSQLITE_ENABLE_FLOCKTIMEOUT=1
   206      -DSQLITE_ENABLE_FTS3=1
   207      -DSQLITE_ENABLE_FTS3_PARENTHESIS=1
   208      -DSQLITE_ENABLE_FTS3_TOKENIZER=1
   209      if:os=="Darwin" -DSQLITE_ENABLE_LOCKING_STYLE=1
   210      -DSQLITE_ENABLE_PERSIST_WAL=1
   211      -DSQLITE_ENABLE_PURGEABLE_PCACHE=1
   212      -DSQLITE_ENABLE_RTREE=1
   213      -DSQLITE_ENABLE_SNAPSHOT=1
   214      # -DSQLITE_ENABLE_SQLLOG=1
   215      -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT=1
   216      -DSQLITE_MAX_LENGTH=2147483645
   217      -DSQLITE_MAX_VARIABLE_NUMBER=500000
   218      # -DSQLITE_MEMDEBUG=1
   219      -DSQLITE_NO_SYNC=1
   220      -DSQLITE_OMIT_AUTORESET=1
   221      -DSQLITE_OMIT_LOAD_EXTENSION=1
   222      -DSQLITE_PREFER_PROXY_LOCKING=1
   223      -DSQLITE_SERIES_CONSTRAINT_VERIFY=1
   224      -DSQLITE_THREADSAFE=2
   225      -DSQLITE_USE_URI=1
   226      -DSQLITE_WRITE_WALFRAME_PREBUFFERED=1
   227      -DUSE_GUARDED_FD=1
   228      -DUSE_PREAD=1
   229      --enable-json1 --enable-fts5
   230    }
   231    "Extra-Robustness" {
   232      -DSQLITE_ENABLE_OVERSIZE_CELL_CHECK=1
   233      -DSQLITE_MAX_ATTACHED=62
   234    }
   235    "Devkit" {
   236      -DSQLITE_DEFAULT_FILE_FORMAT=4
   237      -DSQLITE_MAX_ATTACHED=30
   238      -DSQLITE_ENABLE_COLUMN_METADATA
   239      -DSQLITE_ENABLE_FTS4
   240      -DSQLITE_ENABLE_FTS5
   241      -DSQLITE_ENABLE_FTS4_PARENTHESIS
   242      -DSQLITE_DISABLE_FTS4_DEFERRED
   243      -DSQLITE_ENABLE_RTREE
   244      --enable-json1 --enable-fts5
   245    }
   246    "No-lookaside" {
   247      -DSQLITE_TEST_REALLOC_STRESS=1
   248      -DSQLITE_OMIT_LOOKASIDE=1
   249      -DHAVE_USLEEP=1
   250    }
   251    "Valgrind" {
   252      -DSQLITE_ENABLE_STAT4
   253      -DSQLITE_ENABLE_FTS4
   254      -DSQLITE_ENABLE_RTREE
   255      -DSQLITE_ENABLE_HIDDEN_COLUMNS
   256      -DCONFIG_SLOWDOWN_FACTOR=8.0
   257      --enable-json1
   258    }
   259  
   260    # The next group of configurations are used only by the
   261    # Failure-Detection platform.  They are all the same, but we need
   262    # different names for them all so that they results appear in separate
   263    # subdirectories.
   264    #
   265    Fail0 {-O0}
   266    Fail2 {-O0}
   267    Fail3 {-O0}
   268    Fail4 {-O0}
   269    FuzzFail1 {-O0}
   270    FuzzFail2 {-O0}
   271  }]
   272  
   273  array set ::Platforms [strip_comments {
   274    Linux-x86_64 {
   275      "Check-Symbols"           checksymbols
   276      "Fast-One"                "fuzztest test"
   277      "Debug-One"               "mptest test"
   278      "Have-Not"                test
   279      "Secure-Delete"           test
   280      "Unlock-Notify"           "QUICKTEST_INCLUDE=notify2.test test"
   281      "User-Auth"               tcltest
   282      "Update-Delete-Limit"     test
   283      "Extra-Robustness"        test
   284      "Device-Two"              "threadtest test"
   285      "No-lookaside"            test
   286      "Devkit"                  test
   287      "Apple"                   test
   288      "Sanitize"                {QUICKTEST_OMIT=func4.test,nan.test test}
   289      "Device-One"              fulltest
   290      "Default"                 "threadtest fulltest"
   291      "Valgrind"                valgrindtest
   292    }
   293    Linux-i686 {
   294      "Devkit"                  test
   295      "Have-Not"                test
   296      "Unlock-Notify"           "QUICKTEST_INCLUDE=notify2.test test"
   297      "Device-One"              test
   298      "Device-Two"              test
   299      "Default"                 "threadtest fulltest"
   300    }
   301    Darwin-i386 {
   302      "Locking-Style"           "mptest test"
   303      "Have-Not"                test
   304      "Apple"                   "threadtest fulltest"
   305    }
   306    Darwin-x86_64 {
   307      "Locking-Style"           "mptest test"
   308      "Have-Not"                test
   309      "Apple"                   "threadtest fulltest"
   310    }
   311    "Windows NT-intel" {
   312      "Stdcall"                 test
   313      "Have-Not"                test
   314      "Default"                 "mptest fulltestonly"
   315    }
   316    "Windows NT-amd64" {
   317      "Stdcall"                 test
   318      "Have-Not"                test
   319      "Default"                 "mptest fulltestonly"
   320    }
   321  
   322    # The Failure-Detection platform runs various tests that deliberately
   323    # fail.  This is used as a test of this script to verify that this script
   324    # correctly identifies failures.
   325    #
   326    Failure-Detection {
   327      Fail0     "TEST_FAILURE=0 test"
   328      Sanitize  "TEST_FAILURE=1 test"
   329      Fail2     "TEST_FAILURE=2 valgrindtest"
   330      Fail3     "TEST_FAILURE=3 valgrindtest"
   331      Fail4     "TEST_FAILURE=4 test"
   332      FuzzFail1 "TEST_FAILURE=5 test"
   333      FuzzFail2 "TEST_FAILURE=5 valgrindtest"
   334    }
   335  }]
   336  
   337  
   338  # End of configuration section.
   339  #########################################################################
   340  #########################################################################
   341  
   342  # Configuration verification: Check that each entry in the list of configs
   343  # specified for each platforms exists.
   344  #
   345  foreach {key value} [array get ::Platforms] {
   346    foreach {v t} $value {
   347      if {0==[info exists ::Configs($v)]} {
   348        puts stderr "No such configuration: \"$v\""
   349        exit -1
   350      }
   351    }
   352  }
   353  
   354  # Output log.   Disabled for slave interpreters.
   355  #
   356  if {[lindex $argv end]!="--slave"} {
   357    set LOG [open releasetest-out.txt w]
   358    proc PUTS {txt} {
   359      puts $txt
   360      puts $::LOG $txt
   361      flush $::LOG
   362    }
   363    proc PUTSNNL {txt} {
   364      puts -nonewline $txt
   365      puts -nonewline $::LOG $txt
   366      flush $::LOG
   367    }
   368    proc PUTSERR {txt} {
   369      puts stderr $txt
   370      puts $::LOG $txt
   371      flush $::LOG
   372    }
   373    puts $LOG "$argv0 $argv"
   374    set tm0 [clock format [clock seconds] -format {%Y-%m-%d %H:%M:%S} -gmt 1]
   375    puts $LOG "start-time: $tm0 UTC"
   376  } else {
   377    proc PUTS {txt} {
   378      puts $txt
   379    }
   380    proc PUTSNNL {txt} {
   381      puts -nonewline $txt
   382    }
   383    proc PUTSERR {txt} {
   384      puts stderr $txt
   385    }
   386  }
   387  
   388  # Open the file $logfile and look for a report on the number of errors
   389  # and the number of test cases run.  Add these values to the global
   390  # $::NERRCASE and $::NTESTCASE variables.
   391  #
   392  # If any errors occur, then write into $errmsgVar the text of an appropriate
   393  # one-line error message to show on the output.
   394  #
   395  proc count_tests_and_errors {logfile rcVar errmsgVar} {
   396    if {$::DRYRUN} return
   397    upvar 1 $rcVar rc $errmsgVar errmsg
   398    set fd [open $logfile rb]
   399    set seen 0
   400    while {![eof $fd]} {
   401      set line [gets $fd]
   402      if {[regexp {(\d+) errors out of (\d+) tests} $line all nerr ntest]} {
   403        incr ::NERRCASE $nerr
   404        incr ::NTESTCASE $ntest
   405        set seen 1
   406        if {$nerr>0} {
   407          set rc 1
   408          set errmsg $line
   409        }
   410      }
   411      if {[regexp {runtime error: +(.*)} $line all msg]} {
   412        # skip over "value is outside range" errors
   413        if {[regexp {value .* is outside the range of representable} $line]} {
   414           # noop
   415        } elseif {[regexp {overflow: .* cannot be represented} $line]} {
   416           # noop
   417        } else {
   418          incr ::NERRCASE
   419          if {$rc==0} {
   420            set rc 1
   421            set errmsg $msg
   422          }
   423        }
   424      }
   425      if {[regexp {fatal error +(.*)} $line all msg]} {
   426        incr ::NERRCASE
   427        if {$rc==0} {
   428          set rc 1
   429          set errmsg $msg
   430        }
   431      }
   432      if {[regexp {ERROR SUMMARY: (\d+) errors.*} $line all cnt] && $cnt>0} {
   433        incr ::NERRCASE
   434        if {$rc==0} {
   435          set rc 1
   436          set errmsg $all
   437        }
   438      }
   439      if {[regexp {^VERSION: 3\.\d+.\d+} $line]} {
   440        set v [string range $line 9 end]
   441        if {$::SQLITE_VERSION eq ""} {
   442          set ::SQLITE_VERSION $v
   443        } elseif {$::SQLITE_VERSION ne $v} {
   444          set rc 1
   445          set errmsg "version conflict: {$::SQLITE_VERSION} vs. {$v}"
   446        }
   447      }
   448    }
   449    close $fd
   450    if {$::BUILDONLY} {
   451      incr ::NTESTCASE
   452      if {$rc!=0} {
   453        set errmsg "Build failed"
   454      }
   455    } elseif {!$seen} {
   456      set rc 1
   457      set errmsg "Test did not complete"
   458      if {[file readable core]} {
   459        append errmsg " - core file exists"
   460      }
   461    }
   462  }
   463  
   464  #--------------------------------------------------------------------------
   465  # This command is invoked as the [main] routine for scripts run with the
   466  # "--slave" option.
   467  #
   468  # For each test (i.e. "configure && make test" execution), the master
   469  # process spawns a process with the --slave option. It writes two lines
   470  # to the slaves stdin. The first contains a single boolean value - the
   471  # value of ::TRACE to use in the slave script. The second line contains a
   472  # list in the same format as each element of the list passed to the
   473  # [run_all_test_suites] command in the master process.
   474  #
   475  # The slave then runs the "configure && make test" commands specified. It
   476  # exits successfully if the tests passes, or with a non-zero error code
   477  # otherwise.
   478  #
   479  proc run_slave_test {} {
   480    # Read global vars configuration from stdin.
   481    set V [gets stdin]
   482    foreach {::TRACE ::MSVC ::DRYRUN ::KEEPFILES} $V {}
   483  
   484    # Read the test-suite configuration from stdin.
   485    set T [gets stdin]
   486    foreach {title dir configOpts testtarget makeOpts cflags opts} $T {}
   487  
   488    # Create and switch to the test directory.
   489    set normaldir [file normalize $dir]
   490    set ::env(SQLITE_TMPDIR) $normaldir
   491    trace_cmd file mkdir $dir
   492    trace_cmd cd $dir
   493    catch {file delete core}
   494    catch {file delete test.log}
   495  
   496    # Run the "./configure && make" commands.
   497    set rc 0
   498    set rc [catch [configureCommand $configOpts]]
   499    if {!$rc} {
   500      if {[info exists ::env(TCLSH_CMD)]} {
   501        set savedEnv(TCLSH_CMD) $::env(TCLSH_CMD)
   502      } else {
   503        unset -nocomplain savedEnv(TCLSH_CMD)
   504      }
   505      set ::env(TCLSH_CMD) [file nativename [info nameofexecutable]]
   506  
   507      # Create a file called "makecommand.sh" containing the text of
   508      # the make command line.
   509      catch {
   510        set cmd [makeCommand $testtarget $makeOpts $cflags $opts]
   511        set fd [open makecommand.sh w]
   512        foreach e $cmd { 
   513          if {[string first " " $e]>=0} {
   514            puts -nonewline $fd "\"$e\""
   515          } else {
   516            puts -nonewline $fd $e
   517          }
   518          puts -nonewline $fd " "
   519        }
   520        puts $fd ""
   521        close $fd
   522      } msg
   523  
   524      # Run the make command.
   525      set rc [catch {trace_cmd exec {*}$cmd >>& test.log} msg]
   526      if {[info exists savedEnv(TCLSH_CMD)]} {
   527        set ::env(TCLSH_CMD) $savedEnv(TCLSH_CMD)
   528      } else {
   529        unset -nocomplain ::env(TCLSH_CMD)
   530      }
   531    }
   532  
   533    # Clean up lots of extra files if --keep was not specified.
   534    if {$::KEEPFILES==0} { cleanup $normaldir }
   535  
   536    # Exis successfully if the test passed, or with a non-zero error code
   537    # otherwise.
   538    exit $rc
   539  }
   540  
   541  # This command is invoked in the master process each time a slave
   542  # file-descriptor is readable.
   543  #
   544  proc slave_fileevent {fd T tm1} {
   545    global G
   546    foreach {title dir configOpts testtarget makeOpts cflags opts} $T {}
   547  
   548    if {[eof $fd]} {
   549      fconfigure $fd -blocking 1
   550      set rc [catch { close $fd }]
   551  
   552      set errmsg {}
   553      set logfile [file join $dir test.log]
   554      if {[file exists $logfile]} {
   555        count_tests_and_errors [file join $dir test.log] rc errmsg
   556      } elseif {$rc==0 && !$::DRYRUN} {
   557        set rc 1
   558        set errmsg "no test.log file..."
   559      }
   560  
   561      if {!$::TRACE} {
   562        set tm2 [clock seconds]
   563        set hours [expr {($tm2-$tm1)/3600}]
   564        set minutes [expr {(($tm2-$tm1)/60)%60}]
   565        set seconds [expr {($tm2-$tm1)%60}]
   566        set tm [format (%02d:%02d:%02d) $hours $minutes $seconds]
   567  
   568        if {$rc} {
   569          set status FAIL
   570          incr ::NERR
   571        } else {
   572          set status Ok
   573        }
   574  
   575        set n [string length $title]
   576        if {$::PROGRESS_MSGS} {
   577          PUTS "finished: ${title}[string repeat . [expr {53-$n}]] $status $tm"
   578        } else {
   579          PUTS "${title}[string repeat . [expr {63-$n}]] $status $tm"
   580        }
   581        if {$errmsg!=""} {PUTS "     $errmsg"}
   582        flush stdout
   583      }
   584  
   585      incr G(nJob) -1
   586    } else {
   587      set line [gets $fd]
   588      if {[string trim $line] != ""} {
   589        puts "Trace   : $title - \"$line\""
   590      }
   591    }
   592  }
   593  
   594  #--------------------------------------------------------------------------
   595  # The only argument passed to this function is a list of test-suites to
   596  # run. Each "test-suite" is itself a list consisting of the following
   597  # elements:
   598  #
   599  #   * Test title (for display).
   600  #   * The name of the directory to run the test in.
   601  #   * The argument for [configureCommand]
   602  #   * The first argument for [makeCommand]
   603  #   * The second argument for [makeCommand]
   604  #   * The third argument for [makeCommand]
   605  #
   606  proc run_all_test_suites {alltests} {
   607    global G
   608    set tests $alltests
   609  
   610    set G(nJob) 0
   611  
   612    while {[llength $tests]>0 || $G(nJob)>0} {
   613      if {$G(nJob)>=$::JOBS || [llength $tests]==0} {
   614        vwait G(nJob)
   615      }
   616  
   617      if {[llength $tests]>0} {
   618        set T [lindex $tests 0]
   619        set tests [lrange $tests 1 end]
   620        foreach {title dir configOpts testtarget makeOpts cflags opts} $T {}
   621        if {$::PROGRESS_MSGS && !$::TRACE} {
   622          set n [string length $title]
   623          PUTS "starting: ${title} at [now]"
   624          flush stdout
   625        }
   626  
   627        # Run the job.
   628        #
   629        set tm1 [clock seconds]
   630        incr G(nJob)
   631        set script [file normalize [info script]]
   632        set fd [open "|[info nameofexecutable] $script --slave" r+]
   633        fconfigure $fd -blocking 0
   634        fileevent $fd readable [list slave_fileevent $fd $T $tm1]
   635        puts $fd [list $::TRACE $::MSVC $::DRYRUN $::KEEPFILES]
   636        puts $fd [list {*}$T]
   637        flush $fd
   638      }
   639    }
   640  }
   641  
   642  proc add_test_suite {listvar name testtarget config} {
   643    upvar $listvar alltests
   644  
   645    # Tcl variable $opts is used to build up the value used to set the
   646    # OPTS Makefile variable. Variable $cflags holds the value for
   647    # CFLAGS. The makefile will pass OPTS to both gcc and lemon, but
   648    # CFLAGS is only passed to gcc.
   649    #
   650    set makeOpts ""
   651    set cflags [expr {$::MSVC ? "-Zi" : "-g"}]
   652    set opts ""
   653    set title ${name}($testtarget)
   654    set configOpts $::WITHTCL
   655    set skip 0
   656  
   657    regsub -all {#[^\n]*\n} $config \n config
   658    foreach arg $config {
   659      if {$skip} {
   660        set skip 0
   661        continue
   662      }
   663      if {[regexp {^-[UD]} $arg]} {
   664        lappend opts $arg
   665      } elseif {[regexp {^[A-Z]+=} $arg]} {
   666        lappend testtarget $arg
   667      } elseif {[regexp {^if:([a-z]+)(.*)} $arg all key tail]} {
   668        # Arguments of the form 'if:os=="Linux"' will cause the subsequent
   669        # argument to be skipped if the $tcl_platform(os) is not "Linux", for
   670        # example...
   671        set skip [expr !(\$::tcl_platform($key)$tail)]
   672      } elseif {[regexp {^--(enable|disable)-} $arg]} {
   673        if {$::MSVC} {
   674          if {$arg eq "--disable-amalgamation"} {
   675            lappend makeOpts USE_AMALGAMATION=0
   676            continue
   677          }
   678          if {$arg eq "--disable-shared"} {
   679            lappend makeOpts USE_CRT_DLL=0 DYNAMIC_SHELL=0
   680            continue
   681          }
   682          if {$arg eq "--enable-fts5"} {
   683            lappend opts -DSQLITE_ENABLE_FTS5
   684            continue
   685          }
   686          if {$arg eq "--enable-json1"} {
   687            lappend opts -DSQLITE_ENABLE_JSON1
   688            continue
   689          }
   690          if {$arg eq "--enable-shared"} {
   691            lappend makeOpts USE_CRT_DLL=1 DYNAMIC_SHELL=1
   692            continue
   693          }
   694        }
   695        lappend configOpts $arg
   696      } else {
   697        if {$::MSVC} {
   698          if {$arg eq "-g"} {
   699            lappend cflags -Zi
   700            continue
   701          }
   702          if {[regexp -- {^-O(\d+)$} $arg all level]} then {
   703            lappend makeOpts OPTIMIZATIONS=$level
   704            continue
   705          }
   706        }
   707        lappend cflags $arg
   708      }
   709    }
   710  
   711    # Disable sync to make testing faster.
   712    #
   713    lappend opts -DSQLITE_NO_SYNC=1
   714  
   715    # Some configurations already set HAVE_USLEEP; in that case, skip it.
   716    #
   717    if {[lsearch -regexp $opts {^-DHAVE_USLEEP(?:=|$)}]==-1} {
   718      lappend opts -DHAVE_USLEEP=1
   719    }
   720  
   721    # Add the define for this platform.
   722    #
   723    if {$::tcl_platform(platform)=="windows"} {
   724      lappend opts -DSQLITE_OS_WIN=1
   725    } else {
   726      lappend opts -DSQLITE_OS_UNIX=1
   727    }
   728  
   729    # Set the sub-directory to use.
   730    #
   731    set dir [string tolower [string map {- _ " " _} $name]]
   732  
   733    # Join option lists into strings, using space as delimiter.
   734    #
   735    set makeOpts [join $makeOpts " "]
   736    set cflags   [join $cflags " "]
   737    set opts     [join $opts " "]
   738  
   739    lappend alltests [list \
   740        $title $dir $configOpts $testtarget $makeOpts $cflags $opts]
   741  }
   742  
   743  # The following procedure returns the "configure" command to be exectued for
   744  # the current platform, which may be Windows (via MinGW, etc).
   745  #
   746  proc configureCommand {opts} {
   747    if {$::MSVC} return [list]; # This is not needed for MSVC.
   748    set result [list trace_cmd exec]
   749    if {$::tcl_platform(platform)=="windows"} {
   750      lappend result sh
   751    }
   752    lappend result $::SRCDIR/configure --enable-load-extension
   753    foreach x $opts {lappend result $x}
   754    lappend result >& test.log
   755  }
   756  
   757  # The following procedure returns the "make" command to be executed for the
   758  # specified targets, compiler flags, and options.
   759  #
   760  proc makeCommand { targets makeOpts cflags opts } {
   761    set result [list]
   762    if {$::MSVC} {
   763      set nmakeDir [file nativename $::SRCDIR]
   764      set nmakeFile [file nativename [file join $nmakeDir Makefile.msc]]
   765      lappend result nmake /f $nmakeFile TOP=$nmakeDir
   766      set tclDir [file nativename [file normalize \
   767          [file dirname [file dirname [info nameofexecutable]]]]]
   768      lappend result "TCLDIR=$tclDir"
   769      if {[regexp {USE_STDCALL=1} $cflags]} {
   770        lappend result USE_STDCALL=1
   771      }
   772    } else {
   773      lappend result make
   774    }
   775    foreach makeOpt $makeOpts {
   776      lappend result $makeOpt
   777    }
   778    lappend result clean
   779    foreach target $targets {
   780      lappend result $target
   781    }
   782    lappend result CFLAGS=$cflags OPTS=$opts
   783  }
   784  
   785  # The following procedure prints its arguments if ::TRACE is true.
   786  # And it executes the command of its arguments in the calling context
   787  # if ::DRYRUN is false.
   788  #
   789  proc trace_cmd {args} {
   790    if {$::TRACE} {
   791      PUTS $args
   792    }
   793    set res ""
   794    if {!$::DRYRUN} {
   795      set res [uplevel 1 $args]
   796    }
   797    return $res
   798  }
   799  
   800  
   801  # This proc processes the command line options passed to this script.
   802  # Currently the only option supported is "-makefile", default
   803  # "releasetest.mk". Set the ::MAKEFILE variable to the value of this
   804  # option.
   805  #
   806  proc process_options {argv} {
   807    set ::SRCDIR    [file normalize [file dirname [file dirname $::argv0]]]
   808    set ::QUICK          0
   809    set ::MSVC           0
   810    set ::BUILDONLY      0
   811    set ::DRYRUN         0
   812    set ::TRACE          0
   813    set ::JOBS           1
   814    set ::PROGRESS_MSGS  0
   815    set ::WITHTCL        {}
   816    set ::FORCE          0
   817    set ::KEEPFILES      0          ;# Keep extra files after test run
   818    set config {}
   819    set platform $::tcl_platform(os)-$::tcl_platform(machine)
   820  
   821    for {set i 0} {$i < [llength $argv]} {incr i} {
   822      set x [lindex $argv $i]
   823      if {[regexp {^--[a-z]} $x]} {set x [string range $x 1 end]}
   824      switch -glob -- $x {
   825        -slave {
   826          run_slave_test
   827          exit
   828        }
   829  
   830        # Undocumented legacy option: --srcdir DIRECTORY
   831        #
   832        # DIRECTORY is the root of the SQLite checkout.  This sets the
   833        # SRCDIR global variable.  But that variable is already set
   834        # automatically so there really is no reason to have this option.
   835        #
   836        -srcdir {
   837          incr i
   838          set ::SRCDIR [file normalize [lindex $argv $i]]
   839        }
   840  
   841        -platform {
   842          incr i
   843          set platform [lindex $argv $i]
   844        }
   845  
   846        -jobs {
   847          incr i
   848          set ::JOBS [lindex $argv $i]
   849        }
   850  
   851        -progress {
   852          set ::PROGRESS_MSGS 1
   853        }
   854  
   855        -quick {
   856          set ::QUICK 1
   857        }
   858        -veryquick {
   859          set ::QUICK 2
   860        }
   861  
   862        -config {
   863          incr i
   864          set config [lindex $argv $i]
   865        }
   866  
   867        -msvc {
   868          set ::MSVC 1
   869        }
   870  
   871        -buildonly {
   872          set ::BUILDONLY 1
   873        }
   874  
   875        -dryrun {
   876          set ::DRYRUN 1
   877        }
   878  
   879        -force -
   880        -f {
   881          set ::FORCE 1
   882        }
   883  
   884        -trace {
   885          set ::TRACE 1
   886        }
   887  
   888        -info {
   889          PUTS "Command-line Options:"
   890          PUTS "   --srcdir $::SRCDIR"
   891          PUTS "   --platform [list $platform]"
   892          PUTS "   --config [list $config]"
   893          if {$::QUICK} {
   894            if {$::QUICK==1} {PUTS "   --quick"}
   895            if {$::QUICK==2} {PUTS "   --veryquick"}
   896          }
   897          if {$::MSVC}      {PUTS "   --msvc"}
   898          if {$::BUILDONLY} {PUTS "   --buildonly"}
   899          if {$::DRYRUN}    {PUTS "   --dryrun"}
   900          if {$::TRACE}     {PUTS "   --trace"}
   901          PUTS "\nAvailable --platform options:"
   902          foreach y [lsort [array names ::Platforms]] {
   903            PUTS "   [list $y]"
   904          }
   905          PUTS "\nAvailable --config options:"
   906          foreach y [lsort [array names ::Configs]] {
   907            PUTS "   [list $y]"
   908          }
   909          exit
   910        }
   911  
   912        -g {
   913          lappend ::EXTRACONFIG [lindex $argv $i]
   914        }
   915  
   916        -keep {
   917          set ::KEEPFILES 1
   918        }
   919  
   920        -with-tcl=* {
   921          set ::WITHTCL -$x
   922        }
   923  
   924        -D* -
   925        -O* -
   926        -enable-* -
   927        -disable-* -
   928        *=* {
   929          lappend ::EXTRACONFIG [lindex $argv $i]
   930        }
   931  
   932        default {
   933          PUTSERR ""
   934          PUTSERR [string trim $::USAGE_MESSAGE]
   935          exit -1
   936        }
   937      }
   938    }
   939  
   940    if {0==[info exists ::Platforms($platform)]} {
   941      PUTS "Unknown platform: $platform"
   942      PUTSNNL "Set the -platform option to "
   943      set print [list]
   944      foreach p [array names ::Platforms] {
   945        lappend print "\"$p\""
   946      }
   947      lset print end "or [lindex $print end]"
   948      PUTS "[join $print {, }]."
   949      exit
   950    }
   951  
   952    if {$config!=""} {
   953      if {[llength $config]==1} {lappend config fulltest}
   954      set ::CONFIGLIST $config
   955    } else {
   956      if {$::JOBS>1} {
   957        set ::CONFIGLIST {}
   958        foreach {target zConfig} [lreverse $::Platforms($platform)] {
   959          append ::CONFIGLIST [format "    %-25s %s\n" \
   960                                 [list $zConfig] [list $target]]
   961        }
   962      } else {
   963        set ::CONFIGLIST $::Platforms($platform)
   964      }
   965    }
   966    PUTS "Running the following test configurations for $platform:"
   967    PUTS "    [string trim $::CONFIGLIST]"
   968    PUTSNNL "Flags:"
   969    if {$::PROGRESS_MSGS} {PUTSNNL " --progress"}
   970    if {$::DRYRUN} {PUTSNNL " --dryrun"}
   971    if {$::BUILDONLY} {PUTSNNL " --buildonly"}
   972    if {$::MSVC} {PUTSNNL " --msvc"}
   973    switch -- $::QUICK {
   974       1 {PUTSNNL " --quick"}
   975       2 {PUTSNNL " --veryquick"}
   976    }
   977    if {$::JOBS>1} {PUTSNNL " --jobs $::JOBS"}
   978    PUTS ""
   979  }
   980  
   981  # Check to see if there are uncommitted changes in the SQLite source
   982  # checkout.  Exit if there are.  Except:  Do nothing if the --force
   983  # flag is used.  Also, ignore this test if the fossil binary is
   984  # unavailable, or if the source tree is not a valid fossil checkout.
   985  #
   986  proc check_uncommitted {} {
   987    if {$::FORCE} return
   988    set pwd [pwd]
   989    cd $::SRCDIR
   990    if {[catch {exec fossil changes} res]==0 && [string trim $res]!=""} {
   991      puts "ERROR: The check-out contains uncommitted changes:"
   992      puts $res
   993      puts "Use the -f or --force options to override"
   994      exit 1
   995    }
   996    cd $pwd
   997  }
   998  
   999  # A test run has just finished in directory $dir. This command deletes all
  1000  # non-essential files from the directory. Specifically, everything except
  1001  #
  1002  #   * The "testfixture" and "sqlite3" binaries,
  1003  #   * The "test-out.log" and "test.log" log files.
  1004  #
  1005  proc cleanup {dir} {
  1006    set K(testfixture) 1
  1007    set K(testfixture.exe) 1
  1008    set K(sqlite3) 1
  1009    set K(sqlite3.exe) 1
  1010    set K(test-out.txt) 1
  1011    set K(test.log) 1
  1012  
  1013    foreach f [glob -nocomplain [file join $dir *]] {
  1014      set tail [file tail $f]
  1015      if {[info exists K($tail)]==0} { 
  1016        file delete -force $f
  1017      }
  1018    }
  1019  }
  1020  
  1021  
  1022  # Main routine.
  1023  #
  1024  proc main {argv} {
  1025  
  1026    # Process any command line options.
  1027    set ::EXTRACONFIG {}
  1028    process_options $argv
  1029    if {!$::DRYRUN} check_uncommitted
  1030    PUTS [string repeat * 79]
  1031  
  1032    set ::NERR 0
  1033    set ::NTEST 0
  1034    set ::NTESTCASE 0
  1035    set ::NERRCASE 0
  1036    set ::SQLITE_VERSION {}
  1037    set STARTTIME [clock seconds]
  1038    foreach {zConfig target} $::CONFIGLIST {
  1039      if {$::MSVC && ($zConfig eq "Sanitize" || "checksymbols" in $target
  1040             || "valgrindtest" in $target)} {
  1041        PUTS "Skipping $zConfig / $target for MSVC..."
  1042        continue
  1043      }
  1044      if {$target ne "checksymbols"} {
  1045        switch -- $::QUICK {
  1046           1 {set target quicktest}
  1047           2 {set target smoketest}
  1048        }
  1049        if {$::BUILDONLY} {
  1050          set target testfixture
  1051          if {$::tcl_platform(platform)=="windows"} {
  1052            append target .exe
  1053          }
  1054        }
  1055      }
  1056      set config_options [concat $::Configs($zConfig) $::EXTRACONFIG]
  1057  
  1058      incr NTEST
  1059      add_test_suite all $zConfig $target $config_options
  1060  
  1061      # If the configuration included the SQLITE_DEBUG option, then remove
  1062      # it and run veryquick.test. If it did not include the SQLITE_DEBUG option
  1063      # add it and run veryquick.test.
  1064      if {$target!="checksymbols" && $target!="valgrindtest"
  1065             && $target!="fuzzoomtest" && !$::BUILDONLY && $::QUICK<2} {
  1066        set debug_idx [lsearch -glob $config_options -DSQLITE_DEBUG*]
  1067        set xtarget $target
  1068        regsub -all {fulltest[a-z]*} $xtarget test xtarget
  1069        regsub -all {fuzzoomtest} $xtarget fuzztest xtarget
  1070        if {$debug_idx < 0} {
  1071          incr NTEST
  1072          append config_options " -DSQLITE_DEBUG=1 -DSQLITE_EXTRA_IFNULLROW=1"
  1073          add_test_suite all "${zConfig}_debug" $xtarget $config_options
  1074        } else {
  1075          incr NTEST
  1076          regsub { *-DSQLITE_MEMDEBUG[^ ]* *} $config_options { } config_options
  1077          regsub { *-DSQLITE_DEBUG[^ ]* *} $config_options { } config_options
  1078          add_test_suite all "${zConfig}_ndebug" $xtarget $config_options
  1079        }
  1080      }
  1081    }
  1082  
  1083    run_all_test_suites $all
  1084  
  1085    set elapsetime [expr {[clock seconds]-$STARTTIME}]
  1086    set hr [expr {$elapsetime/3600}]
  1087    set min [expr {($elapsetime/60)%60}]
  1088    set sec [expr {$elapsetime%60}]
  1089    set etime [format (%02d:%02d:%02d) $hr $min $sec]
  1090    if {$::JOBS>1} {append etime " $::JOBS cores"}
  1091    if {[catch {exec hostname} HNAME]==0} {append etime " on $HNAME"}
  1092    PUTS [string repeat * 79]
  1093    incr ::NERRCASE $::NERR
  1094    PUTS "$::NERRCASE failures out of $::NTESTCASE tests in $etime"
  1095    if {$::SQLITE_VERSION ne ""} {
  1096      PUTS "SQLite $::SQLITE_VERSION"
  1097    }
  1098  }
  1099  
  1100  main $argv