+ # Setup global state for the test server
+ set ::idle_clients {}
+ set ::active_clients {}
+
+ # Enter the event loop to handle clients I/O
+ after 100 test_server_cron
+ vwait forever
+}
+
+# This function gets called 10 times per second, for now does nothing but
+# may be used in the future in order to detect test clients taking too much
+# time to execute the task.
+proc test_server_cron {} {
+}
+
+proc accept_test_clients {fd addr port} {
+ fileevent $fd readable [list read_from_test_client $fd]
+}
+
+# This is the readable handler of our test server. Clients send us messages
+# in the form of a status code such and additional data. Supported
+# status types are:
+#
+# ready: the client is ready to execute the command. Only sent at client
+# startup. The server will queue the client FD in the list of idle
+# clients.
+# testing: just used to signal that a given test started.
+# ok: a test was executed with success.
+# err: a test was executed with an error.
+# exception: there was a runtime exception while executing the test.
+# done: all the specified test file was processed, this test client is
+# ready to accept a new task.
+proc read_from_test_client fd {
+ set bytes [gets $fd]
+ set payload [read $fd $bytes]
+ foreach {status data} $payload break
+ puts "($fd) \[$status\]: $data"
+ if {$status eq {ready}} {
+ signal_idle_client $fd
+ } elseif {$status eq {done}} {
+ signal_idle_client $fd
+ puts "+++ [llength $::active_clients] units still in execution."
+ }
+}
+
+# A new client is idle. Remove it from the list of active clients and
+# if there are still test units to run, launch them.
+proc signal_idle_client fd {
+ # Remove this fd from the list of active clients.
+ set ::active_clients \
+ [lsearch -all -inline -not -exact $::active_clients $fd]
+ # New unit to process?
+ if {$::next_test != [llength $::all_tests]} {
+ puts "Spawing new test process for: [lindex $::all_tests $::next_test]"
+ send_data_packet $fd run [lindex $::all_tests $::next_test]
+ lappend ::active_clients $fd
+ incr ::next_test