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(cfgglob) - Glob pattern that all configurations must match
12 # G(test) - Set to "Normal", "Veryquick", "Smoketest" or "Build-Only".
13 # G(keep) - Boolean. True to delete no files after each test.
14 # G(msvc) - Boolean. True to use MSVC as the compiler.
15 # G(tcl) - Use Tcl from this directory for builds.
16 # G(jobs) - How many sub-processes to run simultaneously.
18 set G
(platform
) $
::tcl_platform
(os
)-$
::tcl_platform
(machine
)
23 set G
(tcl
) [::tcl
::pkgconfig get libdir
,install]
31 proc wapptest_init
{} {
34 set lSave
[list platform
test keep msvc tcl
jobs debug noui stdout cfgglob
]
35 foreach k
$lSave { set A
($k) $G($k) }
37 foreach k
$lSave { set G
($k) $A($k) }
39 # The root of the SQLite source tree.
40 set G
(srcdir
) [file dirname [file dirname [info
script]]]
42 set G
(sqlite_version
) "unknown"
44 # Either "config", "running" or "stopped":
47 set G
(hostname
) "(unknown host)"
48 catch
{ set G
(hostname
) [exec hostname
] }
49 set G
(host) $G(hostname
)
50 append G
(host) " $::tcl_platform(os) $::tcl_platform(osVersion)"
51 append G
(host) " $::tcl_platform(machine) $::tcl_platform(byteOrder)"
54 proc wapptest_run
{} {
57 set G
(state
) "running"
61 wapptest_output
"Running the following for $G(platform). $G(jobs) jobs."
62 foreach t
$G(test_array
) {
63 set config
[dict get
$t config
]
64 set target
[dict get
$t target
]
65 wapptest_output
[format
" %-25s%s" $config $target]
67 wapptest_output
[string repeat
* 70]
70 proc releasetest_data
{args
} {
72 set rtd
[file join $G(srcdir
) test releasetest_data.tcl
]
73 set fd
[open
"|[info nameofexecutable] $rtd $args" r
+]
79 # Generate the text for the box at the top of the UI. The current SQLite
80 # version, according to fossil, along with a warning if there are
81 # uncommitted changes in the checkout.
83 proc generate_fossil_info
{} {
88 set r1
[exec fossil info
]
89 set r2
[exec fossil changes
]
94 foreach line
[split $r1 "\n"] {
95 if {[regexp
{^checkout
: *(.
*)$
} $line -> co
]} {
96 wapp-trim
{ <br
> %html
($co) }
100 if {[string trim
$r2]!=""} {
102 <br
><span class
=warning
>
103 WARNING
: Uncommitted changes
in checkout
109 # If the application is in "config" state, set the contents of the
110 # ::G(test_array) global to reflect the tests that will be run. If the
111 # app is in some other state ("running" or "stopped"), this command
114 proc set_test_array
{} {
116 if { $G(state
)=="config" } {
117 set G
(test_array
) [list
]
119 if {$G(debug
)==0} { set debug
"-nodebug"}
120 foreach
{config target
} [releasetest_data tests
$debug $G(platform
)] {
122 # All configuration names must match $g(cfgglob), which defaults to *
124 if {![string match
-nocase $G(cfgglob
) $config]} continue
126 # If using MSVC, do not run sanitize or valgrind tests. Or the
129 "Sanitize" == $config
130 ||
"checksymbols" in $target
131 ||
"valgrindtest" in $target
136 # If the test mode is not "Normal", override the target.
138 if {$target!="checksymbols" && $G(platform
)!="Failure-Detection"} {
140 Veryquick
{ set target quicktest
}
141 Smoketest
{ set target smoketest
}
143 set target testfixture
144 if {$
::tcl_platform
(platform
)=="windows"} {
145 set target testfixture.exe
151 lappend G
(test_array
) [dict create config
$config target
$target]
156 proc count_tests_and_errors
{name logfile
} {
159 set fd
[open
$logfile rb
]
163 if {[regexp
{(\d
+) errors out of
(\d
+) tests
} $line all nerr ntest
]} {
164 incr G
(test.
$name.nError
) $nerr
165 incr G
(test.
$name.nTest
) $ntest
168 set G
(test.
$name.errmsg
) $line
171 if {[regexp
{runtime error
: +(.
*)} $line all msg
]} {
172 # skip over "value is outside range" errors
173 if {[regexp
{.
* is outside the range of representable
} $line]} {
176 incr G
(test.
$name.nError
)
177 if {$G(test.
$name.errmsg
)==""} {
178 set G
(test.
$name.errmsg
) $msg
182 if {[regexp
{fatal error
+(.
*)} $line all msg
]} {
183 incr G
(test.
$name.nError
)
184 if {$G(test.
$name.errmsg
)==""} {
185 set G
(test.
$name.errmsg
) $msg
188 if {[regexp
{ERROR SUMMARY
: (\d
+) errors.
*} $line all cnt
] && $cnt>0} {
189 incr G
(test.
$name.nError
)
190 if {$G(test.
$name.errmsg
)==""} {
191 set G
(test.
$name.errmsg
) $all
194 if {[regexp
{^VERSION
: 3\.\d
+.\d
+} $line]} {
195 set v
[string range
$line 9 end
]
196 if {$G(sqlite_version
) eq
"unknown"} {
197 set G
(sqlite_version
) $v
198 } elseif
{$G(sqlite_version
) ne
$v} {
199 set G
(test.
$name.errmsg
) "version conflict: {$G(sqlite_version)} vs. {$v}"
204 if {$G(test) == "Build-Only"} {
205 incr G
(test.
$name.nTest
)
206 if {$G(test.
$name.nError
)>0} {
207 set errmsg
"Build failed"
210 set G
(test.
$name.errmsg
) "Test did not complete"
211 if {[file readable core
]} {
212 append G
(test.
$name.errmsg
) " - core file exists"
217 proc wapptest_output
{str
} {
219 if {$G(stdout
)} { puts
$str }
220 if {[info exists G
(log
)]} {
225 proc wapptest_openlog
{} {
227 set G
(log
) [open wapptest-out.txt w
+]
229 proc wapptest_closelog
{} {
235 proc format_seconds
{seconds
} {
236 set min
[format
%.2d
[expr ($seconds / 60) % 60]]
237 set hr
[format
%.2d
[expr $seconds / 3600]]
238 set sec
[format
%.2d
[expr $seconds % 60]]
239 return "$hr:$min:$sec"
242 # This command is invoked once a slave process has finished running its
243 # tests, successfully or otherwise. Parameter $name is the name of the
244 # test, $rc the exit code returned by the slave process.
246 proc slave_test_done
{name rc
} {
248 set G
(test.
$name.
done) [clock seconds
]
249 set G
(test.
$name.nError
) 0
250 set G
(test.
$name.nTest
) 0
251 set G
(test.
$name.errmsg
) ""
253 incr G
(test.
$name.nError
)
255 if {[file exists
$G(test.
$name.log
)]} {
256 count_tests_and_errors
$name $G(test.
$name.log
)
259 # If the "keep files" checkbox is clear, delete all files except for
260 # the executables and test logs. And any core file that is present.
263 testfixture testfixture.exe
265 test.log test-out.txt
268 wapptest_configure.sh
271 foreach f
[glob
-nocomplain [file join $G(test.
$name.dir
) *]] {
273 if {[lsearch
$keeplist $t]<0} {
274 catch
{ file delete
-force $f }
279 # Format a message regarding the success or failure of hte test.
280 set t
[format_seconds
[expr $G(test.
$name.
done) - $G(test.
$name.start
)]]
282 if {$G(test.
$name.nError
)} { set res
"FAILED" }
283 set dots
[string repeat .
[expr 60 - [string length
$name]]]
284 set msg
"$name $dots $res ($t)"
287 if {[info exists G
(test.
$name.errmsg
)] && $G(test.
$name.errmsg
)!=""} {
288 wapptest_output
" $G(test.$name.errmsg)"
292 # This is a fileevent callback invoked each time a file-descriptor that
293 # connects this process to a slave process is readable.
295 proc slave_fileevent
{name
} {
297 set fd
$G(test.
$name.channel
)
300 fconfigure
$fd -blocking 1
301 set rc
[catch
{ close
$fd }]
302 unset G
(test.
$name.channel
)
303 slave_test_done
$name $rc
306 if {[string trim
$line] != ""} { puts
"Trace : $name - \"$line\"" }
312 # Return the contents of the "slave script" - the script run by slave
313 # processes to actually perform the test. All it does is execute the
314 # test script already written to disk (wapptest_cmd.sh or wapptest_cmd.bat).
316 proc wapptest_slave_script
{} {
319 set dir
[file join ..
$G(srcdir
)]
320 set res
[subst
-nocommands {
321 set rc
[catch
"exec sh wapptest_cmd.sh {$dir} >>& test.log" ]
325 set dir
[file nativename
[file normalize
$G(srcdir
)]]
326 set dir
[string map
[list
"\\" "\\\\"] $dir]
327 set res
[subst
-nocommands {
328 set rc
[catch
"exec wapptest_cmd.bat {$dir} >>& test.log" ]
337 # Launch a slave process to run a test.
339 proc slave_launch
{name target dir
} {
342 catch
{ file mkdir
$dir } msg
343 foreach f
[glob
-nocomplain [file join $dir *]] {
344 catch
{ file delete
-force $f }
346 set G
(test.
$name.dir
) $dir
348 # Write the test command to wapptest_cmd.sh|bat.
351 if {$G(msvc
)} { set ext bat
}
352 set fd1
[open
[file join $dir wapptest_cmd.
$ext] w
]
354 puts
$fd1 [releasetest_data
script -msvc $name $target]
356 puts
$fd1 [releasetest_data
script $name $target]
360 # Write the wapptest_run.tcl script to the test directory. To run the
361 # commands in the other two files.
363 set fd3
[open
[file join $dir wapptest_run.tcl
] w
]
364 puts
$fd3 [wapptest_slave_script
]
369 set fd
[open
"|[info nameofexecutable] wapptest_run.tcl" r
+]
372 set G
(test.
$name.channel
) $fd
373 fconfigure
$fd -blocking 0
374 fileevent
$fd readable
[list slave_fileevent
$name]
377 proc do_some_stuff
{} {
380 # Count the number of running jobs. A running job has an entry named
381 # "channel" in its dictionary.
384 foreach j
$G(test_array
) {
385 set name
[dict get
$j config
]
386 if { [info exists G
(test.
$name.channel
)]} { incr nRunning
}
387 if {![info exists G
(test.
$name.
done)]} { set bFinished
0 }
394 foreach j
$G(test_array
) {
395 set name
[dict get
$j config
]
396 incr nError
$G(test.
$name.nError
)
397 incr nTest
$G(test.
$name.nTest
)
400 set G
(result
) "$nError errors from $nTest tests in $nConfig configurations."
401 wapptest_output
[string repeat
* 70]
402 wapptest_output
$G(result
)
404 append G
(result
) " SQLite version $G(sqlite_version)"
405 wapptest_output
" SQLite version $G(sqlite_version)"
407 set G
(state
) "stopped"
409 if {$G(noui
)} { exit 0 }
411 set nLaunch
[expr $G(jobs) - $nRunning]
412 foreach j
$G(test_array
) {
413 if {$nLaunch<=0} break
414 set name
[dict get
$j config
]
415 if { ![info exists G
(test.
$name.channel
)]
416 && ![info exists G
(test.
$name.
done)]
419 set target
[dict get
$j target
]
420 set dir
[string tolower
[string map
{" " _
"-" _
} $name]]
421 set G
(test.
$name.start
) [clock seconds
]
422 set G
(test.
$name.log
) [file join $dir test.log
]
424 slave_launch
$name $target $dir
432 proc generate_select_widget
{label id lOpt opt
} {
434 <label
> %string
($label) </label
>
435 <select id
=%string
($id) name
=%string
($id)>
439 if {$o==$opt} { set selected
" selected=1" }
440 wapp-subst
"<option $selected>$o</option>"
442 wapp-trim
{ </select> }
445 proc generate_main_page
{{extra
{}}} {
449 set hostname
$G(hostname
)
453 <title
> %html
($hostname): wapptest.tcl
</title
>
454 <link rel
="stylesheet" type="text/css" href
="style.css"/>
461 <div class
="border">%string
($host)
466 <div class
="border" id
=controls
>
467 <form action
="control" method
="post" name
="control">
470 # Build the "platform" select widget.
471 set lOpt
[releasetest_data platforms
]
472 generate_select_widget Platform control_platform
$lOpt $G(platform
)
474 # Build the "test" select widget.
475 set lOpt
[list Normal Veryquick Smoketest Build-Only
]
476 generate_select_widget Test control_test
$lOpt $G(test)
478 # Build the "jobs" select widget. Options are 1 to 8.
479 generate_select_widget Jobs control_jobs
{1 2 3 4 5 6 7 8 12 16} $G(jobs)
487 set txt
"STOP Tests!"
497 <input id
=%string
($id) name
=%string
($id) type=submit value
="%string($txt)">
504 <label
> Tcl
: </label
>
505 <input id
="control_tcl" name
="control_tcl"></input
>
506 <label
> Keep files
: </label
>
507 <input id
="control_keep" name
="control_keep" type=checkbox value
=1>
509 <label
> Use MSVC
: </label
>
510 <input id
="control_msvc" name
="control_msvc" type=checkbox value
=1>
511 <label
> Debug tests
: </label
>
512 <input id
="control_debug" name
="control_debug" type=checkbox value
=1>
524 set script "script/$G(state).js"
527 <script src
=%string
($script)></script>
533 proc wapp-default
{} {
537 proc wapp-page-tests
{} {
539 wapp-trim
{ <table class
="border" width
=100%> }
540 foreach t
$G(test_array
) {
541 set config
[dict get
$t config
]
542 set target
[dict get
$t target
]
547 if {[info exists G
(test.
$config.log
)]} {
548 if {[info exists G
(test.
$config.channel
)]} {
549 set class
"testrunning"
550 set seconds
[expr [clock seconds
] - $G(test.
$config.start
)]
551 } elseif
{[info exists G
(test.
$config.
done)]} {
552 if {$G(test.
$config.nError
)>0} {
557 set seconds
[expr $G(test.
$config.
done) - $G(test.
$config.start
)]
559 set seconds
[format_seconds
$seconds]
563 <tr class
=%string
($class)>
564 <td class
="nowrap"> %html
($config)
565 <td class
="padleft nowrap"> %html
($target)
566 <td class
="padleft nowrap"> %html
($seconds)
567 <td class
="padleft nowrap">
569 if {[info exists G
(test.
$config.log
)]} {
570 set log
$G(test.
$config.log
)
573 <a href
=%url
($uri)> %html
($log) </a
>
576 if {[info exists G
(test.
$config.errmsg
)] && $G(test.
$config.errmsg
)!=""} {
577 set errmsg
$G(test.
$config.errmsg
)
580 <td
> <td class
="padleft" colspan
=3> %html
($errmsg)
585 wapp-trim
{ </table
> }
587 if {[info exists G
(result
)]} {
590 <div class
=border id
=result
> %string
($res) </div
>
597 # Whenever the form at the top of the application page is submitted, it
600 proc wapp-page-control
{} {
602 if {$
::G
(state
)=="config"} {
603 set lControls
[list platform
test tcl
jobs keep msvc debug
]
608 set lControls
[list
jobs]
610 foreach v
$lControls {
611 if {[wapp-param-exists control_
$v]} {
612 set G
($v) [wapp-param control_
$v]
616 if {[wapp-param-exists control_run
]} {
617 # This is a "run test" command.
621 if {[wapp-param-exists control_stop
]} {
622 # A "STOP tests" command.
623 set G
(state
) "stopped"
624 set G
(result
) "Test halted by user"
625 foreach j
$G(test_array
) {
626 set name
[dict get
$j config
]
627 if { [info exists G
(test.
$name.channel
)] } {
628 close
$G(test.
$name.channel
)
629 unset G
(test.
$name.channel
)
630 slave_test_done
$name 1
636 if {[wapp-param-exists control_reset
]} {
637 # A "reset app" command.
638 set G
(state
) "config"
642 if {$
::G
(state
) == "running"} {
650 # Return the stylesheet for the application main page.
652 proc wapp-page-style.css
{} {
655 /* The boxes with black borders use this class
*/
657 border
: 3px groove
#444444;
663 /* Float to the right
(used
for the Run
/Stop
/Reset button
) */
664 .right
{ float
: right
; }
666 /* Style
for the large red warning
at the top of the page
*/
672 /* Styles used by cells
in the
test table
*/
673 .padleft
{ padding-left
: 5ex
; }
674 .nowrap
{ white-space
: nowrap
; }
676 /* Styles
for individual tests
, depending on the outcome
*/
678 .testrunning
{ color
: blue
}
679 .testdone
{ color
: green
}
680 .testfail
{ color
: red
}
684 # URI: /script/${state}.js
686 # The last part of this URI is always "config.js", "running.js" or
687 # "stopped.js", depending on the state of the application. It returns
688 # the javascript part of the front-end for the requested state to the
691 proc wapp-page-script
{} {
692 regexp
{[^
/]*$
} [wapp-param REQUEST_URI
] script
697 set debug $
::G
(debug
)
700 var lElem
= \
["control_platform", "control_test", "control_msvc",
701 "control_jobs", "control_debug"
703 lElem.forEach
(function(e
) {
704 var elem
= document.getElementById
(e
);
705 elem.addEventListener
("change", function() { control.submit
() } );
708 elem
= document.getElementById
("control_tcl");
709 elem.value
= "%string($tcl)"
711 elem
= document.getElementById
("control_keep");
712 elem.checked
= %string
($keep);
714 elem
= document.getElementById
("control_msvc");
715 elem.checked
= %string
($msvc);
717 elem
= document.getElementById
("control_debug");
718 elem.checked
= %string
($debug);
721 if {$script != "config.js"} {
723 var lElem
= \
["control_platform", "control_test",
724 "control_tcl", "control_keep", "control_msvc",
727 lElem.forEach
(function(e
) {
728 var elem
= document.getElementById
(e
);
729 elem.disabled
= true
;
734 if {$script == "running.js"} {
736 function reload_tests
() {
738 .
then( data
=> data.text
() )
740 document.getElementById
("tests").innerHTML
= data
;
743 if( document.getElementById
("result") ){
744 document.location
= document.location
;
746 setTimeout
(reload_tests
, 1000)
751 setTimeout
(reload_tests
, 1000)
758 # This is for debugging only. Serves no other purpose.
760 proc wapp-page-env
{} {
761 wapp-allow-xorigin-params
763 <h1
>Wapp Environment
</h1
>\n<pre
>
764 <pre
>%html
([wapp-debug-env
])</pre
>
768 # URI: /log/dirname/test.log
770 # This URI reads file "dirname/test.log" from disk, wraps it in a <pre>
771 # block, and returns it to the browser. Use for viewing log files.
773 proc wapp-page-log
{} {
774 set log
[string range
[wapp-param REQUEST_URI
] 5 end
]
785 # Print out a usage message. Then do [exit 1].
787 proc wapptest_usage
{} {
789 This Tcl
script is used to
test various configurations of SQLite. By
790 default it uses
"wapp" to provide an interactive interface. Supported
791 command line options
(all optional
) are
:
793 --platform PLATFORM
(which tests to run
)
794 --config GLOB
(only run configurations matching GLOB
)
795 --smoketest (run
"make smoketest" only
)
796 --veryquick (run veryquick.
test only
)
797 --buildonly (build executables
, do not run tests
)
798 --jobs N
(number of concurrent
jobs)
799 --tcl DIR
(where to
find tclConfig.sh
)
800 --deletefiles (delete extra files after each
test)
801 --msvc (Use MS Visual C
)
802 --debug (Also run
[n
]debugging versions of tests
)
803 --noui (do not use wapp
)
808 # Sort command line arguments into two groups: those that belong to wapp,
809 # and those that belong to the application.
810 set WAPPARG
(-server) 1
811 set WAPPARG
(-local) 1
813 set WAPPARG
(-remote-scgi) 1
814 set WAPPARG
(-fromip) 1
815 set WAPPARG
(-nowait) 0
819 for {set i
0} {$i < [llength
$argv]} {incr i
} {
820 set arg
[lindex
$argv $i]
821 if {[string range
$arg 0 1]=="--"} {
822 set arg
[string range
$arg 1 end
]
824 if {[info exists WAPPARG
($arg)]} {
825 lappend lWappArg
$arg
826 if {$WAPPARG($arg)} {
828 lappend lWappArg
[lindex
$argv $i]
831 lappend lTestArg
$arg
836 for {set i
0} {$i < [llength
$lTestArg]} {incr i
} {
837 set opt
[lindex
$lTestArg $i]
838 if {[string range
$opt 0 1]=="--"} {
839 set opt
[string range
$opt 1 end
]
843 if {$i==[llength
$lTestArg]-1} { wapptest_usage
}
845 set arg
[lindex
$lTestArg $i]
846 set lPlatform
[releasetest_data platforms
]
847 if {[lsearch
$lPlatform $arg]<0} {
848 puts stderr
"No such platform: $arg. Platforms are: $lPlatform"
854 -smoketest { set G
(test) Smoketest
}
855 -veryquick { set G
(test) Veryquick
}
856 -buildonly { set G
(test) Build-Only
}
858 if {$i==[llength
$lTestArg]-1} { wapptest_usage
}
860 set G
(jobs) [lindex
$lTestArg $i]
864 if {$i==[llength
$lTestArg]-1} { wapptest_usage
}
866 set G
(tcl
) [lindex
$lTestArg $i]
887 if {$i==[llength
$lTestArg]-1} { wapptest_usage
}
889 set G
(cfgglob
) [lindex
$lTestArg $i]
897 puts stderr
"Unrecognized option: [lindex $lTestArg $i]"