3 exec wapptclsh
"$0" ${1+"$@"}
5 # package required wapp
6 source [file join [file dirname [info
script]] wapp.tcl
]
8 # Variables set by the "control" form:
10 # G(platform) - User selected platform.
11 # G(test) - Set to "Normal", "Veryquick", "Smoketest" or "Build-Only".
12 # G(keep) - Boolean. True to delete no files after each test.
13 # G(msvc) - Boolean. True to use MSVC as the compiler.
14 # G(tcl) - Use Tcl from this directory for builds.
15 # G(jobs) - How many sub-processes to run simultaneously.
17 set G
(platform
) $
::tcl_platform
(os
)-$
::tcl_platform
(machine
)
21 set G
(tcl
) [::tcl
::pkgconfig get libdir
,install]
29 proc wapptest_init
{} {
32 set lSave
[list platform
test keep msvc tcl
jobs debug noui stdout
]
33 foreach k
$lSave { set A
($k) $G($k) }
35 foreach k
$lSave { set G
($k) $A($k) }
37 # The root of the SQLite source tree.
38 set G
(srcdir
) [file dirname [file dirname [info
script]]]
40 set G
(sqlite_version
) "unknown"
42 # Either "config", "running" or "stopped":
45 set G
(hostname
) "(unknown host)"
46 catch
{ set G
(hostname
) [exec hostname
] }
47 set G
(host) $G(hostname
)
48 append G
(host) " $::tcl_platform(os) $::tcl_platform(osVersion)"
49 append G
(host) " $::tcl_platform(machine) $::tcl_platform(byteOrder)"
52 proc wapptest_run
{} {
55 set G
(state
) "running"
59 wapptest_output
"Running the following for $G(platform). $G(jobs) jobs."
60 foreach t
$G(test_array
) {
61 set config
[dict get
$t config
]
62 set target
[dict get
$t target
]
63 wapptest_output
[format
" %-25s%s" $config $target]
65 wapptest_output
[string repeat
* 70]
68 proc releasetest_data
{args
} {
70 set rtd
[file join $G(srcdir
) test releasetest_data.tcl
]
71 set fd
[open
"|[info nameofexecutable] $rtd $args" r
+]
77 # Generate the text for the box at the top of the UI. The current SQLite
78 # version, according to fossil, along with a warning if there are
79 # uncommitted changes in the checkout.
81 proc generate_fossil_info
{} {
86 set r1
[exec fossil info
]
87 set r2
[exec fossil changes
]
92 foreach line
[split $r1 "\n"] {
93 if {[regexp
{^checkout
: *(.
*)$
} $line -> co
]} {
94 wapp-trim
{ <br
> %html
($co) }
98 if {[string trim
$r2]!=""} {
100 <br
><span class
=warning
>
101 WARNING
: Uncommitted changes
in checkout
107 # If the application is in "config" state, set the contents of the
108 # ::G(test_array) global to reflect the tests that will be run. If the
109 # app is in some other state ("running" or "stopped"), this command
112 proc set_test_array
{} {
114 if { $G(state
)=="config" } {
115 set G
(test_array
) [list
]
117 if {$G(debug
)==0} { set debug
"-nodebug"}
118 foreach
{config target
} [releasetest_data tests
$debug $G(platform
)] {
120 # If using MSVC, do not run sanitize or valgrind tests. Or the
123 "Sanitize" == $config
124 ||
"checksymbols" in $target
125 ||
"valgrindtest" in $target
130 # If the test mode is not "Normal", override the target.
132 if {$target!="checksymbols" && $G(platform
)!="Failure-Detection"} {
134 Veryquick
{ set target quicktest
}
135 Smoketest
{ set target smoketest
}
137 set target testfixture
138 if {$
::tcl_platform
(platform
)=="windows"} {
139 set target testfixture.exe
145 lappend G
(test_array
) [dict create config
$config target
$target]
150 proc count_tests_and_errors
{name logfile
} {
153 set fd
[open
$logfile rb
]
157 if {[regexp
{(\d
+) errors out of
(\d
+) tests
} $line all nerr ntest
]} {
158 incr G
(test.
$name.nError
) $nerr
159 incr G
(test.
$name.nTest
) $ntest
162 set G
(test.
$name.errmsg
) $line
165 if {[regexp
{runtime error
: +(.
*)} $line all msg
]} {
166 # skip over "value is outside range" errors
167 if {[regexp
{.
* is outside the range of representable
} $line]} {
170 incr G
(test.
$name.nError
)
171 if {$G(test.
$name.errmsg
)==""} {
172 set G
(test.
$name.errmsg
) $msg
176 if {[regexp
{fatal error
+(.
*)} $line all msg
]} {
177 incr G
(test.
$name.nError
)
178 if {$G(test.
$name.errmsg
)==""} {
179 set G
(test.
$name.errmsg
) $msg
182 if {[regexp
{ERROR SUMMARY
: (\d
+) errors.
*} $line all cnt
] && $cnt>0} {
183 incr G
(test.
$name.nError
)
184 if {$G(test.
$name.errmsg
)==""} {
185 set G
(test.
$name.errmsg
) $all
188 if {[regexp
{^VERSION
: 3\.\d
+.\d
+} $line]} {
189 set v
[string range
$line 9 end
]
190 if {$G(sqlite_version
) eq
"unknown"} {
191 set G
(sqlite_version
) $v
192 } elseif
{$G(sqlite_version
) ne
$v} {
193 set G
(test.
$name.errmsg
) "version conflict: {$G(sqlite_version)} vs. {$v}"
198 if {$G(test) == "Build-Only"} {
199 incr G
(test.
$name.nTest
)
200 if {$G(test.
$name.nError
)>0} {
201 set errmsg
"Build failed"
204 set G
(test.
$name.errmsg
) "Test did not complete"
205 if {[file readable core
]} {
206 append G
(test.
$name.errmsg
) " - core file exists"
211 proc wapptest_output
{str
} {
213 if {$G(stdout
)} { puts
$str }
214 if {[info exists G
(log
)]} {
219 proc wapptest_openlog
{} {
221 set G
(log
) [open wapptest-out.txt w
+]
223 proc wapptest_closelog
{} {
229 proc format_seconds
{seconds
} {
230 set min
[format
%.2d
[expr ($seconds / 60) % 60]]
231 set hr
[format
%.2d
[expr $seconds / 3600]]
232 set sec
[format
%.2d
[expr $seconds % 60]]
233 return "$hr:$min:$sec"
236 # This command is invoked once a slave process has finished running its
237 # tests, successfully or otherwise. Parameter $name is the name of the
238 # test, $rc the exit code returned by the slave process.
240 proc slave_test_done
{name rc
} {
242 set G
(test.
$name.
done) [clock seconds
]
243 set G
(test.
$name.nError
) 0
244 set G
(test.
$name.nTest
) 0
245 set G
(test.
$name.errmsg
) ""
247 incr G
(test.
$name.nError
)
249 if {[file exists
$G(test.
$name.log
)]} {
250 count_tests_and_errors
$name $G(test.
$name.log
)
253 # If the "keep files" checkbox is clear, delete all files except for
254 # the executables and test logs. And any core file that is present.
257 testfixture testfixture.exe
259 test.log test-out.txt
262 wapptest_configure.sh
265 foreach f
[glob
-nocomplain [file join $G(test.
$name.dir
) *]] {
267 if {[lsearch
$keeplist $t]<0} {
268 catch
{ file delete
-force $f }
273 # Format a message regarding the success or failure of hte test.
274 set t
[format_seconds
[expr $G(test.
$name.
done) - $G(test.
$name.start
)]]
276 if {$G(test.
$name.nError
)} { set res
"FAILED" }
277 set dots
[string repeat .
[expr 60 - [string length
$name]]]
278 set msg
"$name $dots $res ($t)"
281 if {[info exists G
(test.
$name.errmsg
)] && $G(test.
$name.errmsg
)!=""} {
282 wapptest_output
" $G(test.$name.errmsg)"
286 # This is a fileevent callback invoked each time a file-descriptor that
287 # connects this process to a slave process is readable.
289 proc slave_fileevent
{name
} {
291 set fd
$G(test.
$name.channel
)
294 fconfigure
$fd -blocking 1
295 set rc
[catch
{ close
$fd }]
296 unset G
(test.
$name.channel
)
297 slave_test_done
$name $rc
300 if {[string trim
$line] != ""} { puts
"Trace : $name - \"$line\"" }
306 # Return the contents of the "slave script" - the script run by slave
307 # processes to actually perform the test. All it does is execute the
308 # test script already written to disk (wapptest_cmd.sh or wapptest_cmd.bat).
310 proc wapptest_slave_script
{} {
313 set dir
[file join ..
$G(srcdir
)]
314 set res
[subst
-nocommands {
315 set rc
[catch
"exec sh wapptest_cmd.sh {$dir} >>& test.log" ]
319 set dir
[file nativename
[file normalize
$G(srcdir
)]]
320 set dir
[string map
[list
"\\" "\\\\"] $dir]
321 set res
[subst
-nocommands {
322 set rc
[catch
"exec wapptest_cmd.bat {$dir} >>& test.log" ]
331 # Launch a slave process to run a test.
333 proc slave_launch
{name target dir
} {
336 catch
{ file mkdir
$dir } msg
337 foreach f
[glob
-nocomplain [file join $dir *]] {
338 catch
{ file delete
-force $f }
340 set G
(test.
$name.dir
) $dir
342 # Write the test command to wapptest_cmd.sh|bat.
345 if {$G(msvc
)} { set ext bat
}
346 set fd1
[open
[file join $dir wapptest_cmd.
$ext] w
]
348 puts
$fd1 [releasetest_data
script -msvc $name $target]
350 puts
$fd1 [releasetest_data
script $name $target]
354 # Write the wapptest_run.tcl script to the test directory. To run the
355 # commands in the other two files.
357 set fd3
[open
[file join $dir wapptest_run.tcl
] w
]
358 puts
$fd3 [wapptest_slave_script
]
363 set fd
[open
"|[info nameofexecutable] wapptest_run.tcl" r
+]
366 set G
(test.
$name.channel
) $fd
367 fconfigure
$fd -blocking 0
368 fileevent
$fd readable
[list slave_fileevent
$name]
371 proc do_some_stuff
{} {
374 # Count the number of running jobs. A running job has an entry named
375 # "channel" in its dictionary.
378 foreach j
$G(test_array
) {
379 set name
[dict get
$j config
]
380 if { [info exists G
(test.
$name.channel
)]} { incr nRunning
}
381 if {![info exists G
(test.
$name.
done)]} { set bFinished
0 }
388 foreach j
$G(test_array
) {
389 set name
[dict get
$j config
]
390 incr nError
$G(test.
$name.nError
)
391 incr nTest
$G(test.
$name.nTest
)
394 set G
(result
) "$nError errors from $nTest tests in $nConfig configurations."
395 wapptest_output
[string repeat
* 70]
396 wapptest_output
$G(result
)
398 append G
(result
) " SQLite version $G(sqlite_version)"
399 wapptest_output
" SQLite version $G(sqlite_version)"
401 set G
(state
) "stopped"
403 if {$G(noui
)} { exit 0 }
405 set nLaunch
[expr $G(jobs) - $nRunning]
406 foreach j
$G(test_array
) {
407 if {$nLaunch<=0} break
408 set name
[dict get
$j config
]
409 if { ![info exists G
(test.
$name.channel
)]
410 && ![info exists G
(test.
$name.
done)]
413 set target
[dict get
$j target
]
414 set dir
[string tolower
[string map
{" " _
"-" _
} $name]]
415 set G
(test.
$name.start
) [clock seconds
]
416 set G
(test.
$name.log
) [file join $dir test.log
]
418 slave_launch
$name $target $dir
426 proc generate_select_widget
{label id lOpt opt
} {
428 <label
> %string
($label) </label
>
429 <select id
=%string
($id) name
=%string
($id)>
433 if {$o==$opt} { set selected
" selected=1" }
434 wapp-subst
"<option $selected>$o</option>"
436 wapp-trim
{ </select> }
439 proc generate_main_page
{{extra
{}}} {
443 set hostname
$G(hostname
)
447 <title
> %html
($hostname): wapptest.tcl
</title
>
448 <link rel
="stylesheet" type="text/css" href
="style.css"/>
455 <div class
="border">%string
($host)
460 <div class
="border" id
=controls
>
461 <form action
="control" method
="post" name
="control">
464 # Build the "platform" select widget.
465 set lOpt
[releasetest_data platforms
]
466 generate_select_widget Platform control_platform
$lOpt $G(platform
)
468 # Build the "test" select widget.
469 set lOpt
[list Normal Veryquick Smoketest Build-Only
]
470 generate_select_widget Test control_test
$lOpt $G(test)
472 # Build the "jobs" select widget. Options are 1 to 8.
473 generate_select_widget Jobs control_jobs
{1 2 3 4 5 6 7 8} $G(jobs)
481 set txt
"STOP Tests!"
491 <input id
=%string
($id) name
=%string
($id) type=submit value
="%string($txt)">
498 <label
> Tcl
: </label
>
499 <input id
="control_tcl" name
="control_tcl"></input
>
500 <label
> Keep files
: </label
>
501 <input id
="control_keep" name
="control_keep" type=checkbox value
=1>
503 <label
> Use MSVC
: </label
>
504 <input id
="control_msvc" name
="control_msvc" type=checkbox value
=1>
505 <label
> Debug tests
: </label
>
506 <input id
="control_debug" name
="control_debug" type=checkbox value
=1>
518 set script "script/$G(state).js"
521 <script src
=%string
($script)></script>
527 proc wapp-default
{} {
531 proc wapp-page-tests
{} {
533 wapp-trim
{ <table class
="border" width
=100%> }
534 foreach t
$G(test_array
) {
535 set config
[dict get
$t config
]
536 set target
[dict get
$t target
]
541 if {[info exists G
(test.
$config.log
)]} {
542 if {[info exists G
(test.
$config.channel
)]} {
543 set class
"testrunning"
544 set seconds
[expr [clock seconds
] - $G(test.
$config.start
)]
545 } elseif
{[info exists G
(test.
$config.
done)]} {
546 if {$G(test.
$config.nError
)>0} {
551 set seconds
[expr $G(test.
$config.
done) - $G(test.
$config.start
)]
553 set seconds
[format_seconds
$seconds]
557 <tr class
=%string
($class)>
558 <td class
="nowrap"> %html
($config)
559 <td class
="padleft nowrap"> %html
($target)
560 <td class
="padleft nowrap"> %html
($seconds)
561 <td class
="padleft nowrap">
563 if {[info exists G
(test.
$config.log
)]} {
564 set log
$G(test.
$config.log
)
567 <a href
=%url
($uri)> %html
($log) </a
>
570 if {[info exists G
(test.
$config.errmsg
)] && $G(test.
$config.errmsg
)!=""} {
571 set errmsg
$G(test.
$config.errmsg
)
574 <td
> <td class
="padleft" colspan
=3> %html
($errmsg)
579 wapp-trim
{ </table
> }
581 if {[info exists G
(result
)]} {
584 <div class
=border id
=result
> %string
($res) </div
>
591 # Whenever the form at the top of the application page is submitted, it
594 proc wapp-page-control
{} {
596 if {$
::G
(state
)=="config"} {
597 set lControls
[list platform
test tcl
jobs keep msvc debug
]
602 set lControls
[list
jobs]
604 foreach v
$lControls {
605 if {[wapp-param-exists control_
$v]} {
606 set G
($v) [wapp-param control_
$v]
610 if {[wapp-param-exists control_run
]} {
611 # This is a "run test" command.
615 if {[wapp-param-exists control_stop
]} {
616 # A "STOP tests" command.
617 set G
(state
) "stopped"
618 set G
(result
) "Test halted by user"
619 foreach j
$G(test_array
) {
620 set name
[dict get
$j config
]
621 if { [info exists G
(test.
$name.channel
)] } {
622 close
$G(test.
$name.channel
)
623 unset G
(test.
$name.channel
)
624 slave_test_done
$name 1
630 if {[wapp-param-exists control_reset
]} {
631 # A "reset app" command.
632 set G
(state
) "config"
636 if {$
::G
(state
) == "running"} {
644 # Return the stylesheet for the application main page.
646 proc wapp-page-style.css
{} {
649 /* The boxes with black borders use this class
*/
651 border
: 3px groove
#444444;
657 /* Float to the right
(used
for the Run
/Stop
/Reset button
) */
658 .right
{ float
: right
; }
660 /* Style
for the large red warning
at the top of the page
*/
666 /* Styles used by cells
in the
test table
*/
667 .padleft
{ padding-left
: 5ex
; }
668 .nowrap
{ white-space
: nowrap
; }
670 /* Styles
for individual tests
, depending on the outcome
*/
672 .testrunning
{ color
: blue
}
673 .testdone
{ color
: green
}
674 .testfail
{ color
: red
}
678 # URI: /script/${state}.js
680 # The last part of this URI is always "config.js", "running.js" or
681 # "stopped.js", depending on the state of the application. It returns
682 # the javascript part of the front-end for the requested state to the
685 proc wapp-page-script
{} {
686 regexp
{[^
/]*$
} [wapp-param REQUEST_URI
] script
691 set debug $
::G
(debug
)
694 var lElem
= \
["control_platform", "control_test", "control_msvc",
695 "control_jobs", "control_debug"
697 lElem.forEach
(function(e
) {
698 var elem
= document.getElementById
(e
);
699 elem.addEventListener
("change", function() { control.submit
() } );
702 elem
= document.getElementById
("control_tcl");
703 elem.value
= "%string($tcl)"
705 elem
= document.getElementById
("control_keep");
706 elem.checked
= %string
($keep);
708 elem
= document.getElementById
("control_msvc");
709 elem.checked
= %string
($msvc);
711 elem
= document.getElementById
("control_debug");
712 elem.checked
= %string
($debug);
715 if {$script != "config.js"} {
717 var lElem
= \
["control_platform", "control_test",
718 "control_tcl", "control_keep", "control_msvc",
721 lElem.forEach
(function(e
) {
722 var elem
= document.getElementById
(e
);
723 elem.disabled
= true
;
728 if {$script == "running.js"} {
730 function reload_tests
() {
732 .
then( data
=> data.text
() )
734 document.getElementById
("tests").innerHTML
= data
;
737 if( document.getElementById
("result") ){
738 document.location
= document.location
;
740 setTimeout
(reload_tests
, 1000)
745 setTimeout
(reload_tests
, 1000)
752 # This is for debugging only. Serves no other purpose.
754 proc wapp-page-env
{} {
755 wapp-allow-xorigin-params
757 <h1
>Wapp Environment
</h1
>\n<pre
>
758 <pre
>%html
([wapp-debug-env
])</pre
>
762 # URI: /log/dirname/test.log
764 # This URI reads file "dirname/test.log" from disk, wraps it in a <pre>
765 # block, and returns it to the browser. Use for viewing log files.
767 proc wapp-page-log
{} {
768 set log
[string range
[wapp-param REQUEST_URI
] 5 end
]
779 # Print out a usage message. Then do [exit 1].
781 proc wapptest_usage
{} {
783 This Tcl
script is used to
test various configurations of SQLite. By
784 default it uses
"wapp" to provide an interactive interface. Supported
785 command line options
(all optional
) are
:
787 --platform PLATFORM
(which tests to run
)
788 --smoketest (run
"make smoketest" only
)
789 --veryquick (run veryquick.
test only
)
790 --buildonly (build executables
, do not run tests
)
791 --jobs N
(number of concurrent
jobs)
792 --tcl DIR
(where to
find tclConfig.sh
)
793 --deletefiles (delete extra files after each
test)
794 --msvc (Use MS Visual C
)
795 --debug (Also run
[n
]debugging versions of tests
)
796 --noui (do not use wapp
)
801 # Sort command line arguments into two groups: those that belong to wapp,
802 # and those that belong to the application.
803 set WAPPARG
(-server) 1
804 set WAPPARG
(-local) 1
806 set WAPPARG
(-remote-scgi) 1
807 set WAPPARG
(-fromip) 1
808 set WAPPARG
(-nowait) 0
812 for {set i
0} {$i < [llength
$argv]} {incr i
} {
813 set arg
[lindex
$argv $i]
814 if {[string range
$arg 0 1]=="--"} {
815 set arg
[string range
$arg 1 end
]
817 if {[info exists WAPPARG
($arg)]} {
818 lappend lWappArg
$arg
819 if {$WAPPARG($arg)} {
821 lappend lWappArg
[lindex
$argv $i]
824 lappend lTestArg
$arg
829 for {set i
0} {$i < [llength
$lTestArg]} {incr i
} {
830 set opt
[lindex
$lTestArg $i]
831 if {[string range
$opt 0 1]=="--"} {
832 set opt
[string range
$opt 1 end
]
836 if {$i==[llength
$lTestArg]-1} { wapptest_usage
}
838 set arg
[lindex
$lTestArg $i]
839 set lPlatform
[releasetest_data platforms
]
840 if {[lsearch
$lPlatform $arg]<0} {
841 puts stderr
"No such platform: $arg. Platforms are: $lPlatform"
847 -smoketest { set G
(test) Smoketest
}
848 -veryquick { set G
(test) Veryquick
}
849 -buildonly { set G
(test) Build-Only
}
851 if {$i==[llength
$lTestArg]-1} { wapptest_usage
}
853 set G
(jobs) [lindex
$lTestArg $i]
857 if {$i==[llength
$lTestArg]-1} { wapptest_usage
}
859 set G
(tcl
) [lindex
$lTestArg $i]
884 puts stderr
"Unrecognized option: [lindex $lTestArg $i]"