Added control over logfile for nbfake, improved bigrun2/3 tests, added itemmap parameter in requirements for mapping items for item wait. bumped version
check-in: 8c12404219 user: mrwellan tags: v1.55
[87cbe68f31]
[be405e8e2e]
# FROM andyjpg on #chicken
(let ((original-exit (exit-handler)))
(exit-handler (lambda (#!optional (exit-code 0))
(printf "Preparing to exit...\n" exit-code)
(for-each (lambda (pid)
(printf "Sending signal/term to ~A\n" pid)
(process-signal pid signal/term)) (children))
(original-exit exit-code))))
1. All run control access to db is direct.
2. All test machines must have megatest available
3. Tests may or may not have file system access to the originating
run area. rsync is used to pull the test area to the home host
if and only if the originating area can not be seen via file
system. NO LONGER TRUE. Rsync is used but file system must be visible.
4. All db access is done via the home host. NOT IMPLEMENTED YET.
REMOTE ACCESS DB LOADS
INFO: (0) Max cached queries was 10
INFO: (0) Number of cached writes 27043
INFO: (0) Average cached write time 15.0634544983915 ms
INFO: (0) Number non-cached queries 71928
INFO: (0) Average non-cached time 5.15547491936381 ms
INFO: (0) Server shutdown complete. Exiting
fdktestqa on Apr 29, 2013: 1812 tests
INFO: (0) Max cached queries was 10
INFO: (0) Number of cached writes 41335
INFO: (0) Average cached write time 206.081553163179 ms
INFO: (0) Number non-cached queries 74289
1. Run all tests2. create run areas, copy in conf and scripts DONE3. Add a host chooser for ssh to launch-tests
1. Confirm that branch transaction-for-sequential-writes content was added to trunk/development
2. Add a host chooser for ssh to launch-tests
4. Run creation timestamp not happening DONE5 . Check for test already in progress, give meaningful message DONE6. Debug xterm creation for test generation DONE7. Capture run info, host, load, freemem at test launch DONE8. Rename to testalot? Nah! I like Megatest10. Run, test and step comment field 11. At end of test scan all tests for this run, if all done update run status to COMPLETED NOT gonna happen. It is up to the test to mark as PASS/FAIL12. state and status lists need to be regexes 13. Test on Chicken 4. DONE14. Try making static executable
3. Try making static executable
15. Log processor script DONE
ok))
;; Do all the connection work, look up the transport type and set up the
;; connection if required.
;;
;; There are two scenarios.
;; 1. We are a test manager and we received *transport-type* and *runremote* via cmdline
;; 2. We are a run tests, list runs or other interactive process and we mush figure out
;; 2. We are a run tests, list runs or other interactive process and we must figure out
;; *transport-type* and *runremote* from the monitor.db
;;
;; client:setup
(define (client:setup #!key (numtries 50))
(define (client:setup #!key (numtries 3))
(if (not *toppath*)
(if (not (setup-for-run))
(begin
(debug:print 0 "ERROR: failed to find megatest.config, exiting")
(exit))))
(push-directory *toppath*) ;; This is probably NOT needed
(debug:print-info 11 "*transport-type* is " *transport-type* ", *runremote* is " *runremote*)
(let* ((hostinfo (if (not *transport-type*) ;; If we dont' already have transport type set then figure it out(open-run-close tasks:get-best-server tasks:open-db)
(let* ((hostinfo (open-run-close tasks:get-best-server tasks:open-db)))
#f))) ;; if have hostinfo then extract the transport type ;; else fall back to fs (debug:print-info 11 "CLIENT SETUP, hostinfo=" hostinfo)
(set! *transport-type* (if hostinfo
(string->symbol (tasks:hostinfo-get-transport hostinfo))
'fs))
;; ;; DEBUG STUFF ;; (if (eq? *transport-type* 'fs)(begin (print "ERROR!!!!!!! refusing to run with transport " *transport-type*)(exit 99))) (debug:print-info 11 "Using transport type of " *transport-type* (if hostinfo (conc " to connect to " hostinfo) ""))
(case *transport-type*
((fs) ;; (if (not *megatest-db*)(set! *megatest-db* (open-db))))
((fs)(if (not *megatest-db*)(set! *megatest-db* (open-db))))
;; we are not doing fs any longer. let's cheat and start up a server ;; if we are falling back on fs (not 100% supported) do an about face and start a server (if (not (equal? (args:get-arg "-transport") "fs")) (begin (set! *transport-type* #f) (system (conc "megatest -list-servers | grep " megatest-version " | grep alive || megatest -server - -daemonize && sleep 3")) (thread-sleep! 1) (if (> numtries 0) (client:setup numtries: (- numtries 1)))))) ((http)
(http-transport:client-connect (tasks:hostinfo-get-interface hostinfo)
(tasks:hostinfo-get-port hostinfo)))
((zmq)
(zmq-transport:client-connect (tasks:hostinfo-get-interface hostinfo)
(tasks:hostinfo-get-port hostinfo)
(tasks:hostinfo-get-pubport hostinfo)))
(else ;; default to fs
(debug:print 0 "ERROR: unrecognised transport type " *transport-type* " attempting to continue with fs")
(set! *transport-type* 'fs)
(set! *megatest-db* (open-db)))))) (set! *megatest-db* (open-db))))
(pop-directory)))
;; client:signal-handler
(define (client:signal-handler signum)
(handle-exceptions
exn
(debug:print " ... exiting ...")
(let ((th1 (make-thread (lambda ()
(define *waiting-queue* (make-hash-table))
(define *test-meta-updated* (make-hash-table))
(define *globalexitstatus* 0) ;; attempt to work around possible thread issues
(define *passnum* 0) ;; when running track calls to run-tests or similar
;; SERVER
(define *my-client-signature* #f)
(define *transport-type* #f)
(define *transport-type* 'fs)
(define *megatest-db* #f)
(define *rpc:listener* #f) ;; if set up for server communication this will hold the tcp port
(define *runremote* #f) ;; if set up for server communication this will hold <host port>
(define *last-db-access* (current-seconds)) ;; update when db is accessed via server
(define *max-cache-size* 0)
(define *logged-in-clients* (make-hash-table))
(define *client-non-blocking-mode* #f)
(define *server-id* #f)
(define *server-info* #f)
(define *time-to-exit* #f)
(define *received-response* #f)
(define *default-numtries* 10)
(define *server-run* #t)
(define *db-write-access* #t)
(define *target* (make-hash-table)) ;; cache the target here; target is keyval1/keyval2/.../keyvalN
(define *keys* (make-hash-table)) ;; cache the keys here
(define *keyvals* (make-hash-table))
(define *toptest-paths* (make-hash-table)) ;; cache toptest path settings here
(define *test-paths* (make-hash-table)) ;; cache test-id to test run paths here
(define *test-ids* (make-hash-table)) ;; cache run-id, testname, and item-path => test-id
(define *test-info* (make-hash-table)) ;; cache the test info records, update the state, status, run_duration etc. from testdat.db
(define *run-info-cache* (make-hash-table)) ;; run info is stable, no need to reget
;; Awful. Please FIXME
(define *env-vars-by-run-id* (make-hash-table))
(define *current-run-name* #f)
;; Testconfig and runconfig caches.
(define *testconfigs* (make-hash-table)) ;; test-name => testconfig
(define *runconfigs* (make-hash-table)) ;; target => runconfig
;; This is a cache of pre-reqs met, don't re-calc in cases where called with same params less than
;; five seconds ago
(define *pre-reqs-met-cache* (make-hash-table))
(define (common:clear-caches)
(set! *target* (make-hash-table))
(set! *keys* (make-hash-table))
(set! *keyvals* (make-hash-table))
(set! *toptest-paths* (make-hash-table))
(set! *test-paths* (make-hash-table))
(set! *test-ids* (make-hash-table))
(set! *test-info* (make-hash-table))
(set! *run-info-cache* (make-hash-table))
(set! *env-vars-by-run-id* (make-hash-table))
(set! *test-id-cache* (make-hash-table)))
;;======================================================================
;; S T A T E S A N D S T A T U S E S
;;======================================================================
;; Debugging stuff
(define *common:std-states*
(list "COMPLETED" "NOT_STARTED" "RUNNING" "REMOTEHOSTSTART" "LAUNCHED" "KILLED" "KILLREQ" "STUCK"))
(define *common:std-statuses*
(list "PASS" "WARN" "FAIL" "CHECK" "n/a" "WAIVED" "SKIP" "DELETED" "STUCK/DEAD"))
;; These are stopping conditions that prevent a test from being run
(define *common:cant-run-states-sym*
'(COMPLETED KILLED WAIVED UNKNOWN INCOMPLETE))
;;======================================================================
;; D E B U G G I N G S T U F F
;;======================================================================
(define *verbosity* 1)
(define *logging* #f)
(define (get-with-default val default)
(let ((val (args:get-arg val)))
(if val val default)))
(define (assoc/default key lst . default)
(let ((res (assoc key lst)))
(if res (cadr res)(if (null? default) #f (car default)))))
;;======================================================================
;; Misc utils
;;======================================================================
;; Convert strings like "5s 2h 3m" => 60x60x2 + 3x60 + 5
(define (common:hms-string->seconds tstr)
(let ((parts (string-split tstr))
(time-secs 0)
;; s=seconds, m=minutes, h=hours, d=days
(trx (regexp "(\\d+)([smhd])")))
(for-each (lambda (part)
(let ((match (string-match trx part)))
(if match
(let ((val (string->number (cadr match)))
(unt (caddr match)))
(if val
(set! time-secs (+ time-secs (* val
(case (string->symbol unt)
((s) 1)
((m) 60)
((h) (* 60 60))
((d) (* 24 60 60))
(else 0))))))))))
parts)
time-secs))
(define (common:version-signature)
(conc megatest-version "-" (substring megatest-fossil-hash 0 4)))
;; one-of args defined
(define (args-defined? . param)
(let ((res #f))
(for-each
(lambda (arg)
(if (args:get-arg arg)(set! res #t)))
param)
(let ((inp (open-input-file fname)))
(let loop ((inl (read-line inp))
(res '()))
(if (eof-object? inl)
(begin
(close-input-port inp)
(reverse res))
(loop (read-line inp)(cons inl)))))
(loop (read-line inp)(cons inl res)))))
'()))
;;======================================================================
;; Write a config
;; 0. Given a refererence data structure "indat"
;; 1. Open the output file and read it into a list
;; 2. Flatten any multiline entries
;;======================================================================
;; Copyright 2006-2012, Matthew Welland.
;; Copyright 2006-2013, Matthew Welland.
;;
;; This program is made available under the GNU GPL version 2.0 or
;; greater. See the accompanying file COPYING for details.
;;
;; This program is distributed WITHOUT ANY WARRANTY; without even the
;; implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
;; PURPOSE.
;; Create the sqlite db for the individual test(s)
(define (open-test-db work-area)
(debug:print-info 11 "open-test-db " work-area)
(if (and work-area
(directory? work-area)
(file-read-access? work-area))
(let* ((dbpath (conc work-area "/testdat.db"))
(tdb-writeable (file-write-access? dbpath))
(dbexists (file-exists? dbpath))
(handler (make-busy-timeout (if (args:get-arg "-override-timeout")
(string->number (args:get-arg "-override-timeout"))
136000))))
(handle-exceptions
exn
(begin
(debug:print 0 "ERROR: problem accessing test db " work-area ", you probably should clean and re-run this test"
(debug:print 2 "ERROR: problem accessing test db " work-area ", you probably should clean and re-run this test"
((condition-property-accessor 'exn 'message) exn))
#f) (set! db (sqlite3:open-database ":memory:"))) ;; open an in-memory db to allow readonly access
(set! db (sqlite3:open-database dbpath)))
(sqlite3:set-busy-handler! db handler)
(if *db-write-access* (sqlite3:set-busy-handler! db handler))
(if (not dbexists)
(begin
;; Why use FULL here? This data is not that critical
(sqlite3:execute db "PRAGMA synchronous = FULL;")
;; (sqlite3:execute db "PRAGMA synchronous = FULL;")
(debug:print-info 11 "Initialized test database " dbpath)
(db:testdb-initialize db)))
;; (sqlite3:execute db "PRAGMA synchronous = 0;")
(debug:print-info 11 "open-test-db END (sucessful)" work-area)
;; now let's test that everything is correct
(handle-exceptions
exn
(db:set-var db "MEGATEST_VERSION" 1.36)
(sqlite3:execute db "ALTER TABLE test_meta ADD COLUMN jobgroup TEXT DEFAULT 'default';"))
((< mver 1.37)
(db:set-var db "MEGATEST_VERSION" 1.37)
(sqlite3:execute db "ALTER TABLE tests ADD COLUMN archived INTEGER DEFAULT 0;"))
((< mver megatest-version)
(db:set-var db "MEGATEST_VERSION" megatest-version))))))
;;======================================================================
;; M A I N T E N A N C E
;;======================================================================
;; select end_time-now from
;; (select testname,item_path,event_time+run_duration as
;; end_time,strftime('%s','now') as now from tests where state in
;; ('RUNNING','REMOTEHOSTSTART','LAUNCED'));
(define (db:find-and-mark-incomplete db #!key (ovr-deadtime #f))
(let* ((incompleted '())
(deadtime-str (configf:lookup *configdat* "setup" "deadtime"))
(deadtime (if (and deadtime-str
(string->number deadtime-str))
(string->number deadtime-str)
7200)) ;; two hours
(run-ids (db:get-run-ids db))) ;; iterate over runs to divy up the calls
(if (number? ovr-deadtime)(set! deadtime ovr-deadtime))
(for-each
(lambda (run-id)
;; in RUNNING or REMOTEHOSTSTART for more than 10 minutes
;;
;; THIS CANNOT WORK. The run_duration is not updated in the central db due to performance concerns.
;; The testdat.db file must be consulted.
;;
;; HOWEVER: this code in run:test seems to work fine
;; (> (- (current-seconds)(+ (db:test-get-event_time testdat)
;; (db:test-get-run_duration testdat)))
;; 600)
(sqlite3:for-each-row
(lambda (test-id)
(set! incompleted (cons test-id incompleted)))
db
"SELECT id FROM tests WHERE run_id=? AND (strftime('%s','now') - event_time - run_duration) > ? AND state IN ('RUNNING','REMOTEHOSTSTART');"
run-id deadtime)
;; in LAUNCHED for more than one day. Could be long due to job queues TODO/BUG: Need override for this in config
;;
(sqlite3:for-each-row
(lambda (test-id)
(set! incompleted (cons test-id incompleted)))
db
"SELECT id FROM tests WHERE run_id=? AND (strftime('%s','now') - event_time - run_duration) > ? AND state IN ('LAUNCHED');"
run-id (* 60 60 24)))
run-ids)
;; These are defunct tests, do not do all the overhead of set-state-status. Force them to INCOMPLETE.
;;
(if (> (length incompleted) 0)
(begin
(debug:print 0 "WARNING: Marking test(s); " (string-intersperse (map conc incompleted) ", ") " as INCOMPLETE")
(sqlite3:execute
db
(conc "UPDATE tests SET state='INCOMPLETE' WHERE id IN ("
(string-intersperse (map conc incompleted) ",")
");"))))))
;; Clean out old junk and vacuum the database
;;
;; Ultimately do something like this:
;;
;; 1. Look at test records either deleted or part of deleted run:
;; a. If test dir exists, set the the test to state='UNKNOWN', Set the run to 'unknown'
;; b. If test dir gone, delete the test record
;; 2. Look at run records
;; a. If have tests that are not deleted, set state='unknown'
;; b. ....
;;
(define (db:clean-up db)
(let ((count-stmt (sqlite3:prepare db "SELECT (SELECT count(id) FROM tests)+(SELECT count(id) FROM runs);"))
(statements
(map (lambda (stmt)
(sqlite3:prepare db stmt))
(list
;; delete all tests that belong to runs that are 'deleted'
"DELETE FROM tests WHERE run_id in (SELECT id FROM runs WHERE state='deleted');"
;; delete all tests that are 'DELETED'
"DELETE FROM tests WHERE state='DELETED';"
;; delete all tests that have no run
"DELETE FROM tests WHERE run_id NOT IN (SELECT DISTINCT id FROM runs);"
;; delete all runs that are state='deleted'
"DELETE FROM runs WHERE state='deleted';"
;; delete empty runs
"DELETE FROM runs WHERE id NOT IN (SELECT DISTINCT r.id FROM runs AS r INNER JOIN tests AS t ON t.run_id=r.id);"
))))
(sqlite3:with-transaction
db
(lambda ()
(sqlite3:for-each-row (lambda (tot)
(debug:print-info 0 "Records count before clean: " tot))
count-stmt)
(map sqlite3:execute statements)
(sqlite3:for-each-row (lambda (tot)
(debug:print-info 0 "Records count after clean: " tot))
count-stmt)))
(map sqlite3:finalize! statements)
(sqlite3:finalize! count-stmt)
(db:find-and-mark-incomplete db)
(sqlite3:execute db "VACUUM;")))
;;======================================================================
;; meta get and set vars
;;======================================================================
;; returns number if string->number is successful, string otherwise
;; also updates *global-delta*
(debug:print-info 11 "db:set-var END " var " " val))
(define (db:del-var db var)
(debug:print-info 11 "db:del-var START " var)
(sqlite3:execute db "DELETE FROM metadat WHERE var=?;" var)
(debug:print-info 11 "db:del-var END " var))
;; use a global for some primitive caching, it is just silly to re-read the db
;; over and over again for the keys since they never change;; use a global for some primitive caching, it is just silly to
;; re-read the db over and over again for the keys since they never
;; change
;; why get the keys from the db? why not get from the *configdat*
;; using keys:config-get-fields?
(define (db:get-keys db)
(if *db-keys* *db-keys*
(let ((res '()))
(debug:print-info 11 "db:get-keys START (cache miss)") (sqlite3:for-each-row
(lambda (key keytype)
(set! res (cons (vector key keytype) res)))
(lambda (key)
(set! res (cons key res)))
db
"SELECT fieldname,fieldtype FROM keys ORDER BY id DESC;")
"SELECT fieldname FROM keys ORDER BY id DESC;")
(set! *db-keys* res)
(debug:print-info 11 "db:get-keys END (cache miss)") res)))
;;
(define (db:get-value-by-header row header field)
(debug:print-info 4 "db:get-value-by-header row: " row " header: " header " field: " field)
(if (null? header) #f
(let loop ((hed (car header))
(tal (cdr header))
(n 0))
(if (equal? hed field)
;; T E S T S
;;======================================================================
;; states and statuses are lists, turn them into ("PASS","FAIL"...) and use NOT IN
;; i.e. these lists define what to NOT show.
;; states and statuses are required to be lists, empty is ok
;; not-in #t = above behaviour, #f = must match
(define (db:get-tests-for-run db run-id testpatt states statuses
#!key (not-in #t)
(sort-by #f) ;; 'rundir 'event_time(qryvals "id,run_id,testname,state,status,event_time,host,cpuload,diskfree,uname,rundir,item_path,run_duration,final_logf,comment")
(define (db:get-tests-for-run db run-id testpatt states statuses offset limit not-in sort-by sort-order
#!key
(qryvals #f))
(let* ((qryvalstr (case qryvals
((shortlist) "id,run_id,testname,item_path,state,status")
((#f) "id,run_id,testname,state,status,event_time,host,cpuload,diskfree,uname,rundir,item_path,run_duration,final_logf,comment")
) (debug:print-info 11 "db:get-tests-for-run START run-id=" run-id ", testpatt=" testpatt ", states=" states ", statuses=" statuses ", not-in=" not-in ", sort-by=" sort-by) (let* ((res '())
(else qryvals)))
(res '())
;; if states or statuses are null then assume match all when not-in is false
(states-qry (if (null? states)
#f
(conc " state "
(if not-in "NOT" "") " IN ('"
(if not-in
" NOT IN ('"
" IN ('")
(string-intersperse states "','")
"')")))
(statuses-qry (if (null? statuses)
#f
(conc " status "
(if not-in "NOT" "") " IN ('"
(if not-in
" NOT IN ('"
" IN ('")
(string-intersperse statuses "','")
"')")))
(states-statuses-qry
(cond
((and states-qry statuses-qry)
(conc " AND ( " states-qry " AND " statuses-qry " ) "))
(states-qry
(conc " AND " states-qry))
(statuses-qry
(conc " AND " statuses-qry))
(else "")))
(tests-match-qry (tests:match->sqlqry testpatt))
(qry (conc "SELECT " qryvals
" FROM tests WHERE run_id=? "
(qry (conc "SELECT " qryvalstr
" FROM tests WHERE run_id=? AND state != 'DELETED' "
(if states-qry (conc " AND " states-qry) "")(if statuses-qry (conc " AND " statuses-qry) "") states-statuses-qry
(if tests-match-qry (conc " AND (" tests-match-qry ") ") "")
(case sort-by
((rundir) " ORDER BY length(rundir) DESC;")
((event_time) " ORDER BY event_time ASC;")
(else ";"))
)))
((rundir) " ORDER BY length(rundir) ")
((testname) (conc " ORDER BY testname " (if sort-order (conc sort-order ",") "") " item_path "))
((statestatus) (conc " ORDER BY state " (if sort-order (conc sort-order ",") "") " status "))
((event_time) " ORDER BY event_time ")
(else (if (string? sort-by)
(conc " ORDER BY " sort-by)
"")))
(if sort-order sort-order "")
(if limit (conc " LIMIT " limit) "")
(if offset (conc " OFFSET " offset) "")
";"
)))
(debug:print-info 8 "db:get-tests-for-run qry=" qry)
(sqlite3:for-each-row
(lambda (a . b) ;; id run-id testname state status event-time host cpuload diskfree uname rundir item-path run-duration final-logf comment)
(set! res (cons (apply vector a b) res))) ;; id run-id testname state status event-time host cpuload diskfree uname rundir item-path run-duration final-logf comment) res)))
db
qry
run-id
)
(case qryvals
((shortlist)(map db:test-short-record->norm res))
((#f) res)
(else res))))
(debug:print-info 11 "db:get-tests-for-run START run-id=" run-id ", testpatt=" testpatt ", states=" states ", statuses=" statuses ", not-in=" not-in ", sort-by=" sort-by)
(define (db:test-short-record->norm inrec)
;; "id,run_id,testname,item_path,state,status"
;; "id,run_id,testname,state,status,event_time,host,cpuload,diskfree,uname,rundir,item_path,run_duration,final_logf,comment
(vector (vector-ref inrec 0) ;; id
(vector-ref inrec 1) ;; run_id
(vector-ref inrec 2) ;; testname
(vector-ref inrec 4) ;; state
(vector-ref inrec 5) ;; status
-1 "" -1 -1 "" "-"
(vector-ref inrec 3) ;; item-path
-1 "-" "-"))
(define (db:get-tests-for-run-state-status db run-id testpatt)
(let ((res '())
(tests-match-qry (tests:match->sqlqry testpatt)))
(sqlite3:for-each-row
(lambda (id testname item-path state status)
;; id,run_id,testname,state,status,event_time,host,cpuload,diskfree,uname,rundir,item_path,run_duration,final_logf,comment
(set! res (cons (vector id run-id testname state status -1 "" -1 -1 "" "-" item-path -1 "-" "-") res)))
db
(conc "SELECT id,testname,item_path,state,status FROM tests WHERE run_id=? "
(if tests-match-qry (conc " AND (" tests-match-qry ") ") ""))
run-id)
res))
(define (db:get-testinfo-state-status db test-id)
(let ((res #f))
(sqlite3:for-each-row
(lambda (run-id testname item-path state status)
;; id,run_id,testname,state,status,event_time,host,cpuload,diskfree,uname,rundir,item_path,run_duration,final_logf,comment
(set! res (vector test-id run-id testname state status -1 "" -1 -1 "" "-" item-path -1 "-" "-")))
db
"SELECT run_id,testname,item_path,state,status FROM tests WHERE id=?;"
test-id)
res))
;; get a useful subset of the tests data (used in dashboard
;; use db:mintests-get-{id ,run_id,testname ...}
(define (db:get-tests-for-runs-mindata db run-ids testpatt states status)
(db:get-tests-for-runs db run-ids testpatt states status qryvals: "id,run_id,testname,state,status,event_time,item_path"))
(define (db:get-tests-for-runs-mindata db run-ids testpatt states status not-in)
(db:get-tests-for-runs db run-ids testpatt states status not-in: not-in qryvals: "id,run_id,testname,state,status,event_time,item_path"))
;; NB // This is get tests for "runs" (note the plural!!)
;;
;; states and statuses are lists, turn them into ("PASS","FAIL"...) and use NOT IN
;; i.e. these lists define what to NOT show.
;; states and statuses are required to be lists, empty is ok
;; not-in #t = above behaviour, #f = must match
;; run-ids is a list of run-ids or a single number
;; run-ids is a list of run-ids or a single number or #f for all runs
(define (db:get-tests-for-runs db run-ids testpatt states statuses
#!key (not-in #t)
(sort-by #f)
(qryvals "id,run_id,testname,state,status,event_time,host,cpuload,diskfree,uname,rundir,item_path,run_duration,final_logf,comment")) ;; 'rundir 'event_time
#!key (not-in #t)
(sort-by #f)
(qryvals "id,run_id,testname,state,status,event_time,host,cpuload,diskfree,uname,rundir,item_path,run_duration,final_logf,comment")) ;; 'rundir 'event_time
(debug:print-info 11 "db:get-tests-for-run START run-ids=" run-ids ", testpatt=" testpatt ", states=" states ", statuses=" statuses ", not-in=" not-in ", sort-by=" sort-by) (let* ((res '())
;; if states or statuses are null then assume match all when not-in is false
(states-qry (if (null? states)
#f
(conc " state "
(if not-in "NOT" "")
" IN ('"
(string-intersperse states "','")
"')")))
(statuses-qry (if (null? statuses)
#f
(conc " status "
(if not-in "NOT" "")
" IN ('"
(string-intersperse statuses "','")
"')")))
(tests-match-qry (tests:match->sqlqry testpatt))
(qry (conc "SELECT " qryvals
" FROM tests WHERE " " FROM tests WHERE state != 'DELETED' "
(if run-ids
(if (list? run-ids)
(conc " run_id in (" (string-intersperse (map conc run-ids) ",") ") ")
(conc "run_id=" run-ids " "))
(conc "AND run_id IN (" (string-intersperse (map conc run-ids) ",") ") ")
(conc "AND run_id=" run-ids " "))
" ") ;; #f => run-ids don't filter on run-ids
(if states-qry (conc " AND " states-qry) "")
(if statuses-qry (conc " AND " statuses-qry) "")
(if tests-match-qry (conc " AND (" tests-match-qry ") ") "")
(case sort-by
((rundir) " ORDER BY length(rundir) DESC;")
((event_time) " ORDER BY event_time ASC;")
(else ";"))
)))
(debug:print-info 8 "db:get-tests-for-run qry=" qry)
)))
(debug:print-info 8 "db:get-tests-for-runs qry=" qry)
(sqlite3:for-each-row
(lambda (a . b) ;; id run-id testname state status event-time host cpuload diskfree uname rundir item-path run-duration final-logf comment)
(set! res (cons (apply vector a b) res))) ;; id run-id testname state status event-time host cpuload diskfree uname rundir item-path run-duration final-logf comment) res)))
db
qry
)
(debug:print-info 11 "db:get-tests-for-run START run-ids=" run-ids ", testpatt=" testpatt ", states=" states ", statuses=" statuses ", not-in=" not-in ", sort-by=" sort-by) res))
;; this one is a bit broken BUG FIXME
(define (db:delete-test-step-records db test-id #!key (work-area #f))
;; Breaking it into two queries for better file access interleaving
(let* ((tdb (db:open-test-db-by-test-id db test-id work-area: work-area)))
;; test db's can go away - must check every time
(if tdb (if (sqlite3:database? tdb)
(begin
(sqlite3:execute tdb "DELETE FROM test_steps;")
(sqlite3:execute tdb "DELETE FROM test_data;")
(sqlite3:finalize! tdb)))))
;;
(define (db:delete-test-records db tdb test-id #!key (force #f))
(common:clear-caches)
(if tdb
(begin
(sqlite3:execute tdb "DELETE FROM test_steps;")
(sqlite3:execute tdb "DELETE FROM test_data;")))
;; (sqlite3:execute db "DELETE FROM tests WHERE id=?;" test-id))
(if db
(begin
(sqlite3:execute db "DELETE FROM test_steps WHERE test_id=?;" test-id)
(sqlite3:execute db "DELETE FROM test_data WHERE test_id=?;" test-id)
(if force
(sqlite3:execute db "DELETE FROM tests WHERE id=?;" test-id)
(sqlite3:execute db "UPDATE tests SET state='DELETED',status='n/a' WHERE id=?;" test-id)))))
(sqlite3:execute db "UPDATE tests SET state='DELETED',status='n/a',comment='' WHERE id=?;" test-id)))))
(define (db:delete-tests-for-run db run-id)
(common:clear-caches)
(sqlite3:execute db "DELETE FROM tests WHERE run_id=?;" run-id))
(define (db:delete-old-deleted-test-records db)
(common:clear-caches)
(if currstate (conc "state='" currstate "' AND ") "")
(if currstatus (conc "status='" currstatus "' AND ") "")
" run_id=? AND testname=? AND NOT (item_path='' AND testname in (SELECT DISTINCT testname FROM tests WHERE testname=? AND item_path != ''));")))
;;(debug:print 0 "QRY: " qry)
(sqlite3:execute db qry run-id newstate newstatus testname testname)))
testnames))
(define (cdb:set-tests-state-status-faster serverdat run-id testnames currstate currstatus newstate newstatus) ;; Convert #f to wildcard % (if (null? testnames) #t (let ((currstate (if currstate currstate "%")) (currstatus (if currstatus currstatus "%"))) (let loop ((hed (car testnames)) (tal (cdr testnames)) (thr '())) (let ((th1 (if newstate (create-thread (cbd:client-call serverdat 'update-test-state #t *default-numtries* newstate currstate run-id testname testname)) #f)) (th2 (if newstatus (create-thread (cbd:client-call serverdat 'update-test-status #t *default-numtries* newstatus currstatus run-id testname testname)) #f))) (thread-start! th1) (thread-start! th2) (if (null? tal) (loop (car tal)(cdr tal)(cons th1 (cons th2 thr))) (for-each (lambda (th) (if th (thread-join! th))) thr)))))))(define (cdb:delete-tests-in-state serverdat run-id state)
(common:clear-caches)
(cdb:client-call serverdat 'delete-tests-in-state #t *default-numtries* run-id state))
(define (cdb:tests-update-cpuload-diskfree serverdat test-id cpuload diskfree)
(cdb:client-call serverdat 'update-cpuload-diskfree #t *default-numtries* cpuload diskfree test-id))
(define (cdb:tests-update-run-duration serverdat test-id minutes)
(cdb:client-call serverdat 'update-run-duration #t *default-numtries* minutes test-id))
(define (cdb:tests-update-uname-host serverdat test-id uname hostname)
(cdb:client-call serverdat 'update-uname-host #t *default-numtries* test-id uname hostname))
(cdb:client-call serverdat 'update-uname-host #t *default-numtries* uname hostname test-id))
;; speed up for common cases with a little logic
;; NB// Ultimately this will be deprecated in deference to mt:test-set-state-status-by-id
;;
(define (db:test-set-state-status-by-id db test-id newstate newstatus newcomment)
(cond
((and newstate newstatus newcomment)
(sqlite3:exectute db "UPDATE tests SET state=?,status=?,comment=? WHERE id=?;" newstate newstatus test-id))
(sqlite3:execute db "UPDATE tests SET state=?,status=?,comment=? WHERE id=?;" newstate newstatus newcomment test-id))
((and newstate newstatus)
(sqlite3:execute db "UPDATE tests SET state=?,status=? WHERE id=?;" newstate newstatus test-id))
(else
(if newstate (sqlite3:execute db "UPDATE tests SET state=? WHERE id=?;" newstate test-id))
(if newstatus (sqlite3:execute db "UPDATE tests SET status=? WHERE id=?;" newstatus test-id))
(if newcomment (sqlite3:execute db "UPDATE tests SET comment=? WHERE id=?;" newcomment test-id))))) (if newcomment (sqlite3:execute db "UPDATE tests SET comment=? WHERE id=?;" newcomment test-id))))
(mt:process-triggers test-id newstate newstatus))
;; Never used, but should be?
(define (db:test-set-state-status-by-run-id-testname db run-id test-name item-path status state)
(sqlite3:execute db "UPDATE tests SET state=?,status=?,event_time=strftime('%s','now') WHERE run_id=? AND testname=? AND item_path=?;"
state status run-id test-name item-path))
state status run-id test-name item-path))
(define (db:get-count-tests-running db)
(let ((res 0))
(sqlite3:for-each-row
(lambda (count)
(set! res count))
db
"SELECT count(id) FROM tests WHERE state in ('RUNNING','LAUNCHED','REMOTEHOSTSTART');")
"SELECT count(id) FROM tests WHERE state in ('RUNNING','LAUNCHED','REMOTEHOSTSTART') AND run_id NOT IN (SELECT id FROM runs WHERE state='deleted');")
res))
(define (db:get-count-tests-running-for-run-id db run-id)
(let ((res 0))
(sqlite3:for-each-row
(lambda (count)
(set! res count))
db
"SELECT count(id) FROM tests WHERE state in ('RUNNING','LAUNCHED','REMOTEHOSTSTART') AND run_id=?;" run-id)
res))
(define (db:get-running-stats db)
(let ((res '()))
(sqlite3:for-each-row
(lambda (state count)
(set! res (cons (list state count) res)))
db
"SELECT state,count(id) FROM tests GROUP BY state ORDER BY id DESC;")
res))
(define (db:get-count-tests-running-in-jobgroup db jobgroup)
(if (not jobgroup)
0 ;;
(let ((res 0))
(sqlite3:for-each-row
(lambda (count)
(set! res count))
db
"SELECT count(id) FROM tests WHERE state = 'RUNNING' OR state = 'LAUNCHED' OR state = 'REMOTEHOSTSTART'
AND testname in (SELECT testname FROM test_meta WHERE jobgroup=?;"
"SELECT count(id) FROM tests WHERE state in ('RUNNING','LAUNCHED','REMOTEHOSTSTART')
AND testname in (SELECT testname FROM test_meta WHERE jobgroup=?);"
jobgroup)
res)))
;; done with run when:
;; 0 tests in LAUNCHED, NOT_STARTED, REMOTEHOSTSTART, RUNNING
(define (db:estimated-tests-remaining db run-id)
(let ((res 0))
(string-split target "/"))
" AND "))
(testqry (tests:match->sqlqry testpatt))
(qrystr (conc "SELECT t.rundir FROM tests AS t INNER JOIN runs AS r ON t.run_id=r.id WHERE "
keystr " AND r.runname LIKE '" runname "' AND " testqry
" AND t.state LIKE '" statepatt "' AND t.status LIKE '" statuspatt
"' ORDER BY t.event_time ASC;")))
(debug:print 3 "qrystr: " qrystr) (sqlite3:for-each-row
(lambda (p)
(set! res (cons p res)))
db
qrystr)
res))
(define (db:test-get-paths-matching-keynames-target-new db keynames target res
#!key
(testpatt "%")
(statepatt "%")
(statuspatt "%")
(runname "%"))
(let* ((row-ids '())
(keystr (string-intersperse
(map (lambda (key val)
(conc key " like '" val "'"))
keynames
(string-split target "/"))
" AND "))
(testqry (tests:match->sqlqry testpatt))
(runsqry (sqlite3:prepare db (conc "SELECT id FROM runs WHERE " keystr " AND runname LIKE '" runname "';")))
(tstsqry (sqlite3:prepare db (conc "SELECT rundir FROM tests WHERE run_id=? AND " testqry " AND state LIKE '" statepatt "' AND status LIKE '" statuspatt "' ORDER BY event_time ASC;"))))
(sqlite3:for-each-row
(lambda (rid)
(set! row-ids (cons rid row-ids)))
runsqry)
(for-each (lambda (rid)
(sqlite3:for-each-row
(lambda (p)
(set! res (cons p res)))
tstsqry rid))
row-ids)
(sqlite3:finalize! tstsqry)
(sqlite3:finalize! runsqry)
res))
;; look through tests from matching runs for a file
(define (db:test-get-first-path-matching db keynames target fname)
;; [refpaths] is the section where references to other megatest databases are stored
(let ((mt-paths (configf:get-section "refpaths"))
(res (db:test-get-paths-matching db keynames target fname)))
(let loop ((pathdat (if (null? paths) #f (car mt-paths)))
;;======================================================================
;; A G R E G A T E D T R A N S A C T I O N D B W R I T E S
;;======================================================================
(define db:queries
(list '(register-test "INSERT OR IGNORE INTO tests (run_id,testname,event_time,item_path,state,status) VALUES (?,?,strftime('%s','now'),?,'NOT_STARTED','n/a');")
;; Test state and status
'(set-test-state "UPDATE tests SET state=? WHERE id=?;")
'(set-test-status "UPDATE tests SET state=? WHERE id=?;")
'(state-status "UPDATE tests SET state=?,status=? WHERE id=?;")
'(state-status-msg "UPDATE tests SET state=?,status=?,comment=? WHERE id=?;")
;; Test comment
'(set-test-comment "UPDATE tests SET comment=? WHERE id=?;")
'(set-test-start-time "UPDATE tests SET event_time=strftime('%s','now') WHERE id=?;")
'(pass-fail-counts "UPDATE tests SET fail_count=?,pass_count=? WHERE id=?;")
;; test_data-pf-rollup is used to set a tests PASS/FAIL based on the pass/fail info from the steps
'(test_data-pf-rollup "UPDATE tests
SET status=CASE WHEN (SELECT fail_count FROM tests WHERE id=?) > 0
THEN 'FAIL'
WHEN (SELECT pass_count FROM tests WHERE id=?) > 0 AND
(SELECT status FROM tests WHERE id=?) NOT IN ('WARN','FAIL')
THEN 'PASS'
ELSE status
END WHERE id=?;")
'(test-set-log "UPDATE tests SET final_logf=? WHERE id=?;")
'(test-set-rundir-by-test-id "UPDATE tests SET rundir=? WHERE id=?")
'(test-set-rundir "UPDATE tests SET rundir=? WHERE run_id=? AND testname=? AND item_path=?;")
'(delete-tests-in-state "DELETE FROM tests WHERE state=? AND run_id=?;")
'(tests:test-set-toplog "UPDATE tests SET final_logf=? WHERE run_id=? AND testname=? AND item_path='';")
'(update-cpuload-diskfree "UPDATE tests SET cpuload=?,diskfree=? WHERE id=?;")
'(update-run-duration "UPDATE tests SET run_duration=? WHERE id=?;")
'(update-uname-host "UPDATE tests SET uname=?,host=? WHERE id=?;")
'(update-test-state "UPDATE tests SET state=? WHERE state=? AND run_id=? AND testname=? AND NOT (item_path='' AND testname IN (SELECT DISTINCT testname FROM tests WHERE testname=? AND item_path != ''));")
'(update-test-status "UPDATE tests SET status=? WHERE status like ? AND run_id=? AND testname=? AND NOT (item_path='' AND testname IN (SELECT DISTINCT testname FROM tests WHERE testname=? AND item_path != ''));")
;; stuff for roll-up-pass-fail-counts
'(update-fail-pass-counts "UPDATE tests
SET fail_count=(SELECT count(id) FROM tests WHERE run_id=? AND testname=? AND item_path != '' AND status IN ('FAIL','CHECK')),
pass_count=(SELECT count(id) FROM tests WHERE run_id=? AND testname=? AND item_path != '' AND status IN ('PASS','WARN','WAIVED'))
WHERE run_id=? AND testname=? AND item_path='';")
'(top-test-set-running "UPDATE tests SET state='RUNNING' WHERE run_id=? AND testname=? AND item_path='';")
'(top-test-set-per-pf-counts "UPDATE tests
SET state=CASE
WHEN (SELECT count(id) FROM tests
WHERE run_id=? AND testname=?
AND item_path != ''
AND state in ('RUNNING','NOT_STARTED','LAUNCHED','REMOTEHOSTSTART')) > 0 THEN 'RUNNING'
ELSE 'COMPLETED' END,
status=CASE
WHEN fail_count > 0 THEN 'FAIL'
WHEN pass_count > 0 AND fail_count=0 THEN 'PASS'
WHEN (SELECT count(id) FROM tests
WHERE run_id=? AND testname=?
AND item_path != ''
AND status = 'SKIP') > 0 THEN 'SKIP'
ELSE 'UNKNOWN' END
WHERE run_id=? AND testname=? AND item_path='';")
))
))
;; do not run these as part of the transaction
(define db:special-queries '(rollup-tests-pass-fail
db:roll-up-pass-fail-counts
login
immediate
;; db:roll-up-pass-fail-counts ;; WHY NOT!?
login
immediate
flush
sync
set-verbosity
killserver
))
;; not used, intended to indicate to run in calling process
(hash-table-set! *logged-in-clients* client-key (current-seconds))
(server:reply return-address qry-sig #t '(#t "successful login"))) ;; path matches - pass! Should vet the caller at this time ...
(server:reply return-address qry-sig #f (list #f (conc "Login failed due to mismatch paths: " calling-path ", " *toppath*)))))))
((flush sync)
(server:reply return-address qry-sig #t 1)) ;; (length data)))
((set-verbosity)
(set! *verbosity* (car params))
(server:reply return-address qry-sig #t '(#t *verbosity*)))
(server:reply return-address qry-sig #t (list #t *verbosity*)))
((killserver)
(let ((hostname (car *runremote*))
(port (cadr *runremote*))
(pid (car params)))
(debug:print 0 "WARNING: Server going down in 15 seconds by user request!")
(open-run-close tasks:server-deregister tasks:open-db
(debug:print 0 "WARNING: Server on " hostname ":" port " going down by user request!")
(debug:print-info 1 "current pid=" (current-process-id))
(open-run-close tasks:server-deregister tasks:open-db
(car *runremote*)pullport: (cadr *runremote*))
(thread-start! (make-thread (lambda ()(thread-sleep! 15)(exit))))
(server:reply return-address qry-sig #t '(#t "exit process started")))
hostname
port: port)
(set! *server-run* #f)
(thread-sleep! 3)
(process-signal pid signal/kill)
(server:reply return-address qry-sig #t '(#t "exit process started"))))
(else ;; not a command, i.e. is a query
(debug:print 0 "ERROR: Unrecognised query/command " stmt-key)
(server:reply return-address qry-sig #f 'failed)))))
(else
(debug:print-info 11 "Executing " stmt-key " for " params)
(apply sqlite3:execute (hash-table-ref queries stmt-key) params)
(server:reply return-address qry-sig #t #t)))))))
(define (db:test-get-records-for-index-file db run-id test-name)
(let ((res '()))
(sqlite3:for-each-row
(lambda (id itempath state status run_duration logf comment)
(set! res (cons (vector id itempath state status run_duration logf comment) res)))
db
"SELECT id,item_path,state,status,run_duration,final_logf,comment FROM tests WHERE run_id=? AND testname=? AND item_path != '';"
run-id test-name)
res))
;; Rollup the pass/fail counts from itemized tests into fail_count and pass_count;; NOTE: Is this duplicating (db:test-data-rollup db test-id status) ????(define (db:roll-up-pass-fail-counts db run-id test-name item-path status) ;; (cdb:flush-queue *runremote*) (if (and (not (equal? item-path "")) (member status '("PASS" "WARN" "FAIL" "WAIVED" "RUNNING" "CHECK" "SKIP"))) (begin (sqlite3:execute db "UPDATE tests SET fail_count=(SELECT count(id) FROM tests WHERE run_id=? AND testname=? AND item_path != '' AND status IN ('FAIL','CHECK')), pass_count=(SELECT count(id) FROM tests WHERE run_id=? AND testname=? AND item_path != '' AND status IN ('PASS','WARN','WAIVED')) WHERE run_id=? AND testname=? AND item_path='';" run-id test-name run-id test-name run-id test-name) ;; (thread-sleep! 0.1) ;; give other processes a chance here, no, better to be done ASAP? (if (equal? status "RUNNING") ;; running takes priority over all other states, force the test state to RUNNING (sqlite3:execute db "UPDATE tests SET state=? WHERE run_id=? AND testname=? AND item_path='';" "RUNNING" run-id test-name) (sqlite3:execute db "UPDATE tests SET state=CASE WHEN (SELECT count(id) FROM tests WHERE run_id=? AND testname=? AND item_path != '' AND state in ('RUNNING','NOT_STARTED')) > 0 THEN 'RUNNING' ELSE 'COMPLETED' END, status=CASE WHEN fail_count > 0 THEN 'FAIL' WHEN pass_count > 0 AND fail_count=0 THEN 'PASS' WHEN (SELECT count(id) FROM tests WHERE run_id=? AND testname=? AND item_path != '' AND status = 'SKIP') > 0 THEN 'SKIP' ELSE 'UNKNOWN' END WHERE run_id=? AND testname=? AND item_path='';" run-id test-name run-id test-name run-id test-name)) #f) #f));;======================================================================
;; Tests meta data
;;======================================================================
;; read the record given a testname
(define (db:testmeta-get-record db testname)
(let ((res #f))
(sqlite3:for-each-row
(lambda (id testname author owner description reviewed iterated avg_runtime avg_disk tags)
(set! res (vector id testname author owner description reviewed iterated avg_runtime avg_disk tags)))
db "SELECT id,testname,author,owner,description,reviewed,iterated,avg_runtime,avg_disk,tags FROM test_meta WHERE testname=?;"
(lambda (id testname author owner description reviewed iterated avg_runtime avg_disk tags jobgroup)
(set! res (vector id testname author owner description reviewed iterated avg_runtime avg_disk tags jobgroup)))
db "SELECT id,testname,author,owner,description,reviewed,iterated,avg_runtime,avg_disk,tags,jobgroup FROM test_meta WHERE testname=?;"
testname)
res))
;; create a new record for a given testname
(define (db:testmeta-add-record db testname)
(sqlite3:execute db "INSERT OR IGNORE INTO test_meta (testname,author,owner,description,reviewed,iterated,avg_runtime,avg_disk,tags) VALUES (?,'','','','','','','','');" testname))
;; update one of the testmeta fields
(define (db:testmeta-update-field db testname field value)
(sqlite3:execute db (conc "UPDATE test_meta SET " field "=? WHERE testname=?;") value testname))
;;======================================================================
;; T E S T D A T A
;;======================================================================
(define (db:csv->test-data db test-id csvdata #!key (work-area #f))
(debug:print 4 "test-id " test-id ", csvdata: " csvdata)
(let ((tdb (db:open-test-db-by-test-id db test-id work-area: work-area)))
(if tdb (if (sqlite3:database? tdb)
(let ((csvlist (csv->list (make-csv-reader
(open-input-string csvdata)
'((strip-leading-whitespace? #t)
(strip-trailing-whitespace? #t)) )))) ;; (csv->list csvdata)))
(for-each
(lambda (csvrow)
(let* ((padded-row (take (append csvrow (list #f #f #f #f #f #f #f #f #f)) 9))
test-id category variable value expected tol units (if comment comment "") status type)))
csvlist)
(sqlite3:finalize! tdb)))))
;; get a list of test_data records matching categorypatt
(define (db:read-test-data db test-id categorypatt #!key (work-area #f))
(let ((tdb (db:open-test-db-by-test-id db test-id work-area: work-area)))
(if tdb (if (sqlite3:database? tdb)
(let ((res '()))
(sqlite3:for-each-row
(lambda (id test_id category variable value expected tol units comment status type)
(set! res (cons (vector id test_id category variable value expected tol units comment status type) res)))
tdb
"SELECT id,test_id,category,variable,value,expected,tol,units,comment,status,type FROM test_data WHERE test_id=? AND category LIKE ? ORDER BY category,variable;" test-id categorypatt)
(sqlite3:finalize! tdb)
;; look at the test_data status field,
;; if all are pass (any case) and the test status is PASS or NULL or '' then set test status to PASS.
;; if one or more are fail (any case) then set test status to PASS, non "pass" or "fail" are ignored
(define (db:test-data-rollup db test-id status #!key (work-area #f))
(let ((tdb (db:open-test-db-by-test-id db test-id work-area: work-area))
(fail-count 0)
(pass-count 0))
(if tdb (if (sqlite3:database? tdb)
(begin
(sqlite3:for-each-row
(lambda (fcount pcount)
(set! fail-count fcount)
(set! pass-count pcount))
tdb
"SELECT (SELECT count(id) FROM test_data WHERE test_id=? AND status like 'fail') AS fail_count,
;; (thread-sleep! 1) ;; play nice with the queue by ensuring the rollup is at least 10ms later than the set
;; if the test is not FAIL then set status based on the fail and pass counts.
(cdb:test-rollup-test_data-pass-fail *runremote* test-id)
;; (sqlite3:execute
;; db ;;; NOTE: Should this be WARN,FAIL? A WARN is not a FAIL????? BUG FIXME
;; "UPDATE tests
;; SET status=CASE WHEN (SELECT fail_count FROM tests WHERE id=?) > 0
;; THEN 'FAIL'
;; WHEN (SELECT pass_count FROM tests WHERE id=?) > 0 AND
;; (SELECT status FROM tests WHERE id=?) NOT IN ('WARN','FAIL')
;; THEN 'PASS'
;; ELSE status
;; END WHERE id=?;"
;; SET status=CASE WHEN (SELECT fail_count FROM tests WHERE id=?) > 0
;; THEN 'FAIL'
;; WHEN (SELECT pass_count FROM tests WHERE id=?) > 0 AND
;; (SELECT status FROM tests WHERE id=?) NOT IN ('WARN','FAIL')
;; THEN 'PASS'
;; ELSE status
;; END WHERE id=?;"
;; test-id test-id test-id test-id)
))))
(define (db:get-prev-tol-for-test db test-id category variable)
;; Finish me?
(values #f #f #f))
;;======================================================================
;; S T E P S
;;======================================================================
(define (db:step-get-time-as-string vec)
(seconds->time-string (db:step-get-event_time vec)))
;; db-get-test-steps-for-run
(define (db:get-steps-for-test db test-id #!key (work-area #f))
(let* ((tdb (db:open-test-db-by-test-id db test-id work-area: work-area))
(res '()))
(if tdb
(begin
(sqlite3:for-each-row
(lambda (id test-id stepname state status event-time logfile)
(set! res (cons (vector id test-id stepname state status event-time (if (string? logfile) logfile "")) res)))
tdb
"SELECT id,test_id,stepname,state,status,event_time,logfile FROM test_steps WHERE test_id=? ORDER BY id ASC;" ;; event_time DESC,id ASC;
test-id)
(sqlite3:finalize! tdb)
(reverse res))
(if (sqlite3:database? tdb)
(handle-exceptions
exn
(begin
(debug:print 0 "ERROR: error on access to testdat for test with id " test-id)
'())
(begin
(sqlite3:for-each-row
(lambda (id test-id stepname state status event-time logfile)
(set! res (cons (vector id test-id stepname state status event-time (if (string? logfile) logfile "")) res)))
tdb
"SELECT id,test_id,stepname,state,status,event_time,logfile FROM test_steps WHERE test_id=? ORDER BY id ASC;" ;; event_time DESC,id ASC;
test-id)
(sqlite3:finalize! tdb)
(reverse res)))
'())))
;; get a pretty table to summarize steps
;;
(define (db:get-steps-table db test-id #!key (work-area #f))
(let ((steps (db:get-steps-for-test db test-id work-area: work-area)))
;; organise the steps for better readability
#f))
(string<? (conc time-a)(conc time-b))))))))
'()))
;;======================================================================
;; M I S C M A N A G E M E N T I T E M S
;;======================================================================
;; A routine to map itempaths using a itemmap
(define (db:compare-itempaths patha pathb itemmap)
(debug:print-info 3 "ITEMMAP is " itemmap)
(if itemmap
(let* ((mapparts (string-split itemmap))
(pattern (car mapparts))
(replacement (if (> (length mapparts) 1) (cadr mapparts) "")))
(if replacement
(equal? (string-substitute pattern replacement patha)
(string-substitute pattern replacement pathb))
(equal? (string-substitute pattern "" patha)
(string-substitute pattern "" pathb))))
(equal? patha pathb)))
;; the new prereqs calculation, looks also at itempath if specified
;; all prereqs must be met:
;; if prereq test with itempath='' is COMPLETED and PASS, WARN, CHECK, or WAIVED then prereq is met
;; if prereq test with itempath=ref-item-path and COMPLETED with PASS, WARN, CHECK, or WAIVED then prereq is met
;;
;; Note: mode 'normal means that tests must be COMPLETED and ok (i.e. PASS, WARN, CHECK, SKIP or WAIVED)
;; mode 'toplevel means that tests must be COMPLETED only
;; mode 'itemmatch means that tests items must be COMPLETED and (PASS|WARN|WAIVED|CHECK) [[ NB// NOT IMPLEMENTED YET ]]
;; mode 'itemmatch or 'itemwait means that tests items must be COMPLETED and (PASS|WARN|WAIVED|CHECK) [[ NB// NOT IMPLEMENTED YET ]]
;;
(define (db:get-prereqs-not-met run-id waitons ref-item-path #!key (mode 'normal))
(define (db:get-prereqs-not-met run-id waitons ref-item-path #!key (mode '(normal))(itemmap #f))
(if (or (not waitons)
(null? waitons))
'()
(let* ((unmet-pre-reqs '())
(result '()))
(for-each
(lambda (waitontest-name)
;; by getting the tests with matching name we are looking only at the matching test
;; and related sub items
(let ((tests (cdb:remote-run db:get-tests-for-run #f run-id waitontest-name '() '()))
(let ((tests (cdb:remote-run db:get-tests-for-run-state-status #f run-id waitontest-name)) ;; (mt:get-tests-for-run run-id waitontest-name '() '()))
(ever-seen #f)
(parent-waiton-met #f)
(item-waiton-met #f))
(for-each
(lambda (test)
;; (if (equal? waitontest-name (db:test-get-testname test)) ;; by defintion this had better be true ...
(let* ((state (db:test-get-state test))
(status (db:test-get-status test))
(item-path (db:test-get-item-path test))
(is-completed (equal? state "COMPLETED"))
(is-running (equal? state "RUNNING"))
(is-killed (equal? state "KILLED"))
(is-ok (member status '("PASS" "WARN" "CHECK" "WAIVED" "SKIP")))
(same-itempath (equal? ref-item-path item-path)))
(same-itempath (db:compare-itempaths ref-item-path item-path itemmap))) ;; (equal? ref-item-path item-path)))
(set! ever-seen #t)
(cond
;; case 1, non-item (parent test) is
((and (equal? item-path "") ;; this is the parent test
((and (equal? item-path "") ;; this is the parent test of the waiton being examined
is-completed
(or is-ok (not (null? (lset-intersection eq? mode '(toplevel)))))) ;; itemmatch itemwait))))))
(set! parent-waiton-met #t))
;; Special case for toplevel and KILLED
((and (equal? item-path "") ;; this is the parent test
is-killed
(or is-ok (eq? mode 'toplevel)))
(member 'toplevel mode))
(set! parent-waiton-met #t))
;; For itemwait mode IFF the previous matching item is good the set parent-waiton-met
((and (not (null? (lset-intersection eq? mode '(itemmatch itemwait))))
;; (not (equal? item-path "")) ;; this applies to both top level (to allow launching of next batch) and items
((and same-itempath
is-completed
(or is-ok (eq? mode 'toplevel))) same-itempath)
(if (and is-completed is-ok)
(set! item-waiton-met #t))
(if (and (equal? item-path "")
(or is-completed is-running));; this is the parent, set it to run if completed or running
(set! parent-waiton-met #t)))
;; normal checking of parent items, any parent or parent item not ok blocks running
((and is-completed
(or is-ok
(member 'toplevel mode)) ;; toplevel does not block on FAIL
(and is-ok (member 'itemmatch mode))) ;; itemmatch blocks on not ok
(set! item-waiton-met #t)))))
tests)
tests)
;; both requirements, parent and item-waiton must be met to NOT add item to
;; prereq's not met list
(if (not (or parent-waiton-met item-waiton-met))
(set! result (append (if (null? tests) (list waitontest-name) tests) result)))
;; if the test is not found then clearly the waiton is not met...
;; (if (not ever-seen)(set! result (cons waitontest-name result)))))
(if (not ever-seen)
(set! result (append (if (null? tests)(list waitontest-name) tests) result)))))
waitons)
(delete-duplicates result))))
(define (db:teststep-set-status! db test-id teststep-name state-in status-in comment logfile #!key (work-area #f))
(debug:print 4 "test-id: " test-id " teststep-name: " teststep-name) ;; db:open-test-db-by-test-id does cdb:remote-run
(let* ((tdb (db:open-test-db-by-test-id db test-id work-area: work-area))
(state (items:check-valid-items "state" state-in))
(status (items:check-valid-items "status" status-in)))
(if (or (not state)(not status))
(debug:print 3 "WARNING: Invalid " (if status "status" "state")
" value \"" (if status state-in status-in) "\", update your validvalues section in megatest.config"))
(if tdb (if (sqlite3:database? tdb)
(begin
(sqlite3:execute
tdb
"INSERT OR REPLACE into test_steps (test_id,stepname,state,status,event_time,comment,logfile) VALUES(?,?,?,?,?,?,?);"
test-id teststep-name state-in status-in (current-seconds) (if comment comment "") (if logfile logfile ""))
(sqlite3:finalize! tdb)
#t)
(debug:print 0 "WARNING: path given, " outputfile " is relative, prefixing with current directory")
(conc (current-directory) "/" outputfile)))
results)
;; brutal clean up
(system "rm -rf tempdir")))
;; (db:extract-ods-file db "outputfile.ods" '(("sysname" "%")("fsname" "%")("datapath" "%")) "%")
;; This is a list of all procs that write to the db
;;
(define *db:all-write-procs*
(list
db:set-var
db:del-var
db:register-run
db:set-comment-for-run
db:delete-run
db:update-run-event_time
db:lock/unlock-run
db:delete-test-step-records
db:delete-test-records
db:delete-tests-for-run
db:delete-old-deleted-test-records
db:set-tests-state-status
db:test-set-state-status-by-id
db:test-set-state-status-by-run-id-testname
db:test-set-comment
db:testmeta-add-record
db:csv->test-data
db:test-data-rollup
db:teststep-set-status! ))
How To Do Things
================
Tricks
------
This section is a compendium of a various useful tricks for debugging,
configuring and generally getting the most out of Megatest.
Debugging Tricks
----------------
Examining The Environment
~~~~~~~~~~~~~~~~~~~~~~~~~
During Config File Processing
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Organising Your Tests and Tasks
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/nfs/ch/disks/ch_unienv_disk005/qa_mrwellan/interim/src/megatest/tests/fdktestqa/testqa
----------------------------
[tests-paths]
1 #{get misc parent}/simplerun/tests
----------------------------
-------------------
[setup]
-------------------
The runscript method is a brute force way to run scripts where the
user is responsible for setting STATE and STATUS
-------------------
runscript main.csh
-------------------
ww30.2
cellname/LVS/cellname.LAYOUT_ERRORS
Error: text open
ww31.3
cellname/LVS/cellname.LAYOUT_ERRORS
Error: text open
<h1>The Megatest Users Manual</h1>
<span id="author">Matt Welland</span><br />
<span id="email"><tt><<a href="mailto:matt@kiatoa.com">matt@kiatoa.com</a>></tt></span><br />
<span id="revnumber">version 1.0,</span>
<span id="revdate">April 2012</span>
</div>
<div id="content">
<div class="sect1"><h2 id="_dedication">Dedication</h2><div class="sectionbody"><div class="paragraph"><p>Dedicated to my wife Joanna who has kindly supported my working on various projects over the years.</p></div><div class="sect1"><h2 id="_thanks">Thanks</h2><div class="sectionbody"><div class="paragraph"><p>Thank you the many people I’ve worked over the years who haveshared their knowledge and insights with me.</p></div><div class="paragraph"><p>Thanks also to the creators of the various open source projects thatMegatest is built on. These include Linux, xemacs, chicken scheme,fossil and asciidoc. Without these projects something like Megatestwould be difficult or impossible to do.</p></div></div></div></div></div><div class="sect1">
<h2 id="_preface">Preface</h2>
<div class="sectionbody">
<div class="paragraph"><p>This book is organised as three sub-books; getting started, writing tests and reference.</p></div>
<div class="sect2">
<h3 id="_why_megatest">Why Megatest?</h3>
<div class="paragraph"><p>The Megatest project was started for two reasons, the first was an
immediate and pressing need for a generalized tool to manage a suite
of regression tests and the second was the fact that I had written or
maintained several such tools at different companies over the years
and it seemed a good thing to have a single open source tool, flexible
enough to meet the needs of any team doing continuous integrating and
or running a complex suite of tests for release qualification.</p></div>of regression tests and the second was the fact that the author had
written or maintained several such tools at different companies over
the years and it seemed a good thing to have a single open source
tool, flexible enough to meet the needs of any team doing continuous
integrating and or running a complex suite of tests for release
qualification.</p></div>
</div>
<div class="sect2">
<h3 id="_megatest_design_philosophy">Megatest Design Philosophy</h3>
<div class="paragraph"><p>Megatest is intended to provide the minimum needed resources to make
writing a suite of tests and implementing continuous build for
software, design engineering or process control (via owlfs for
writing a suite of tests and tasks for implementing continuous build
for software, design engineering or process control (via owlfs for
example) without being specialized for any specific problem
space. Megatest in of itself does not know what constitutes a PASS or
FAIL of a test. In most cases megatest is best used in conjunction
with logpro or a similar tool to parse, analyze and decide on the test
outcome.</p></div>
</div>
<div class="sect2">
<h3 id="_megatest_architecture">Megatest Architecture</h3>
<div class="paragraph"><p>All data to specify the tests and configure the system is stored in
plain text files. All system state is stored in an sqlite3
database. Tests are launched using the launching system available for
the distributed compute platform in use. A template script is provided
which can launch jobs on local and remote Linux hosts. Currently
megatest uses the network filesystem to call home to your master
sqlite3 database.</p></div>
</div>
</div>
</div>
<h1 id="_road_map">Road Map</h1>
<div class="paragraph"><p>Note: This road-map is tentative and subject to change without notice.</p></div>
<div class="sect2">
<h3 id="_ww32">ww32</h3>
<div class="olist arabic"><ol class="arabic">
<li>
<p>
Rerun step and or subsequent steps from gui
</p>
</li>
<li>
<p>
Refresh test area files from gui
</p>
</li>
<li>
<p>
Clean and re-run button
</p>
</li>
<li>
<p>
Clean up STATE and STATUS handling.
</p>
<div class="olist loweralpha"><ol class="loweralpha">
<li>
<p>
Dashboard and Test control panel are reverse order - choose and fix
</p>
</li>
<li>
<p>
Move seldom used states and status to drop down selector
</p>
</li>
</ol></div>
</li>
<li>
<p>
Access test control panel when clicking on Run Summary tests
</p>
</li>
<li>
<p>
Feature: -generate-index-tree
</p>
</li>
<li>
<p>
Change specifing of state and status to use STATE1/STATUS1,STATE2/STATUS2
</p>
</li>
</ol></div>
</div>
<div class="sect2">
<h3 id="_ww33">ww33</h3>
<div class="olist arabic"><ol class="arabic">
<li>
<p>
http api available for use with Perl, Ruby etc. scripts
</p>
</li>
<li>
<p>
megatest.config setup entries for:
</p>
<div class="olist loweralpha"><ol class="loweralpha">
<li>
<p>
run launching (e.g. /bin/sh %CMD% > /dev/null)
</p>
</li>
<li>
<p>
browser "konqueror %FNAME%
</p>
</li>
</ol></div>
</li>
</ol></div>
</div>
<div class="sect2">
<h3 id="_ww34">ww34</h3>
<div class="olist arabic"><ol class="arabic">
<li>
<p>
Mark dependent tests for clean/rerun -rerun-downstream
</p>
</li>
<li>
<p>
On run start check for defunct tests in RUNNING, LAUNCHED or REMOTEHOSTSTART and correct or notify
</p>
</li>
<li>
<p>
Fix: refresh of gui sometimes fails on last item (race condition?)
</p>
</li>
</ol></div>
</div>
<div class="sect2">
<h3 id="_ww35">ww35</h3>
<div class="olist arabic"><ol class="arabic">
<li>
<p>
refdb: Add export of csv, json and sexp
</p>
</li>
<li>
<p>
Convert to using call-with-environment-variables where possible. Should allow handling of parallel runs in same process.
</p>
</li>
<li>
<p>
Re-work text interface wizards. Several bugs on record. Possibly convert to gui based.
</p>
</li>
<li>
<p>
Add to testconfig requirements section; launchlimiter scriptname, calls scriptname to check if ok to launch test
</p>
</li>
<li>
<p>
Refactor Run Summary view, currently very clumsy
</p>
</li>
<li>
<p>
Add option to show steps in Run Summary view
</p>
</li>
</ol></div>
</div>
<div class="sect2">
<h3 id="_ww36">ww36</h3>
<div class="olist arabic"><ol class="arabic">
<li>
<p>
Refactor guis for resizeablity
</p>
</li>
<li>
<p>
Add filters to Run Summary view and Run Control view
</p>
</li>
<li>
<p>
Add to megatest.config or testconfig; rerunok STATE/STATUS,STATE/STATUS…
</p>
</li>
<li>
<p>
Launch gates for diskspace; /path/one>1G,/path/two>200M,/tmp>5G,#{scheme <strong>toppath</strong>}>1G
</p>
</li>
</ol></div>
</div>
<div class="sect2">
<h3 id="_bin_list">Bin List</h3>
<div class="olist arabic"><ol class="arabic">
<li>
<p>
Quality improvements
</p>
<div class="olist loweralpha"><ol class="loweralpha">
<li>
<p>
Server stutters occasionally
</p>
</li>
<li>
<p>
Large number of items or tests still has some issues.
</p>
</li>
<li>
<p>
Code refactoring
</p>
</li>
<li>
<p>
Replace remote process with true API using json (supports Web app also)
</p>
</li>
</ol></div>
</li>
<li>
<p>
Streamline the gui
</p>
<div class="olist loweralpha"><ol class="loweralpha">
<li>
<p>
Everything resizable
</p>
</li>
<li>
<p>
Less clutter
</p>
</li>
<li>
<p>
Tool tips
</p>
</li>
<li>
<p>
Filters on Run Summary, Summary and Run Control panel
</p>
</li>
<li>
<p>
Built in log viewer (partially implemented)
</p>
</li>
<li>
<p>
Refactor the test control panel
</p>
</li>
</ol></div>
</li>
<li>
<p>
Help and documentation
</p>
<div class="olist loweralpha"><ol class="loweralpha">
<li>
<p>
Complete the user manual (I’ve been working on this lately).
</p>
</li>
<li>
<p>
Online help in the gui
</p>
</li>
</ol></div>
</li>
<li>
<p>
Streamlined install
</p>
<div class="olist loweralpha"><ol class="loweralpha">
<li>
<p>
Deployed version (download a location independent ready to run binary bundle)
</p>
</li>
<li>
<p>
Install Makefile (in progress, needed for Mike to install on VMs)
</p>
</li>
<li>
<p>
Added option to compile IUP (needed for VMs)
</p>
</li>
</ol></div>
</li>
<li>
<p>
Server side run launching
</p>
</li>
<li>
<p>
Support for re-running, cleaning etc. of individual steps (ezsteps makes this very easy to implement).
</p>
</li>
<li>
<p>
Launch process needs built in daemonizing (easy to do, just need to test it thoroughly).
</p>
</li>
<li>
<p>
Wizards for creating tests, regression areas (current ones are text only and limited).
</p>
</li>
<li>
<p>
Fully functional built in web service (currently you can browse runs but it is very simplistic).
</p>
</li>
<li>
<p>
Wildcards in runconfigs: e.g. [p1271/9/%/%]
</p>
</li>
<li>
<p>
Gui panels for editing megatest.config and runconfigs.config
</p>
</li>
<li>
<p>
Fully isolated tests (no use of NFS to see regression area files)
</p>
</li>
<li>
<p>
Windows version
</p>
</li>
</ol></div>
</div>
<h1 id="_getting_started">Getting Started</h1>
<div class="openblock">
<div class="title">Getting started with Megatest</div>
<div class="content">
<div class="paragraph"><p>How to install Megatest and set it up for running your regressions and continuous integration process.</p></div>
</div></div>
<div class="sect1">
<h2 id="_the_second_chapter">The Second Chapter</h2>
<div class="sectionbody">
<div class="paragraph"><p>An example link to anchor at start of the <a href="#X1">first sub-section</a>.</p></div>
<div class="paragraph"><p>An example link to a bibliography entry <a href="#taoup">[taoup]</a>.</p></div>
</div>
</div>
<h1 id="_reference">Reference</h1>
<h1 id="_writing_tests">Writing Tests</h1>
<div class="sect1">
<h2 id="_the_first_chapter_of_the_second_part">The First Chapter of the Second Part</h2>
<div class="sectionbody">
<div class="paragraph"><p>Chapters grouped into book parts are at level 1 and can contain
sub-sections.</p></div>
</div>
</div>
<h1 id="_how_to_do_things">How To Do Things</h1>
<div class="sect1">
<h2 id="_the_testconfig_file">The testconfig File</h2>
<h2 id="_tricks">Tricks</h2>
<div class="sectionbody">
<div class="paragraph"><p>This section is a compendium of a various useful tricks for debugging,
configuring and generally getting the most out of Megatest.</p></div>
</div>
</div>
<div class="sect1">
<h2 id="_debugging_tricks">Debugging Tricks</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_examining_the_environment">Examining The Environment</h3>
<div class="sect3">
<h4 id="_during_config_file_processing">During Config File Processing</h4>
</div>
<div class="sect3">
<h4 id="_organising_your_tests_and_tasks">Organising Your Tests and Tasks</h4>
<div class="paragraph"><p>/nfs/ch/disks/ch_unienv_disk005/qa_mrwellan/interim/src/megatest/tests/fdktestqa/testqa</p></div>
<div class="listingblock">
<div class="content">
<pre><tt># testconfig
[requirements]
# A normal waiton waits for the prior tests to be COMPLETED
<pre><tt>[tests-paths]
1 #{get misc parent}/simplerun/tests</tt></pre>
</div></div>
<div class="listingblock">
<div class="content">
<pre><tt>[setup]</tt></pre>
</div></div>
<div class="paragraph"><p>The runscript method is a brute force way to run scripts where the
user is responsible for setting STATE and STATUS</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>runscript main.csh</tt></pre>
</div></div>
<div class="paragraph"><p>ww30.2
cellname/LVS/cellname.LAYOUT_ERRORS</p></div>
<div class="paragraph"><p>Error: text open</p></div>
<div class="paragraph"><p>ww31.3
cellname/LVS/cellname.LAYOUT_ERRORS</p></div>
<div class="paragraph"><p>Error: text open
Reference</p></div>
<div class="exampleblock">
<div class="content">
<div class="paragraph"><p>Chapters grouped into book parts are at level 1 and can contain
sub-sections.</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>[setup]</tt></pre>
</div></div>
<div class="paragraph"><p>The runscript method is a brute force way to run scripts where the
user is responsible for setting STATE and STATUS</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>runscript main.csh</tt></pre>
</div></div>
<div class="listingblock">
<div class="content">
<pre><tt>[requirements]</tt></pre>
</div></div>
<div class="listingblock">
<div class="content">
<pre><tt># A normal waiton waits for the prior tests to be COMPLETED
# and PASS, CHECK or WAIVED
waiton test1 test2
# A item based waiton will start items in a test when the
# same-named item is COMPLETED and PASS, CHECK or WAIVED
# in the prior test
waiton test1 test2</tt></pre>
</div></div>
<div class="paragraph"><p>The default (i.e. if mode is not specified) is normal. All pre-dependent tests
must be COMPLETED and PASS, CHECK or WAIVED before the test will start</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>mode normal</tt></pre>
</div></div>
<div class="paragraph"><p>The toplevel mode requires only that the prior tests are COMPLETED.</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>mode toplevel</tt></pre>
</div></div>
<div class="paragraph"><p>A item based waiton will start items in a test when the
same-named item is COMPLETED and PASS, CHECK or WAIVED
in the prior test</p></div>
#
mode normal | toplevel
# With a toplevel test you may wish to generate your list
<div class="listingblock">
<div class="content">
<pre><tt>mode itemmatch</tt></pre>
</div></div>
<div class="listingblock">
<div class="content">
<pre><tt># With a toplevel test you may wish to generate your list
# of tests to run dynamically
#
# waiton #{shell get-valid-tests-to-run.sh}</tt></pre>
</div></div>
<div class="listingblock">
<div class="content">
<pre><tt>runtimelim 1h 2m 3s # this will automatically kill the test if it runs for more than 1h 2m and 3s</tt></pre>
</div>
</div>
<div class="sect1">
<h2 id="_example_appendix">Appendix A: Example Appendix</h2>
<div class="sectionbody">
<div class="paragraph"><p>One ormore optional appendixes go here at section level zero.</p></div>
<div class="sect2">
<h3 id="_appendix_sub_section">Appendix Sub-section</h3></div></div>
<div class="listingblock">
<div class="content">
<pre><tt>[skip]</tt></pre>
</div></div>
<div class="listingblock">
<div class="content">
<pre><tt># NB// If the prevrunning line exists with *any* value the test will
# automatically SKIP if the same-named test is currently RUNNING
prevrunning x</tt></pre>
</div></div>
<div class="listingblock">
<div class="content">
<pre><tt>fileexists /path/to/a/file # skip if /path/to/a/file exists</tt></pre>
</div></div>
<div class="paragraph"><p>If test is FAIL and previous test in run with same MT_TARGET is WAIVED then apply the following rules from the testconfig:
If a waiver check is specified in the testconfig apply the check and if it passes then set this FAIL to WAIVED</p></div>
<div class="paragraph"><p>Waiver check has two parts, 1) a list of waiver, rulename, filepatterns and 2) the rulename script spec (note that "diff" and "logpro" are predefined)</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>###### EXAMPLE FROM testconfig #########
# matching file(s) will be diff'd with previous run and logpro applied
# if PASS or WARN result from logpro then WAIVER state is set
#
[waivers]
# logpro_file rulename input_glob
waiver_1 logpro lookittmp.log
[waiver_rules]
# This builtin rule is the default if there is no <waivername>.logpro file
# diff diff %file1% %file2%
# This builtin rule is applied if a <waivername>.logpro file exists
# logpro diff %file1% %file2% | logpro %waivername%.logpro %waivername%.html</tt></pre>
</div></div>
<div class="paragraph"><p>To transfer the environment to the next step you can do the following:</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>$MT_MEGATEST -env2file .ezsteps/${stepname}</tt></pre>
</div></div>
<div class="paragraph"><p>One or more optional appendixes go here at section level zero.</p></div>
<div class="admonitionblock">
<table><tr>
<td class="icon">
<div class="title">Note</div>
</td>
<td class="content">Preface and appendix subsections start out of sequence at level
2 (level 1 is skipped). This only applies to multi-part book
documents.</td>
</tr></table>
</div>
</div></div></div><div class="sect1"><h2 id="_example_bibliography">Example Bibliography</h2><div class="sectionbody"><div class="paragraph"><p>The bibliography list is a style of AsciiDoc bulleted list.</p></div>
<div class="ulist bibliography"><ul>
<li>
<p>
<a id="taoup"></a>[taoup] Eric Steven Raymond. <em>The Art of Unix
Programming</em>. Addison-Wesley. ISBN 0-13-142901-9.
</p>
</li>
<li>
<p>
<a id="walsh-muellner"></a>[walsh-muellner] Norman Walsh & Leonard Muellner.
<em>DocBook - The Definitive Guide</em>. O’Reilly & Associates. 1999.
ISBN 1-56592-580-7.
</p>
</li>
</ul></div>
</div></div><div class="sect1"><h2 id="_example_glossary">Example Glossary</h2><div class="sectionbody"><div class="paragraph"><p>Glossaries are optional. Glossaries entries are an example of a style
of AsciiDoc labeled lists.</p></div>
<div class="dlist glossary"><dl>
<dt>
A glossary term
</dt>
<dd>
<p>
The corresponding (indented) definition.
</p>
</dd>
<dt>
A second glossary term
</dt>
<dd>
<p>
The corresponding (indented) definition.
</p>
</dd>
</dl></div>
</div></div><div class="sect1"><h2 id="_example_colophon">Example Colophon</h2><div class="sectionbody"><div class="paragraph"><p>Text at the end of a book describing facts about its production.</p></div>
</div></div>
</div>
</div>
<div class="sect1"><h2 id="_example_index">Example Index</h2><div class="sectionbody"></div>
</div>
</div>
<div id="footnotes"><hr /></div>
<div id="footer">
<div id="footer-text">
Version 1.0<br />
Last updated 2013-03-25 09:17:02 MST
Last updated 2014-02-13 21:27:42 MST
</div>
</div>
</body>
</html>
The Megatest Users Manual
=========================
Matt Welland <matt@kiatoa.com>
v1.0, April 2012
:doctype: book
[dedication]Dedication==========Dedicated to my wife Joanna who has kindly supported my working on various projects over the years.Thanks------Thank you the many people I've worked over the years who have shared their knowledge and insights with me.Thanks also to the creators of the various open source projects thatMegatest is built on. These include Linux, xemacs, chicken scheme,fossil and asciidoc. Without these projects something like Megatest would be difficult or impossible to do.[preface]
Preface
=======
This book is organised as three sub-books; getting started, writing tests and reference.
Why Megatest?
~~~~~~~~~~~~~
The Megatest project was started for two reasons, the first was an
immediate and pressing need for a generalized tool to manage a suite
of regression tests and the second was the fact that I had written or
maintained several such tools at different companies over the years
and it seemed a good thing to have a single open source tool, flexible
enough to meet the needs of any team doing continuous integrating and
or running a complex suite of tests for release qualification.of regression tests and the second was the fact that the author had
written or maintained several such tools at different companies over
the years and it seemed a good thing to have a single open source
tool, flexible enough to meet the needs of any team doing continuous
integrating and or running a complex suite of tests for release
qualification.
Megatest Design Philosophy
~~~~~~~~~~~~~~~~~~~~~~~~~~
Megatest is intended to provide the minimum needed resources to make
writing a suite of tests and implementing continuous build for
software, design engineering or process control (via owlfs for
writing a suite of tests and tasks for implementing continuous build
for software, design engineering or process control (via owlfs for
example) without being specialized for any specific problem
space. Megatest in of itself does not know what constitutes a PASS or
FAIL of a test. In most cases megatest is best used in conjunction
with logpro or a similar tool to parse, analyze and decide on the test
outcome.
Megatest Architecture
~~~~~~~~~~~~~~~~~~~~~
All data to specify the tests and configure the system is stored in
plain text files. All system state is stored in an sqlite3
database. Tests are launched using the launching system available for
the distributed compute platform in use. A template script is provided
which can launch jobs on local and remote Linux hosts. Currently
megatest uses the network filesystem to call home to your master
sqlite3 database.
include::../plan.txt[]
include::getting_started.txt[] include::writing_tests.txt[]include::getting_started.txt[]
include::writing_tests.txt[]
include::howto.txt[]
include::reference.txt[]
Controlled waiver propagation=============================If test is FAIL and previous test in run with same MT_TARGET is WAIVED then apply the following rules from the testconfig:If a waiver check is specified in the testconfig apply the check and if it passes then set this FAIL to WAIVEDWaiver check has two parts, 1) a list of waiver, rulename, filepatterns and 2) the rulename script spec (note that "diff" and "logpro" are predefined)=========================================================###### EXAMPLE FROM testconfig ########## matching file(s) will be diff'd with previous run and logpro applied# if PASS or WARN result from logpro then WAIVER state is set#[waivers]# logpro_file rulename input_globwaiver_1 logpro lookittmp.log[waiver_rules]# This builtin rule is the default if there is no <waivername>.logpro file# diff diff %file1% %file2%# This builtin rule is applied if a <waivername>.logpro file exists# logpro diff %file1% %file2% | logpro %waivername%.logpro %waivername%.html=========================================================
[appendix]
Example Appendix
================
One or more optional appendixes go here at section level zero.
Appendix Sub-section
Reference
=========
The First Chapter of the Second Part
------------------------------------
Chapters grouped into book parts are at level 1 and can contain
sub-sections.
The testconfig File
-------------------
Setup section
~~~~~~~~~~~~~
Header
^^^^^^
-------------------
[setup]
-------------------
# testconfig
The runscript method is a brute force way to run scripts where the
user is responsible for setting STATE and STATUS
-------------------
runscript main.csh
-------------------
Requirements section
~~~~~~~~~~~~~~~~~~~~
Header
^^^^^^
-------------------
[requirements]
-------------------
Wait on Other Tests
^^^^^^^^^^^^^^^^^^^
-------------------
# A normal waiton waits for the prior tests to be COMPLETED
# and PASS, CHECK or WAIVED
waiton test1 test2
-------------------
Mode
^^^^
The default (i.e. if mode is not specified) is normal. All pre-dependent tests
must be COMPLETED and PASS, CHECK or WAIVED before the test will start
-------------------
mode normal
-------------------
The toplevel mode requires only that the prior tests are COMPLETED.
-------------------
mode toplevel
-------------------
# A item based waiton will start items in a test when the
# same-named item is COMPLETED and PASS, CHECK or WAIVED
# in the prior test
#mode normal | toplevelA item based waiton will start items in a test when the
same-named item is COMPLETED and PASS, CHECK or WAIVED
in the prior test
-------------------
mode itemmatch
-------------------
-------------------
# With a toplevel test you may wish to generate your list
# of tests to run dynamically
#
# waiton #{shell get-valid-tests-to-run.sh}
------------------
Run time limit
^^^^^^^^^^^^^^
-----------------
runtimelim 1h 2m 3s # this will automatically kill the test if it runs for more than 1h 2m and 3s
-----------------
Skip
^^^^
Header
^^^^^^
-----------------
[skip]
-----------------
Skip on Still-running Tests
^^^^^^^^^^^^^^^^^^^^^^^^^^^
-----------------
# NB// If the prevrunning line exists with *any* value the test will
# automatically SKIP if the same-named test is currently RUNNING
prevrunning x
-----------------
Skip if a File Exists
^^^^^^^^^^^^^^^^^^^^^
-----------------
fileexists /path/to/a/file # skip if /path/to/a/file exists
-----------------
Controlled waiver propagation
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If test is FAIL and previous test in run with same MT_TARGET is WAIVED then apply the following rules from the testconfig:
If a waiver check is specified in the testconfig apply the check and if it passes then set this FAIL to WAIVED
Waiver check has two parts, 1) a list of waiver, rulename, filepatterns and 2) the rulename script spec (note that "diff" and "logpro" are predefined)
-----------------
###### EXAMPLE FROM testconfig #########
# matching file(s) will be diff'd with previous run and logpro applied
# if PASS or WARN result from logpro then WAIVER state is set
#
[waivers]
# logpro_file rulename input_glob
waiver_1 logpro lookittmp.log
[waiver_rules]
# This builtin rule is the default if there is no <waivername>.logpro file
# diff diff %file1% %file2%
# This builtin rule is applied if a <waivername>.logpro file exists
# logpro diff %file1% %file2% | logpro %waivername%.logpro %waivername%.html
-----------------
Ezsteps
~~~~~~~
To transfer the environment to the next step you can do the following:
----------------------------
$MT_MEGATEST -env2file .ezsteps/${stepname}
----------------------------
:numbered!:
Road Map
========
Note: This road-map is tentative and subject to change without notice.
Move test specific db to test dir=================================
ww32
~~~~
. Rerun step and or subsequent steps from gui
. Refresh test area files from gui
. Clean and re-run button
. Clean up STATE and STATUS handling.
.. Dashboard and Test control panel are reverse order - choose and fix
.. Move seldom used states and status to drop down selector
. Access test control panel when clicking on Run Summary tests
. Feature: -generate-index-tree
. Change specifing of state and status to use STATE1/STATUS1,STATE2/STATUS2
ww33
~~~~
. Create teststats.db. Redirect test run stats to teststats.db. Redirect test steps data to teststats.db. Redirect test_data to teststats.db. Direct dboard to get stats from teststats.db. Redirect kill requests to teststats.db. Kill requests need to kill all processes in the tree. Roll up overall stats to megatest.db every five minutes or when test done. Add any necessary tests
. http api available for use with Perl, Ruby etc. scripts
. megatest.config setup entries for:
.. run launching (e.g. /bin/sh %CMD% > /dev/null)
.. browser "konqueror %FNAME%
ww34
~~~~
. Mark dependent tests for clean/rerun -rerun-downstream
. On run start check for defunct tests in RUNNING, LAUNCHED or REMOTEHOSTSTART and correct or notify
. Fix: refresh of gui sometimes fails on last item (race condition?)
ww35
~~~~
. refdb: Add export of csv, json and sexp
. Convert to using call-with-environment-variables where possible. Should allow handling of parallel runs in same process.
. Re-work text interface wizards. Several bugs on record. Possibly convert to gui based.
. Add to testconfig requirements section; launchlimiter scriptname, calls scriptname to check if ok to launch test
. Refactor Run Summary view, currently very clumsy
. Add option to show steps in Run Summary view
ww36
~~~~
. Refactor guis for resizeablity
. Add filters to Run Summary view and Run Control view
. Add to megatest.config or testconfig; rerunok STATE/STATUS,STATE/STATUS...
. Launch gates for diskspace; /path/one>1G,/path/two>200M,/tmp>5G,#{scheme *toppath*}>1G
Bin List
~~~~~~~~
. Quality improvements
.. Server stutters occasionally
.. Large number of items or tests still has some issues.
.. Code refactoring
.. Replace remote process with true API using json (supports Web app also)
. Streamline the gui
.. Everything resizable
.. Less clutter
.. Tool tips
.. Filters on Run Summary, Summary and Run Control panel
.. Built in log viewer (partially implemented)
.. Refactor the test control panel
. Help and documentation
.. Complete the user manual (I’ve been working on this lately).
.. Online help in the gui
. Streamlined install
.. Deployed version (download a location independent ready to run binary bundle)
.. Install Makefile (in progress, needed for Mike to install on VMs)
.. Added option to compile IUP (needed for VMs)
. Server side run launching
. Support for re-running, cleaning etc. of individual steps (ezsteps makes this very easy to implement).
. Launch process needs built in daemonizing (easy to do, just need to test it thoroughly).
. Wizards for creating tests, regression areas (current ones are text only and limited).
. Fully functional built in web service (currently you can browse runs but it is very simplistic).
. Wildcards in runconfigs: e.g. [p1271/9/%/%]
. Gui panels for editing megatest.config and runconfigs.config
. Fully isolated tests (no use of NFS to see regression area files)
. Windows version
[fields]
PLATFORM TEXTOS TEXTCFG_TYPE This is the refdb to use.
RUN_TYPE Can be: full or quick
[setup]
# Adjust max_concurrent_jobs to limit parallel jobs
max_concurrent_jobs 50
# This is your link path, best to set it and then not change it
linktree #{getenv PWD}/linktree
# Job tools control how your jobs are launched
[jobtools]
useshell yes
launcher nbfindlauncher nbfake
# You can override environment variables for all your tests here[env-override]EXAMPLE_VAR example value
# As you run more tests you may need to add additional disks
# the names are arbitrary but must be unique
[disks]
disk0 #{getenv PWD}/runs
[include local.megatest.config]
[default]
ALLTESTS see this variable
# Your variables here are grouped by targets [SYSTEM/RELEASE]
[SYSTEM_val/RELEASE_val]
[cfg/default]
ANOTHERVAR only defined if target is SYSTEM_val/RELEASE_val
[include local.runconfigs.config]
# Add steps here. Format is "stepname script"[ezsteps]checkspace checkspace.sh# Iteration for your tests are controlled by the items section[itemstable]DIRECTORY /tmp /optREQUIRED 1000000 100000
;; Analyze the output from diskspace.sh
;;
(expect:error in "LogFileBody" = 0 "Insufficient space" #/ERROR: available space is less/)
(expect:error in "LogFileBody" = 0 "Any error" #/err/i)
(expect:required in "LogFileBody" > 1 "Sucess signature" #/INFO: space available/)
#!/bin/bash -e
filter=`refdb lookup $MT_RUN_AREA_HOME/$CFG_TYPE machines $TARGETHOST filter`
echo "Using filter: $filter"
diskareas=`mount | egrep 'ext|mfs|nfs'| egrep -v "$filter" | awk '{print $3}'`
for dirname in $diskareas;do
echo "dirname: $dirname"
# measure the free space
freespace=`df -P -k $dirname | grep $dirname | awk '{print $4}'`
# get the minfree allowed from the refdb
minfree=`refdb lookup $MT_RUN_AREA_HOME/$CFG_TYPE machines $TARGETHOST minfree`
if [[ "$freespace" -lt "$minfree" ]];then
echo "ERROR: available space $freespace is less than minimum allowed of $minfree on $dirname"
else
echo "INFO: space available of $freespace k on $dirname meets required minimum of $minfree."
fi
done
# Add steps here. Format is "stepname script"
[ezsteps]
ping ping -c 5 $PINGHOST
# Iteration for your tests are controlled by the items section
[items]
PINGHOST [system refdb getrownames $CFG_TYPE machines]
;; Copyright 2006-2011, Matthew Welland.
;;
;; This program is made available under the GNU GPL version 2.0 or
;; greater. See the accompanying file COPYING for details.
;;
;; This program is distributed WITHOUT ANY WARRANTY; without even the
;; implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
;; PURPOSE.
;; (require-extension synch sqlite3 posix srfi-13 srfi-1 utils regex)
(use sqlite3 srfi-1 posix regex srfi-69 srfi-13 posix-extras)
(import (prefix sqlite3 sqlite3:))
(declare (unit filedb))
(include "fdb_records.scm")
;; (include "settings.scm")
(define (filedb:open-db dbpath)
(let* ((fdb (make-filedb:fdb))
(dbexists (file-exists? dbpath))
(db (sqlite3:open-database dbpath)))
(filedb:fdb-set-db! fdb db)
(filedb:fdb-set-dbpath! fdb dbpath)
(filedb:fdb-set-pathcache! fdb (make-hash-table))
(filedb:fdb-set-idcache! fdb (make-hash-table))
(filedb:fdb-set-partcache! fdb (make-hash-table))
;(sqlite3:set-busy-timeout! db 1000000)
(if (not dbexists)
(begin
(sqlite3:execute db "PRAGMA synchronous = OFF;")
(sqlite3:execute db "CREATE TABLE names (id INTEGER PRIMARY KEY,name TEST);") ;; for future use - change path in paths table to path_id
(sqlite3:execute db "CREATE INDEX name_index ON names (name);")
;; NB// We store a useful subset of file attributes but do not attempt to store all
(sqlite3:execute db "CREATE TABLE paths (id INTEGER PRIMARY KEY,
path TEXT,
parent_id INTEGER,
mode INTEGER DEFAULT -1,
uid INTEGER DEFAULT -1,
gid INTEGER DEFAULT -1,
size INTEGER DEFAULT -1,
mtime INTEGER DEFAULT -1);")
(sqlite3:execute db "CREATE INDEX path_index ON paths (path,parent_id);")
(sqlite3:execute db "CREATE TABLE bases (id INTEGER PRIMARY KEY,base TEXT, updated TIMESTAMP);")))
fdb))
(define (filedb:finalize-db! fdb)
(sqlite3:finalize! (filedb:fdb-get-db fdb)))
(define (filedb:get-current-time-string)
(string-chomp (time->string (seconds->local-time (current-seconds)))))
(define (filedb:get-base-id db path)
(let ((stmt (sqlite3:prepare db "SELECT id FROM bases WHERE base=?;"))
(id-num #f))
(sqlite3:for-each-row
(lambda (num) (set! id-num num)) stmt path)
(sqlite3:finalize! stmt)
id-num))
(define (filedb:get-path-id db path parent)
(let ((stmt (sqlite3:prepare db "SELECT id FROM paths WHERE path=? AND parent_id=?;"))
(id-num #f))
(sqlite3:for-each-row
(lambda (num) (set! id-num num)) stmt path parent)
(sqlite3:finalize! stmt)
id-num))
(define (filedb:add-base db path)
(let ((existing (filedb:get-base-id db path)))
(if existing #f
(begin
(sqlite3:execute db "INSERT INTO bases (base,updated) VALUES (?,?);" path (filedb:get-current-time-string))))))
;; index value field notes
;; 0 inode number st_ino
;; 1 mode st_mode bitfield combining file permissions and file type
;; 2 number of hard links st_nlink
;; 3 UID of owner st_uid as with file-owner
;; 4 GID of owner st_gid
;; 5 size st_size as with file-size
;; 6 access time st_atime as with file-access-time
;; 7 change time st_ctime as with file-change-time
;; 8 modification time st_mtime as with file-modification-time
;; 9 parent device ID st_dev ID of device on which this file resides
;; 10 device ID st_rdev device ID for special files (i.e. the raw major/minor number)
;; 11 block size st_blksize
;; 12 number of blocks allocated st_blocks
(define (filedb:add-path-stat db path parent statinfo)
(let ((stmt (sqlite3:prepare db "INSERT INTO paths (path,parent_id,mode,uid,gid,size,mtime) VALUES (?,?,?,?,?,?,?);")))
(sqlite3:execute stmt
path
parent
(vector-ref statinfo 1) ;; mode
(vector-ref statinfo 3) ;; uid
(vector-ref statinfo 4) ;; gid
(vector-ref statinfo 5) ;; size
(vector-ref statinfo 8) ;; mtime
)
(sqlite3:finalize! stmt))) ;; (filedb:get-current-time-string))))
(define (filedb:add-path db path parent)
(let ((stmt (sqlite3:prepare db "INSERT INTO paths (path,parent_id) VALUES (?,?);")))
(sqlite3:execute stmt path parent)
(sqlite3:finalize! stmt)))
(define (filedb:register-path fdb path #!key (save-stat #f))
(let* ((db (filedb:fdb-get-db fdb))
(pathcache (filedb:fdb-get-pathcache fdb))
(stat (if save-stat (file-stat path #t)))
(id (hash-table-ref/default pathcache path #f)))
(if id id
(let ((plist (string-split path "/")))
(let loop ((head (car plist))
(tail (cdr plist))
(parent 0))
(let ((id (filedb:get-path-id db head parent))
(done (null? tail)))
(if id ;; we'll have a id if the path is already registered
(if done
(begin
(hash-table-set! pathcache path id)
id) ;; return the last path id for a result
(loop (car tail)(cdr tail) id))
(begin ;; add the path and then repeat the loop with the same data
(if save-stat
(filedb:add-path-stat db head parent stat)
(filedb:add-path db head parent))
(loop head tail parent)))))))))
(define (filedb:update-recursively fdb path #!key (save-stat #f))
(let ((p (open-input-pipe (string-append "find -L " path)))) ;; (resolve-pathname path)))) ;; (string-append "find " path))))
(print "processed 0 files...")
(let loop ((l (read-line p))
(lc 0)) ;; line count
(if (eof-object? l)
(begin
(print " " lc " files")
(close-input-port p))
(begin
(filedb:register-path fdb l save-stat: save-stat) ;; (get-real-path l)) ;; don't like losing the original path info
(if (= (modulo lc 100) 0)
(print " " lc " files"))
(loop (read-line p)(+ lc 1)))))))
(define (filedb:update fdb path #!key (save-stat #f))
;; first get the realpath and add it to the bases table
(let ((real-path path) ;; (filedb:get-real-path path))
(db (filedb:fdb-get-db fdb)))
(filedb:add-base db real-path)
(filedb:update-recursively fdb path save-stat: save-stat)))
;; not used and broken
;;
(define (filedb:get-real-path path)
(let* ((p (open-input-pipe (string-append real-path " " (regexp-escape path))))
(pth (read-line p)))
(if (eof-object? pth) path
(begin
(close-input-port p)
pth))))
(define (filedb:drop-base fdb path)
(print "Sorry, I don't do anything yet"))
(define (filedb:find-all fdb pattern action)
(let* ((db (filedb:fdb-get-db fdb))
(stmt (sqlite3:prepare db "SELECT id FROM paths WHERE path like ?;"))
(result '()))
(sqlite3:for-each-row
(lambda (num)
(action num)
(set! result (cons num result))) stmt pattern)
(sqlite3:finalize! stmt)
result))
(define (filedb:get-path-record fdb id)
(let* ((db (filedb:fdb-get-db fdb))
(partcache (filedb:fdb-get-partcache fdb))
(dat (hash-table-ref/default partcache id #f)))
(if dat dat
(let ((stmt (sqlite3:prepare db "SELECT path,parent_id FROM paths WHERE id=?;"))
(result #f))
(sqlite3:for-each-row
(lambda (path parent_id)(set! result (list path parent_id))) stmt id)
(hash-table-set! partcache id result)
(sqlite3:finalize! stmt)
result))))
(define (filedb:get-children fdb parent-id)
(let* ((db (filedb:fdb-get-db fdb))
(res '()))
(sqlite3:for-each-row
(lambda (id path parent-id)
(set! res (cons (vector id path parent-id) res)))
db "SELECT id,path,parent_id FROM paths WHERE parent_id=?;"
parent-id)
res))
;; retrieve all that have children and those without
;; children that match patt
(define (filedb:get-children-patt fdb parent-id search-patt)
(let* ((db (filedb:fdb-get-db fdb))
(res '()))
;; first get the children that have no children
(sqlite3:for-each-row
(lambda (id path parent-id)
(set! res (cons (vector id path parent-id) res)))
db "SELECT id,path,parent_id FROM paths WHERE parent_id=? AND
(id IN (SELECT parent_id FROM paths) OR path LIKE ?);"
parent-id search-patt)
res))
(define (filedb:get-path fdb id)
(let* ((db (filedb:fdb-get-db fdb))
(idcache (filedb:fdb-get-idcache fdb))
(path (hash-table-ref/default idcache id #f)))
(if path path
(let loop ((curr-id id)
(path ""))
(let ((path-record (filedb:get-path-record fdb curr-id)))
(if (not path-record) #f ;; this id has no path
(let* ((parent-id (list-ref path-record 1))
(pname (list-ref path-record 0))
(newpath (string-append "/" pname path)))
(if (= parent-id 0) ;; fields 0=path, 1=parent. root parent=0
(begin
(hash-table-set! idcache id newpath)
newpath)
(loop parent-id newpath)))))))))
(define (filedb:search db pattern)
(let ((action (lambda (id)(print (filedb:get-path db id)))))
(filedb:find-all db pattern action)))
(include "db_records.scm")
(define (http-transport:make-server-url hostport)
(if (not hostport)
#f
(conc "http://" (car hostport) ":" (cadr hostport))))
(define *server-loop-heart-beat* (current-seconds))
(define *server-loop-heart-beat* (current-seconds))
(define *heartbeat-mutex* (make-mutex))
;;======================================================================
;; S E R V E R
;;======================================================================
;; Call this to start the actual server
;;======================================================================
;; Copyright 2006-2013, Matthew Welland.
;;
;; This program is made available under the GNU GPL version 2.0 or
;; greater. See the accompanying file COPYING for details.
;;
;; This program is distributed WITHOUT ANY WARRANTY; without even the
;; implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
;; PURPOSE.
;;======================================================================
;;======================================================================
;; Tests
;;======================================================================
(use sqlite3 srfi-1 posix regex regex-case srfi-69 dot-locking tcp directory-utils)
(import (prefix sqlite3 sqlite3:))
(declare (unit tests))
(declare (uses lock-queue))
(declare (uses db))
(declare (uses common))
(declare (uses items))
(declare (uses runconfig))
(include "common_records.scm")
(include "key_records.scm")
(include "db_records.scm")
(include "run_records.scm")
(include "test_records.scm")
;; Populate the links tree with index.html files
;;
;; - start from most recent tests and work towards oldest -OR-
;; start from deepest hierarchy and work way up
;; - look up tests in megatest.db
;; - cross-reference the tests to stats.db
;; - if newer than event_time in stats.db or not registered in stats.db regenerate
;; - run du and store in stats.db
;; - when all tests at that level done generate next level up index.html
;;
;; include in rollup html index.html:
;; sum of du
;; counts of PASS, FAIL, RUNNING, REMOTEHOSTSTART, LAUNCHED, CHECK etc.
;; overall status
;;
;; include in test specific index.html:
;; host, uname, cpu graph, disk avail graph, steps, data
;; meta data, state, status, du
;;
;;======================================================================
;; Copyright 2006-2012, Matthew Welland.
;;
;; This program is made available under the GNU GPL version 2.0 or
;; greater. See the accompanying file COPYING for details.
;;
;; This program is distributed WITHOUT ANY WARRANTY; without even the
;; implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
;; PURPOSE.
;;======================================================================
(define-inline (key:get-fieldname key)(vector-ref key 0))(define-inline (key:get-fieldtype key)(vector-ref key 1))(define-inline (keys->valslots keys) ;; => ?,?,? ....
(string-intersperse (map (lambda (x) "?") keys) ","))
(define-inline (keys->key/field keys . additional)
(string-join (map (lambda (k)(conc (key:get-fieldname k) " " (string-join (map (lambda (k)(conc k " TEXT"))
(key:get-fieldtype k))) (append keys additional)) ","))
(define-inline (item-list->path itemdat)
(if (list? itemdat)
(string-intersperse (map cadr itemdat) "/")
""))
(declare (unit keys))
(declare (uses common))
(include "key_records.scm")
(include "common_records.scm")
(define (get-keys db) (let ((keys '())) ;; keys are vectors <fieldname,type> (sqlite3:for-each-row (lambda (fieldname fieldtype) (set! keys (cons (vector fieldname fieldtype) keys))) db "SELECT fieldname,fieldtype FROM keys ORDER BY id ASC;") (reverse keys))) ;; could just sort desc?(define (keys->keystr keys) ;; => key1,key2,key3,additiona1, ...
(string-intersperse (map key:get-fieldname keys) ","))
(string-intersperse keys ","))
(define (args:usage . a) #f)
;; keys->vallist is called several times (quite unnecessarily), use this hash to suppress multiple;; reporting of missing keys on the command line.(define keys:warning-suppress-hash (make-hash-table));;======================================================================
;; key <=> target routines
;;======================================================================
;; this now invalidates using "/" in item names
;; This invalidates using "/" in item names. Every key will be
;; available via args:get-arg as :keyfield. Since this only needs to
;; be called once let's use it to set the environment vars
;;
;; The setting of :keyfield in args should be turned off ASAP
;;
(define (keys:target-set-args keys target ht)
(let ((vals (string-split target "/")))
(if (eq? (length vals)(length keys))
(for-each (lambda (key val)
(setenv key val)
(hash-table-set! ht (conc ":" (vector-ref key 0)) val))
(hash-table-set! ht (conc ":" key) val))
keys
vals)
(debug:print 0 "ERROR: wrong number of values in " target ", should match " keys))
vals))
;; given the keys (a list of vectors <key field>) and a target return a keyval list
;; given the keys (a list of vectors <key field> or a list of keys) and a target return a keyval list
;; keyval list ( (key1 val1) (key2 val2) ...)
(define (keys:target->keyval keys target)
(let* ((targlist (string-split target "/"))
(numkeys (length keys))
(numtarg (length targlist))
(targtweaked (if (> numkeys numtarg)
(append targlist (make-list (- numkeys numtarg) ""))
targlist)))
(map (lambda (key targ)
(list (vector-ref key 0) targ))
(list key targ))
keys targtweaked)))
;;======================================================================
;; key<=> args routines
;; config file related routines
;;======================================================================
;; Using the keys pulled from the database (initially set from the megatest.config file);; look for the equivalent value on the command line and add it to a list, or #f if not found.;; default => (val1 val2 val3 ...);; withkey => (:key1 val1 :key2 val2 :key3 val3 ...)(define (keys->vallist keys . withkey) ;; ORDERING IS VERY IMPORTANT, KEEP PROPER ORDER HERE! (let* ((keynames (map key:get-fieldname keys)) (argkeys (map (lambda (k)(conc ":" k)) keynames)) (withkey (not (null? withkey))) (newremargs (args:get-args (cons "blah" remargs) ;; the cons blah works around a bug in args [args assumes ("calling-prog-name" .... ) ] argkeys '() args:arg-hash 0))) ;;(debug:print 0 "remargs: " remargs " newremargs: " newremargs) (apply append (map (lambda (x) (let ((val (args:get-arg x))) ;; (debug:print 0 "x: " x " val: " val) (if (not val) (begin (if (not (hash-table-ref/default keys:warning-suppress-hash x #f)) (begin (debug:print 0 "WARNING: missing key " x ". Specified in database but not on command line, using \"unk\"") (hash-table-set! keys:warning-suppress-hash x #t))) (set! val "default"))) (if withkey (list x val) (list val)))) argkeys))));; Given a list of keys (list of vectors) return an alist ((key argval) ...)(define (keys->alist keys defaultval) (let* ((keynames (map key:get-fieldname keys)) (newremargs (args:get-args (cons "blah" remargs) (map (lambda (k)(conc ":" k)) keynames) '() args:arg-hash 0))) ;; the cons blah works around a bug in args (map (lambda (key) (let ((val (args:get-arg (conc ":" key)))) (list key (if val val defaultval)))) keynames)))(define (keystring->keys keystring) (map (lambda (x) (let ((xlst (string-split x ":"))) (list->vector (if (> (length xlst) 1) xlst (append (car xlst)(list "TEXT")))))) (delete-duplicates (string-split keystring ","))))
(define (config-get-fields confdat)
(define (keys:config-get-fields confdat)
(let ((fields (hash-table-ref/default confdat "fields" '())))
(map (lambda (x)(vector (car x)(cadr x))) fields)))
(map car fields)))
;; Copyright 2006-2012, Matthew Welland.
;; Copyright 2006-2013, Matthew Welland.
;;
;; This program is made available under the GNU GPL version 2.0 or
;; greater. See the accompanying file COPYING for details.
;;
;; This program is distributed WITHOUT ANY WARRANTY; without even the
;; implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
;; PURPOSE.
(loop (+ i 1)))
)))))
;; then, if runscript ran ok (or did not get called)
;; do all the ezsteps (if any)
(if ezsteps
(let* ((testconfig (read-config (conc work-area "/testconfig") #f #t environ-patt: "pre-launch-env-vars")) ;; FIXME??? is allow-system ok here?
(ezstepslst (hash-table-ref/default testconfig "ezsteps" '())))
(hash-table-set! *testconfigs* test-name testconfig) ;; cached for lazy reads later ...
(if (not (file-exists? ".ezsteps"))(create-directory ".ezsteps"))
;; if ezsteps was defined then we are sure to have at least one step but check anyway
(if (not (> (length ezstepslst) 0))
(debug:print 0 "ERROR: ezsteps defined but ezstepslst is zero length")
(let loop ((ezstep (car ezstepslst))
(tal (cdr ezstepslst))
(prevstep #f))
((eq? rollup-status 2) 'warn)
((eq? rollup-status 0) 'pass)
(else 'fail)))
(next-status (cond
((eq? overall-status 'pass) this-step-status)
((eq? overall-status 'warn)
(if (eq? this-step-status 'fail) 'fail 'warn))
(else 'fail)))) (else 'fail)))
(next-state ;; "RUNNING") ;; WHY WAS THIS CHANGED TO NOT USE (null? tal) ??
(cond
((null? tal) ;; more to run?
"COMPLETED")
(else "RUNNING")))
)
(debug:print 4 "Exit value received: " (vector-ref exit-info 2) " logpro-used: " logpro-used
" this-step-status: " this-step-status " overall-status: " overall-status
" next-status: " next-status " rollup-status: " rollup-status)
(case next-status
((warn)
(set! rollup-status 2)
;; NB// test-set-status! does rdb calls under the hood
(tests:test-set-status! test-id "RUNNING" "WARN"
(tests:test-set-status! test-id next-state "WARN"
(if (eq? this-step-status 'warn) "Logpro warning found" #f)
#f))
((pass)
(tests:test-set-status! test-id "RUNNING" "PASS" #f #f))
(tests:test-set-status! test-id next-state "PASS" #f #f))
(else ;; 'fail
(set! rollup-status 1) ;; force fail
(tests:test-set-status! test-id "RUNNING" "FAIL" (conc "Failed at step " stepname) #f)
(set! rollup-status 1) ;; force fail, this used to be next-state but that doesn't make sense. should always be "COMPLETED"
(tests:test-set-status! test-id "COMPLETED" "FAIL" (conc "Failed at step " stepname) #f)
))))
(if (and (steprun-good? logpro-used (vector-ref exit-info 2))
(not (null? tal)))
(loop (car tal) (cdr tal) stepname)))
(debug:print 4 "WARNING: a prior step failed, stopping at " ezstep))))))))
(monitorjob (lambda ()
(let* ((start-seconds (current-seconds))
(calc-minutes (lambda ()
(inexact->exact
(round
(-
(current-seconds)
start-seconds)))))
(kill-tries 0))
(tests:set-full-meta-info #f test-id run-id (calc-minutes) work-area 10)
(let loop ((minutes (calc-minutes)))
(begin
(set! kill-job? (test-get-kill-request test-id)) ;; run-id test-name itemdat))
(set! kill-job? (or (test-get-kill-request test-id) ;; run-id test-name itemdat))
(and runtlim (let* ((run-seconds (- (current-seconds) start-seconds))
(time-exceeded (> run-seconds runtlim)))
(if time-exceeded
(begin
(debug:print-info 0 "KILLING TEST DUE TO TIME LIMIT EXCEEDED! Runtime=" run-seconds " seconds, limit=" runtlim)
#t)
#f)))))
;; open-run-close not needed for test-set-meta-info
(tests:set-meta-info #f test-id run-id test-name itemdat minutes work-area)
(tests:set-partial-meta-info #f test-id run-id minutes work-area 10)
(if kill-job?
(begin
(mutex-lock! m)
;; NOTE: The pid can change as different steps are run. Do we need handshaking between this
;; section and the runit section? Or add a loop that tries three times with a 1/4 second
;; between tries?
(let* ((pid (vector-ref exit-info 0)))
(if (number? pid)
(handle-exceptions
exn
(debug:print-info 0 "Unable to kill process with pid " pid ", possibly already killed.")
;;(process-signal pid signal/kill))
(begin
(debug:print 0 "WARNING: Request received to kill job (attempt # " kill-tries ")")
(let ((processes (cmd-run->list (conc "pgrep -l -P " pid))))
(for-each
(lambda (p)
(let* ((parts (string-split p))
(p-id (if (> (length parts) 0)
(string->number (car parts))
#f)))
(if p-id
(begin
(debug:print 0 "Killing " (cadr parts) "; kill -9 " p-id)
(system (conc "kill -9 " p-id))))))
(car processes))
(system (conc "kill -9 -" pid))))
(begin
(debug:print 0 "WARNING: Request received to kill job (attempt # " kill-tries ")")
(let ((processes (cmd-run->list (conc "pgrep -l -P " pid))))
(for-each
(lambda (p)
(let* ((parts (string-split p))
(p-id (if (> (length parts) 0)
(string->number (car parts))
#f)))
(if p-id
(begin
(debug:print 0 "Killing " (cadr parts) "; kill -9 " p-id)
;; (process-signal pid signal/kill))))) ;;
(system (conc "kill -9 " p-id))))))
(car processes)))
(system (conc "kill -9 -" pid))
(tests:test-set-status! test-id "KILLED" "FAIL" (args:get-arg "-m") #f)))
(begin
(debug:print 0 "WARNING: Request received to kill job but problem with process, attempting to kill manager process")
(tests:test-set-status! test-id "KILLED" "FAIL"
(tests:test-set-status! test-id "KILLED" "FAIL" (args:get-arg "-m") #f)
(args:get-arg "-m") #f) (sqlite3:finalize! tdb)
(exit 1)))) (exit 1) ;; IS THIS NECESSARY OR WISE???
)))
(set! kill-tries (+ 1 kill-tries))
(mutex-unlock! m)))
;; (sqlite3:finalize! db)
(if keep-going
(begin
(thread-sleep! (+ 10 (random 10))) ;; add some jitter to the call home time to spread out the db accesses
(loop (calc-minutes)))))))
(th1 (make-thread monitorjob))
(th2 (make-thread runit)))
(thread-sleep! 3) ;; (+ 3 (random 6))) ;; add some jitter to the call home time to spread out the db accesses
(if keep-going
(loop (calc-minutes)))))))
(tests:update-central-meta-info test-id (get-cpu-load) (get-df (current-directory))(calc-minutes) #f #f)))) ;; NOTE: Checking twice for keep-going is intentional
(th1 (make-thread monitorjob "monitor job"))
(th2 (make-thread runit "run job")))
(set! job-thread th2)
(thread-start! th1)
(thread-start! th2)
(thread-join! th2)
(set! keep-going #f)
(thread-join! th1)
;; (thread-sleep! 1)
;; (thread-terminate! th1) ;; Not sure if this is a good idea
(thread-sleep! 1) ;; give thread th1 a chance to be done TODO: Verify this is needed. At 0.1 I was getting fail to stop, increased to total of 1.1 sec.
;; (tests:update-central-meta-info test-id cpuload diskfree minutes #f #f)
(mutex-lock! m)
(let* ((item-path (item-list->path itemdat))
;; only state and status needed - use lazy routine
(testinfo (cdb:get-test-info-by-id *runremote* test-id))) ;; )) ;; run-id test-name item-path)))
(testinfo (cdb:remote-run db:get-testinfo-state-status #f test-id))) ;;;(cdb:get-test-info-by-id *runremote* test-id))) ;; )) ;; run-id test-name item-path)))
;; Am I completed?
(if (not (equal? (db:test-get-state testinfo) "COMPLETED"))
(if (member (db:test-get-state testinfo) '("REMOTEHOSTSTART" "RUNNING")) ;; NOTE: It should *not* be REMOTEHOSTSTART but for reasons I don't yet understand it sometimes gets stuck in that state ;; (not (equal? (db:test-get-state testinfo) "COMPLETED"))
(begin (debug:print 2 "Test NOT logged as COMPLETED, (state=" (db:test-get-state testinfo) "), updating result, rollup-status is " rollup-status) (tests:test-set-status! test-id (ifkill-job?"KILLED" "COMPLETED") (cond
(let ((new-state (if kill-job? "KILLED" "COMPLETED") ;; (if (eq? (vector-ref exit-info 2) 0) ;; exited with "good" status
;; "COMPLETED"
;; (db:test-get-state testinfo))) ;; else preseve the state as set within the test
)
(new-status (cond
((not (vector-ref exit-info 1)) "FAIL") ;; job failed to run
((eq? rollup-status 0)
;; if the current status is AUTO the defer to the calculated value (i.e. leave this AUTO)
;; if the current status is AUTO then defer to the calculated value (i.e. leave this AUTO)
(if (equal? (db:test-get-status testinfo) "AUTO") "AUTO" "PASS"))
((eq? rollup-status 1) "FAIL")
((eq? rollup-status 2)
;; if the current status is AUTO the defer to the calculated value but qualify (i.e. make this AUTO-WARN)
(if (equal? (db:test-get-status testinfo) "AUTO") "AUTO-WARN" "WARN"))
(else "FAIL"))
(args:get-arg "-m") #f)))
(else "FAIL")))) ;; (db:test-get-status testinfo)))
(debug:print-info 1 "Test exited in state=" (db:test-get-state testinfo) ", setting state/status based on exit code of " (vector-ref exit-info 1) " and rollup-status of " rollup-status)
(tests:test-set-status! test-id
new-state
new-status
(args:get-arg "-m") #f)
;; need to update the top test record if PASS or FAIL and this is a subtest
;; NO NEED TO CALL roll-up-pass-fail-counts HERE, THIS IS DONE IN roll-up-pass-fail-counts called by tests:test-set-status!
;; (if (not (equal? item-path ""))
;; (begin
;; (thread-sleep! 0.1) ;; give other processes an opportunity to access the db as rollup is lower priority
;; (cdb:roll-up-pass-fail-counts *runremote* run-id test-name item-path new-status)))
))
;; for automated creation of the rollup html file this is a good place...
(if (not (equal? item-path ""))
(tests:summarize-items #f run-id test-name #f)) ;; don't force - just update if no
(tests:summarize-items #f run-id test-id test-name #f))) ;; don't force - just update if no
) (mutex-unlock! m)
;; (exec-results (cmd-run->list fullrunscript)) ;; (list ">" (conc test-name "-run.log")))) ;; (success exec-results)) ;; (eq? (cadr exec-results) 0))) (debug:print 2 "Output from running " fullrunscript ", pid " (vector-ref exit-info 0) " in work area "
work-area ":\n====\n exit code " (vector-ref exit-info 2) "\n" "====\n")
;; (sqlite3:finalize! db) ;; (sqlite3:finalize! tdb) (if (not (vector-ref exit-info 1))
(exit 4)))))))
;; set up the very basics needed for doing anything here.
(define (setup-for-run)
;; would set values for KEYS in the environment here for better support of env-override but
;; have chicken/egg scenario. need to read megatest.config then read it again. Going to
;; pass on that idea for now
;; special case
(if (not (hash-table? *configdat*)) ;; no need to re-open on every call
(begin
(set! *configinfo* (find-and-read-config
(if (args:get-arg "-config")(args:get-arg "-config") "megatest.config")
environ-patt: "env-override"
given-toppath: (get-environment-variable "MT_RUN_AREA_HOME")
pathenvvar: "MT_RUN_AREA_HOME"))
(set! *configdat* (if (car *configinfo*)(car *configinfo*) #f))
(set! *toppath* (if (car *configinfo*)(cadr *configinfo*) #f))
(if *toppath*
(setenv "MT_RUN_AREA_HOME" *toppath*) ;; to be deprecated
(debug:print 0 "ERROR: failed to find the top path to your run setup."))
(set! *configinfo* (find-and-read-config
(if (args:get-arg "-config")(args:get-arg "-config") "megatest.config")
environ-patt: "env-override"
given-toppath: (get-environment-variable "MT_RUN_AREA_HOME")
pathenvvar: "MT_RUN_AREA_HOME"))
(set! *configdat* (if (car *configinfo*)(car *configinfo*) #f))
(set! *toppath* (if (car *configinfo*)(cadr *configinfo*) #f))
(if *toppath*
(setenv "MT_RUN_AREA_HOME" *toppath*) ;; to be deprecated
(debug:print 0 "ERROR: failed to find the top path to your Megatest area."))))
*toppath*)
(define (get-best-disk confdat)
(let* ((disks (hash-table-ref/default confdat "disks" #f))
(best #f)
(bestsize 0))
(if disks
(debug:print 2 "INFO:\n lnkbase=" lnkbase "\n lnkpath=" lnkpath "\n toptest-path=" toptest-path "\n test-path=" test-path)
(if (not (file-exists? linktree))
(begin
(debug:print 0 "WARNING: linktree did not exist! Creating it now at " linktree)
(create-directory linktree #t))) ;; (system (conc "mkdir -p " linktree))))
;; create the directory for the tests dir links, this is needed no matter what...
(if (not (directory-exists? lnkbase))
(if (and (not (directory-exists? lnkbase))
(not (file-exists? lnkbase)))
(create-directory lnkbase #t))
;; update the toptest record with its location rundir, cache the path
;; This wass highly inefficient, one db write for every subtest, potentially
;; thousands of unnecessary updates, cache the fact it was set and don't set it
;; again.
" - creating link from: " test-path "\n"
" to: " lnktarget)
;; If there is already a symlink delete it and recreate it.
(handle-exceptions
exn
(begin
(debug:print 0 "ERROR: Failed to re-create link " linktarget ((condition-property-accessor 'exn 'message) exn) ", exiting")
(debug:print 0 "ERROR: Failed to re-create link " lnktarget ((condition-property-accessor 'exn 'message) exn) ", exiting")
(exit))
(if (symbolic-link? lnktarget) (delete-file lnktarget))
(if (not (file-exists? lnktarget)) (create-symbolic-link test-path lnktarget)))))
;; I suspect this section was deleting test directories under some
;; wierd sitations? This doesn't make sense - reenabling the rm -f
;; I honestly don't remember *why* this chunk was needed...
;; 1. look though disks list for disk with most space
;; 2. create run dir on disk, path name is meaningful
;; 3. create link from run dir to megatest runs area
;; 4. remotely run the test on allocated host
;; - could be ssh to host from hosts table (update regularly with load)
;; - could be netbatch
;; (launch-test db (cadr status) test-conf))
(define (launch-test test-id run-id run-info key-vals runname test-conf keyvallst test-name test-path itemdat params)
(define (launch-test test-id run-id run-info keyvals runname test-conf test-name test-path itemdat params)
(change-directory *toppath*)
(alist->env-vars ;; consolidate this code with the code in megatest.scm for "-execute"
(list ;; (list "MT_TEST_RUN_DIR" work-area)
(list "MT_RUN_AREA_HOME" *toppath*)
(list "MT_TEST_NAME" test-name)
;; (list "MT_ITEM_INFO" (conc itemdat))
(list "MT_RUNNAME" runname)
;; (list "MT_TARGET" mt_target)
))
(let* ((useshell (config-lookup *configdat* "jobtools" "useshell"))
(launcher (config-lookup *configdat* "jobtools" "launcher"))
(runscript (config-lookup test-conf "setup" "runscript"))
(ezsteps (> (length (hash-table-ref/default test-conf "ezsteps" '())) 0)) ;; don't send all the steps, could be big
(diskspace (config-lookup test-conf "requirements" "diskspace"))
(memory (config-lookup test-conf "requirements" "memory"))
(hosts (config-lookup *configdat* "jobtools" "workhosts"))
(let* ((useshell (let ((ush (config-lookup *configdat* "jobtools" "useshell")))
(if ush
(if (equal? ush "no") ;; must use "no" to NOT use shell
#f
ush)
#t))) ;; default is yes
(launcher (config-lookup *configdat* "jobtools" "launcher"))
(runscript (config-lookup test-conf "setup" "runscript"))
(ezsteps (> (length (hash-table-ref/default test-conf "ezsteps" '())) 0)) ;; don't send all the steps, could be big
(diskspace (config-lookup test-conf "requirements" "diskspace"))
(memory (config-lookup test-conf "requirements" "memory"))
(hosts (config-lookup *configdat* "jobtools" "workhosts"))
(remote-megatest (config-lookup *configdat* "setup" "executable"))
(run-time-limit (or (configf:lookup test-conf "requirements" "runtimelim")
(configf:lookup *configdat* "setup" "runtimelim")))
;; FIXME SOMEDAY: not good how this is so obtuse, this hack is to
;; allow running from dashboard. Extract the path
;; from the called megatest and convert dashboard
;; or dboard to megatest
(local-megatest (let* ((lm (car (argv)))
(dir (pathname-directory lm))
(exe (pathname-strip-directory lm)))
(conc (if dir (conc dir "/") "")
(case (string->symbol exe)
((dboard) "megatest")
((mtest) "megatest")
((dboard) "../megatest")
((mtest) "../megatest")
((dashboard) "megatest")
(else exe)))))
(test-sig (conc test-name ":" (item-list->path itemdat))) ;; test-path is the full path including the item-path
(work-area #f)
(toptest-work-area #f) ;; for iterated tests the top test contains data relevant for all
(diskpath #f)
(cmdparms #f)
(fullcmd #f) ;; (define a (with-output-to-string (lambda ()(write x))))
(mt-bindir-path #f)
(item-path (item-list->path itemdat))
;; (test-id (cdb:remote-run db:get-test-id #f run-id test-name item-path))
(testinfo (cdb:get-test-info-by-id *runremote* test-id))
(mt_target (string-intersperse (map cadr keyvallst) "/"))
(mt_target (string-intersperse (map cadr keyvals) "/"))
(debug-param (append (if (args:get-arg "-debug") (list "-debug" (args:get-arg "-debug")) '())
(if (args:get-arg "-logging")(list "-logging") '()))))
(setenv "MT_ITEMPATH" item-path)
(if hosts (set! hosts (string-split hosts)))
;; set the megatest to be called on the remote host
(if (not remote-megatest)(set! remote-megatest local-megatest)) ;; "megatest"))
(set! mt-bindir-path (pathname-directory remote-megatest))
(if launcher (set! launcher (string-split launcher)))
;; set up the run work area for this test
(set! diskpath (get-best-disk *configdat*))
(if diskpath
(let ((dat (create-work-area run-id run-info key-vals test-id test-path diskpath test-name itemdat)))
(let ((dat (create-work-area run-id run-info keyvals test-id test-path diskpath test-name itemdat)))
(set! work-area (car dat))
(set! toptest-work-area (cadr dat))
(debug:print-info 2 "Using work area " work-area))
(begin
(set! work-area (conc test-path "/tmp_run"))
(create-directory work-area #t)
(debug:print 0 "WARNING: No disk work area specified - running in the test directory under tmp_run")))
(list 'runscript runscript)
(list 'run-id run-id )
(list 'test-id test-id )
(list 'itemdat itemdat )
(list 'megatest remote-megatest)
(list 'ezsteps ezsteps)
(list 'target mt_target)
(list 'runtlim (if run-time-limit (common:hms-string->seconds run-time-limit) #f))
(list 'env-ovrd (hash-table-ref/default *configdat* "env-override" '()))
(list 'set-vars (if params (hash-table-ref/default params "-setvars" #f)))
(list 'runname runname)
(list 'mt-bindir-path mt-bindir-path))))))) ;; (string-intersperse keyvallst " ")))) (list 'mt-bindir-path mt-bindir-path)))))))
;; clean out step records from previous run if they exist
;; (debug:print-info 4 "FIXMEEEEE!!!! This can be removed some day, perhaps move all test records to the test db?")
;; (open-run-close db:delete-test-step-records db test-id)
(change-directory work-area) ;; so that log files from the launch process don't clutter the test dir
(tests:test-set-status! test-id "LAUNCHED" "n/a" #f #f) ;; (if launch-results launch-results "FAILED"))
(cond
((and launcher hosts) ;; must be using ssh hostname
;; Copyright 2006-2013, Matthew Welland.
;;
;; This program is made available under the GNU GPL version 2.0 or
;; greater. See the accompanying file COPYING for details.
;;
;; This program is distributed WITHOUT ANY WARRANTY; without even the
;; implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
;; PURPOSE.
;;======================================================================
;; launch a task - this runs on the originating host, tests themselves
;;
;;======================================================================
(use sqlite3 srfi-18)
(import (prefix sqlite3 sqlite3:))
(declare (unit lock-queue))
(declare (uses common))
;;======================================================================
;; attempt to prevent overlapping updates of rollup files by queueing
;; update requests in an sqlite db
;;======================================================================
(define (lock-queue:open-db fname)
(let* ((actualfname (conc fname ".lockdb"))
(dbexists (file-exists? actualfname))
(db (sqlite3:open-database actualfname))
(handler (make-busy-timeout 136000)))
(if dbexists
db
(begin
(sqlite3:execute
db
"CREATE TABLE IF NOT EXISTS queue (
id INTEGER PRIMARY KEY,
test_id INTEGER,
start_time INTEGER,
state TEXT,
CONSTRAINT queue_constraint UNIQUE (test_id));")
(sqlite3:execute
db
"CREATE TABLE IF NOT EXISTS runlocks (
id INTEGER PRIMARY KEY,
test_id INTEGER,
run_lock TEXT,
CONSTRAINT runlock_constraint UNIQUE (run_lock));")))
(sqlite3:set-busy-handler! db handler)
db))
(define (lock-queue:set-state db test-id newstate)
(sqlite3:execute db "UPDATE queue SET state=? WHERE test_id=?;"
newstate
test-id))
(define (lock-queue:any-younger? db mystart test-id)
(let ((res #f))
(sqlite3:for-each-row
(lambda (tid)
;; Actually this should not be needed as mystart cannot be simultaneously less than and test-id same as
(if (not (equal? tid test-id))
(set! res tid)))
db
"SELECT test_id FROM queue WHERE start_time > ?;" mystart)
res))
(define (lock-queue:get-lock db test-id)
(let ((res #f)
(lckqry (sqlite3:prepare db "SELECT test_id,run_lock FROM runlocks WHERE run_lock='locked';"))
(mklckqry (sqlite3:prepare db "INSERT INTO runlocks (test_id,run_lock) VALUES (?,'locked');")))
(let ((result
(handle-exceptions
exn
#f
(sqlite3:with-transaction
db
(lambda ()
(sqlite3:for-each-row (lambda (tid lockstate)
(set! res (list tid lockstate)))
lckqry)
(if res
(if (equal? (car res) test-id)
#t ;; already have the lock
#f)
(begin
(sqlite3:execute mklckqry test-id)
;; if no error handled then return #t for got the lock
#t)))))))
(sqlite3:finalize! lckqry)
(sqlite3:finalize! mklckqry)
result)))
(define (lock-queue:release-lock fname test-id)
(let ((db (lock-queue:open-db fname)))
(sqlite3:execute db "DELETE FROM runlocks WHERE test_id=?;" test-id)
(sqlite3:finalize! db)))
(define (lock-queue:steal-lock db test-id)
(sqlite3:execute db "DELETE FROM runlocks WHERE run_lock='locked';")
(lock-queue:get-lock db test-it))
;; returns #f if ok to skip the task
;; returns #t if ok to proceed with task
;; otherwise waits
;;
(define (lock-queue:wait-turn fname test-id)
(let ((db (lock-queue:open-db fname))
(mystart (current-seconds)))
(sqlite3:execute
db
"INSERT OR REPLACE INTO queue (test_id,start_time,state) VALUES (?,?,'waiting');"
test-id mystart)
(thread-sleep! 1) ;; give other tests a chance to register
(let ((result
(let loop ((younger-waiting (lock-queue:any-younger? db mystart test-id)))
(if younger-waiting
(begin
;; no need for us to wait. mark in the lock queue db as skipping
(lock-queue:set-state db test-id "skipping")
#f) ;; let the calling process know that nothing needs to be done
(if (lock-queue:get-lock db test-id)
#t
(if (> (- (current-seconds) mystart) 36000) ;; waited too long, steal the lock
(lock-queue:steal-lock db test-id)
(begin
(thread-sleep! 1)
(loop (lock-queue:any-younger? db mystart test-id)))))))))
(sqlite3:finalize! db)
result)))
;; (use trace)
;; (trace lock-queue:get-lock lock-queue:release-lock lock-queue:wait-turn lock-queue:any-younger? lock-queue:set-state)
;; Copyright 2006-2012, Matthew Welland.
;;
;; This program is made available under the GNU GPL version 2.0 or
;; greater. See the accompanying file COPYING for details.
;;
;; This program is distributed WITHOUT ANY WARRANTY; without even the
;; implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
;; PURPOSE.
;; (include "common.scm")
;; (include "megatest-version.scm")
(use sqlite3 srfi-1 posix regex regex-case srfi-69 base64 format readline apropos json http-client) ;; (srfi 18) extras)
(use sqlite3 srfi-1 posix regex regex-case srfi-69 base64 format readline apropos json http-client directory-utils) ;; (srfi 18) extras)
(import (prefix sqlite3 sqlite3:))
(import (prefix base64 base64:))
;; (use zmq)
(declare (uses common))
(declare (uses megatest-version))
(declare (uses db))
(define *db* #f) ;; this is only for the repl, do not use in general!!!!
(include "common_records.scm")
(include "key_records.scm")
(include "db_records.scm")
(include "run_records.scm")
(include "megatest-fossil-hash.scm")
(let ((debugcontrolf (conc (get-environment-variable "HOME") "/.megatestrc")))
(if (file-exists? debugcontrolf)
(load debugcontrolf)))
;; Disabled help items
;; -rollup : (currently disabled) fill run (set by :runname) with latest test(s)
;; from prior runs with same keys
(define help (conc "
Megatest, documentation at http://www.kiatoa.com/fossils/megatest
version " megatest-version "
license GPL, Copyright Matt Welland 2006-2012
Usage: megatest [options]
-h : this help
-version : print megatest version (currently " megatest-version ")
Launching and managing runs
-runall : run all tests that are not state COMPLETED and status PASS,
CHECK or KILLED
-runtests tst1,tst2 ... : run tests
-remove-runs : remove the data for a run, requires :runname and -testpatt
-remove-runs : remove the data for a run, requires -runname and -testpatt
Optionally use :state and :status
-set-state-status X,Y : set state to X and status to Y, requires controls per -remove-runs
-rerun FAIL,WARN... : force re-run for tests with specificed status(s)
-rollup : (currently disabled) fill run (set by :runname) with latest test(s) from prior runs with same keys -lock : lock run specified by target and runname
-unlock : unlock run specified by target and runname
-set-run-status status : sets status for run to status, requires -target and -runname
-get-run-status : gets status for run specified by target and runname
-run-wait : wait on run specified by target and runname
Selectors (e.g. use for -runtests, -remove-runs, -set-state-status, -list-runs etc.)
-target key1/key2/... : run for key1, key2, etc.
-reqtarg key1/key2/... : run for key1, key2, etc. but key1/key2 must be in runconfig
-testpatt patt1/patt2,patt3/... : % is wildcard
:runname : required, name for this particular test run
:state : Applies to runs, tests or steps depending on context
:status : Applies to runs, tests or steps depending on context
-runname : required, name for this particular test run
-state : Applies to runs, tests or steps depending on context
-status : Applies to runs, tests or steps depending on context
Test helpers (for use inside tests)
-step stepname
-test-status : set the state and status of a test (use :state and :status)
-setlog logfname : set the path/filename to the final log relative to the test
directory. may be used with -test-status
-set-toplog logfname : set the overall log for a suite of sub-tests
:units : name of the units for value, expected_value etc. (optional)
-load-test-data : read test specific data for storage in the test_data table
from standard in. Each line is comma delimited with four
fields category,variable,value,comment
Queries
-list-runs patt : list runs matching pattern \"patt\", % is the wildcard
-showkeys : show the keys used in this megatest setup
-test-files targpatt : get the most recent test path/file matching targpatt e.g. %/%...
-show-keys : show the keys used in this megatest setup
-test-files targpatt : get the most recent test path/file matching targpatt e.g. %/%...
returns list sorted by age ascending, see examples below
-test-paths : get the test paths matching target, runname, item and test
patterns.
-list-disks : list the disks available for storing runs
-list-targets : list the targets in runconfigs.config
-list-db-targets : list the target combinations used in the db
-show-config : dump the internal representation of the megatest.config file
-show-runconfig : dump the internal representation of the runconfigs.config file
-dumpmode json : dump in json format instead of sexpr
-show-cmdinfo : dump the command info for a test (run in test environment)
-section sectionName
-var varName : for config and runconfig lookup value for sectionName varName
Misc
-start-dir path : switch to this directory before running megatest
-rebuild-db : bring the database schema up to date
-cleanup-db : remove any orphan records, vacuum the db
-update-meta : update the tests metadata for all tests
-env2file fname : write the environment to fname.csh and fname.sh -setvars VAR1=val1,VAR2=val2 : Add environment variables to a run NB// these are
overwritten by values set in config files.
-server -|hostname : start the server (reduces contention on megatest.db), use
- to automatically figure out hostname
-transport http|fs : use http or direct access for transport (default is http)
-daemonize : fork into background and disconnect from stdin/out
-list-servers : list the servers
-stop-server id : stop server specified by id (see output of -list-servers)
-stop-server id : stop server specified by id (see output of -list-servers), use
0 to kill all
-repl : start a repl (useful for extending megatest)
-load file.scm : load and run file.scm
-mark-incompletes : find and mark incomplete tests
Utilities
-env2file fname : write the environment to fname.csh and fname.sh
-refdb2dat refdb : convert refdb to sexp or to format specified by -dumpmode
formats: perl, ruby, sqlite3
-o : output file for refdb2dat (defaults to stdout)
Spreadsheet generation
-extract-ods fname.ods : extract an open document spreadsheet from the database
-pathmod path : insert path, i.e. path/runame/itempath/logfile.html
will clear the field if no rundir/testname/itempath/logfile
if it contains forward slashes the path will be converted
to windows style
Getting started
-gen-megatest-area : create a skeleton megatest area. You will be prompted for paths
-gen-megatest-test tname : create a skeleton megatest test. You will be prompted for info
Examples
# Get test path, use '.' to get a single path or a specific path/file pattern
megatest -test-files 'logs/*.log' -target ubuntu/n%/no% :runname w49% -testpatt test_mt%
megatest -test-files 'logs/*.log' -target ubuntu/n%/no% -runname w49% -testpatt test_mt%
Called as " (string-intersperse (argv) " ") "
Version " megatest-version ", built from " megatest-fossil-hash ))
;; -gui : start a gui interface
;; -config fname : override the runconfig file with fname
;; process args
(define remargs (args:get-args
(argv)
(list "-runtests" ;; run a specific test
"-config" ;; override the config file name
"-execute" ;; run the command encoded in the base64 parameter
"-step"
":runname" "-target"
"-reqtarg"
":runname"
"-runname"
":state"
"-state"
":status"
;; process deferred tasks per above steps
;; run all tests are are Not COMPLETED and PASS or CHECK
(if (args:get-arg "-runall")
(general-run-call
"-runall"
"run all tests"
(lambda (target runname keys keynames keyvallst)
(lambda (target runname keys keyvals)
(runs:run-tests target
runname
"%" (args:get-arg "-testpatt")
user
args:arg-hash))))
;;======================================================================
;; run one test
;;======================================================================
(run-id (assoc/default 'run-id cmdinfo))
(test-id (assoc/default 'test-id cmdinfo))
(itemdat (assoc/default 'itemdat cmdinfo))
(work-area (assoc/default 'work-area cmdinfo))
(db #f))
(change-directory testpath)
;; (set! *runremote* runremote)
;; The transport is handled earlier in the loading process of megatest.
(set! *transport-type* (string->symbol transport))
;; (set! *transport-type* (string->symbol transport))
(if (not (setup-for-run))
(begin
(debug:print 0 "Failed to setup, exiting")
(exit 1)))
(if (and state status)
;; DO NOT remote run, makes calls to the testdat.db test db.
(db:teststep-set-status! db test-id step state status msg logfile work-area: work-area)
;;======================================================================
;; Copyright 2006-2012, Matthew Welland.
;; Copyright 2006-2013, Matthew Welland.
;;
;; This program is made available under the GNU GPL version 2.0 or
;; greater. See the accompanying file COPYING for details.
;;
;; This program is distributed WITHOUT ANY WARRANTY; without even the
;; implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
;; PURPOSE.
;; The runconfigs.config file
;;
(define (rconfig window-id)
(iup:vbox
(iup:frame #:title "Default")))
;;======================================================================;; T R E E S T U F F ;;======================================================================;; path is a list of nodes, each the child of the previous;; this routine returns the id so another node can be added;; either as a leaf or as a branch;;;; BUG: This needs a stop sensor for when a branch is exhausted;;(define (tree-find-node obj path) ;; start at the base of the tree (if (null? path) #f ;; or 0 ???? (let loop ((hed (car path)) (tal (cdr path)) (depth 0) (nodenum 0)) ;; nodes in iup tree are 100% sequential so iterate over nodenum (if (iup:attribute obj (conc "DEPTH" nodenum)) ;; end when no more nodes (let ((node-depth (string->number (iup:attribute obj (conc "DEPTH" nodenum)))) (node-title (iup:attribute obj (conc "TITLE" nodenum)))) (if (and (equal? depth node-depth) (equal? hed node-title)) ;; yep, this is the one! (if (null? tal) ;; end of the line nodenum (loop (car tal)(cdr tal)(+ depth 1)(+ 1 nodenum))) ;; this is the case where we found part of the hierarchy but not ;; all of it, i.e. the node-depth went from deep to less deep (if (> depth node-depth) ;; (+ 1 node-depth)) #f (loop hed tal depth (+ nodenum 1))))) #f))));; top is the top node name zeroeth node VALUE=0(define (tree-add-node obj top nodelst #!key (userdata #f)) (if (not (iup:attribute obj "TITLE0")) (iup:attribute-set! obj "ADDBRANCH0" top)) (cond ((not (string=? top (iup:attribute obj "TITLE0"))) (print "ERROR: top name " top " doesn't match " (iup:attribute obj "TITLE0"))) ((null? nodelst)) (else (let loop ((hed (car nodelst)) (tal (cdr nodelst)) (depth 1) (pathl (list top))) ;; Because the tree dialog changes node numbers when ;; nodes are added or removed we must look up nodes ;; each and every time. 0 is the top node so default ;; to that. (let* ((newpath (append pathl (list hed))) (parentnode (tree-find-node obj pathl)) (nodenum (tree-find-node obj newpath))) ;; Add the branch under lastnode if not found (if (not nodenum) (begin (iup:attribute-set! obj (conc "ADDBRANCH" parentnode) hed) (if userdata (iup:attribute-set! obj (conc "USERDATA" parentnode) userdata)) (if (null? tal) #t ;; reset to top (loop (car nodelst)(cdr nodelst) 1 (list top)))) (if (null? tal) ;; if null here then this path has already been added #t (loop (car tal)(cdr tal)(+ depth 1) newpath))))))))(define (tree-node->path obj nodenum) ;; (print "\ncurrnode nodenum depth node-depth node-title path") (let loop ((currnode 0) (depth 0) (path '())) (let ((node-depth (iup:attribute obj (conc "DEPTH" currnode))) (node-title (iup:attribute obj (conc "TITLE" currnode)))) ;; (display (conc "\n "currnode " " nodenum " " depth " " node-depth " " node-title " " path)) (if (> currnode nodenum) path (if (not node-depth) ;; #f if we are out of nodes '() (let ((ndepth (string->number node-depth))) (if (eq? ndepth depth) ;; This next is the match condition depth == node-depth (if (eq? currnode nodenum) (begin ;; (display " <X>") (append path (list node-title))) (loop (+ currnode 1) (+ depth 1) (append path (list node-title)))) ;; didn't match, reset to base path and keep looking ;; due to more iup odditys we don't reset to base (begin ;; (display " <L>") (loop (+ 1 currnode) 2 (append (take path ndepth)(list node-title)))))))))));;======================================================================
;; T E S T S
;;======================================================================
(define (tree-path->test-id path)
(if (not (null? path))
(hash-table-ref/default (dboard:data-get-path-test-ids *data*) path #f)
runs-matrix)))))
;; Browse and control a single run
;;
(define (runcontrol window-id)
(iup:hbox))
;;======================================================================;; P R O C E S S R U N S;;======================================================================;; MOVE THIS INTO *data*(define *cachedata* (make-hash-table))(hash-table-set! *cachedata* "runid-to-col" (make-hash-table))(hash-table-set! *cachedata* "testname-to-row" (make-hash-table));; TO-DO;; 1. Make "data" hash-table hierarchial store of all displayed data;; 2. Update synchash to understand "get-runs", "get-tests" etc.;; 3. Add extraction of filters to synchash calls;;;; Mode is 'full or 'incremental for full refresh or incremental refresh(define (run-update keys data runname keypatts testpatt states statuses mode window-id) (let* (;; count and offset => #f so not used ;; the synchash calls modify the "data" hash (get-runs-sig (conc (client:get-signature) " get-runs")) (get-tests-sig (conc (client:get-signature) " get-tests")) (get-details-sig (conc (client:get-signature) " get-test-details")) ;; test-ids to get and display are indexed on window-id in curr-test-ids hash (test-ids (hash-table-values (dboard:data-get-curr-test-ids *data*))) (run-changes (synchash:client-get 'db:get-runs get-runs-sig (length keypatts) data runname #f #f keypatts)) (tests-detail-changes (if (not (null? test-ids)) (synchash:client-get 'db:get-test-info-by-ids get-details-sig 0 data test-ids) '())) ;; Now can calculate the run-ids (run-hash (hash-table-ref/default data get-runs-sig #f)) (run-ids (if run-hash (filter number? (hash-table-keys run-hash)) '())) (test-changes (synchash:client-get 'db:get-tests-for-runs-mindata get-tests-sig 0 data run-ids testpatt states statuses)) (runs-hash (hash-table-ref/default data get-runs-sig #f)) (header (hash-table-ref/default runs-hash "header" #f)) (run-ids (sort (filter number? (hash-table-keys runs-hash)) (lambda (a b) (let* ((record-a (hash-table-ref runs-hash a)) (record-b (hash-table-ref runs-hash b)) (time-a (db:get-value-by-header record-a header "event_time")) (time-b (db:get-value-by-header record-b header "event_time"))) (> time-a time-b))) )) (runid-to-col (hash-table-ref *cachedata* "runid-to-col")) (testname-to-row (hash-table-ref *cachedata* "testname-to-row")) (colnum 1) (rownum 0)) ;; rownum = 0 is the header;; (debug:print 0 "test-ids " test-ids ", tests-detail-changes " tests-detail-changes) ;; tests related stuff ;; (all-testnames (delete-duplicates (map db:test-get-testname test-changes)))) ;; Given a run-id and testname/item_path calculate a cell R:C ;; NOTE: Also build the test tree browser and look up table ;; ;; Each run is unique on its keys and runname or run-id, store in hash on colnum (for-each (lambda (run-id) (let* ((run-record (hash-table-ref/default runs-hash run-id #f)) (key-vals (map (lambda (key)(db:get-value-by-header run-record header key)) (map key:get-fieldname keys))) (run-name (db:get-value-by-header run-record header "runname")) (col-name (conc (string-intersperse key-vals "\n") "\n" run-name)) (run-path (append key-vals (list run-name)))) (hash-table-set! (dboard:data-get-run-keys *data*) run-id run-path) (iup:attribute-set! (dboard:data-get-runs-matrix *data*) (conc rownum ":" colnum) col-name) (hash-table-set! runid-to-col run-id (list colnum run-record)) ;; Here we update the tests treebox and tree keys (tree-add-node (dboard:data-get-tests-tree *data*) "Runs" (append key-vals (list run-name)) userdata: (conc "run-id: " run-id)) (set! colnum (+ colnum 1)))) run-ids) ;; Scan all tests to be displayed and organise all the test names, respecting what is in the hash table ;; Do this analysis in the order of the run-ids, the most recent run wins (for-each (lambda (run-id) (let* ((run-path (hash-table-ref (dboard:data-get-run-keys *data*) run-id)) (new-test-dat (car test-changes)) (removed-tests (cadr test-changes)) (tests (sort (map cadr (filter (lambda (testrec) (eq? run-id (db:mintest-get-run_id (cadr testrec)))) new-test-dat)) (lambda (a b) (let ((time-a (db:mintest-get-event_time a)) (time-b (db:mintest-get-event_time b))) (> time-a time-b))))) ;; test-changes is a list of (( id record ) ... ) ;; Get list of test names sorted by time, remove tests (test-names (delete-duplicates (map (lambda (t) (let ((i (db:mintest-get-item_path t)) (n (db:mintest-get-testname t))) (if (string=? i "") (conc " " i) n))) tests))) (colnum (car (hash-table-ref runid-to-col run-id)))) ;; for each test name get the slot if it exists and fill in the cell ;; or take the next slot and fill in the cell, deal with items in the ;; run view panel? The run view panel can have a tree selector for ;; browsing the tests/items ;; SWITCH THIS TO USING CHANGED TESTS ONLY (for-each (lambda (test) (let* ((test-id (db:mintest-get-id test)) (state (db:mintest-get-state test)) (status (db:mintest-get-status test)) (testname (db:mintest-get-testname test)) (itempath (db:mintest-get-item_path test)) (fullname (conc testname "/" itempath)) (dispname (if (string=? itempath "") testname (conc " " itempath))) (rownum (hash-table-ref/default testname-to-row fullname #f)) (test-path (append run-path (if (equal? itempath "") (list testname) (list testname itempath))))) (tree-add-node (dboard:data-get-tests-tree *data*) "Runs" test-path userdata: (conc "test-id: " test-id)) (hash-table-set! (dboard:data-get-path-test-ids *data*) test-path test-id) (if (not rownum) (let ((rownums (hash-table-values testname-to-row))) (set! rownum (if (null? rownums) 1 (+ 1 (apply max rownums)))) (hash-table-set! testname-to-row fullname rownum) ;; create the label (iup:attribute-set! (dboard:data-get-runs-matrix *data*) (conc rownum ":" 0) dispname) )) ;; set the cell text and color ;; (debug:print 2 "rownum:colnum=" rownum ":" colnum ", state=" status) (iup:attribute-set! (dboard:data-get-runs-matrix *data*) (conc rownum ":" colnum) (if (string=? state "COMPLETED") status state)) (iup:attribute-set! (dboard:data-get-runs-matrix *data*) (conc "BGCOLOR" rownum ":" colnum) (gutils:get-color-for-state-status state status)) )) tests))) run-ids) (let ((updater (hash-table-ref/default (dboard:data-get-updaters *data*) window-id #f))) (if updater (updater (hash-table-ref/default data get-details-sig #f)))) (iup:attribute-set! (dboard:data-get-runs-matrix *data*) "REDRAW" "ALL") ;; (debug:print 2 "run-changes: " run-changes) ;; (debug:print 2 "test-changes: " test-changes) (list run-changes test-changes)));;======================================================================
;; D A S H B O A R D
;;======================================================================
;; Main Panel
(define (main-panel window-id)
(iup:dialog
#:title "Megatest Control Panel"
#:menu (main-menu)
#:menu (dcommon:main-menu)
(let ((tabtop (iup:tabs
(runs window-id)
(tests window-id)
(runcontrol window-id)
(mtest window-id)
(rconfig window-id)
)))
;; test-records is a hash table testname:item_path => vector < testname testconfig waitons priority items-info ... >(define (runs:run-tests-queue-classic run-id runname test-records keyvallst flags test-patts required-tests) ;; At this point the list of parent tests is expanded ;; NB// Should expand items here and then insert into the run queue. (debug:print 5 "test-records: " test-records ", keyvallst: " keyvallst " flags: " (hash-table->alist flags)) (let ((run-info (cdb:remote-run db:get-run-info #f run-id)) (key-vals (cdb:remote-run db:get-key-vals #f run-id)) (sorted-test-names (tests:sort-by-priority-and-waiton test-records)) (test-registry (make-hash-table)) (registry-mutex (make-mutex)) (num-retries 0) (max-retries (config-lookup *configdat* "setup" "maxretries")) (max-concurrent-jobs (let ((mcj (config-lookup *configdat* "setup" "max_concurrent_jobs"))) (if (and mcj (string->number mcj)) (string->number mcj) 1)))) (set! max-retries (if (and max-retries (string->number max-retries))(string->number max-retries) 100)) (if (not (null? sorted-test-names)) (let loop ((hed (car sorted-test-names)) (tal (cdr sorted-test-names)) (reruns '())) (if (not (null? reruns))(debug:print-info 4 "reruns=" reruns)) ;; (print "Top of loop, hed=" hed ", tal=" tal " ,reruns=" reruns) (let* ((test-record (hash-table-ref test-records hed)) (test-name (tests:testqueue-get-testname test-record)) (tconfig (tests:testqueue-get-testconfig test-record)) (testmode (let ((m (config-lookup tconfig "requirements" "mode"))) (if m (string->symbol m) 'normal))) (waitons (tests:testqueue-get-waitons test-record)) (priority (tests:testqueue-get-priority test-record)) (itemdat (tests:testqueue-get-itemdat test-record)) ;; itemdat can be a string, list or #f (items (tests:testqueue-get-items test-record)) (item-path (item-list->path itemdat)) (newtal (append tal (list hed)))) (debug:print 6 "test-name: " test-name "\n hed: " hed "\n itemdat: " itemdat "\n items: " items "\n item-path: " item-path "\n waitons: " waitons "\n num-retries: " num-retries "\n tal: " tal "\n reruns: " reruns) ;; check for hed in waitons => this would be circular, remove it and issue an ;; error (if (member test-name waitons) (begin (debug:print 0 "ERROR: test " test-name " has listed itself as a waiton, please correct this!") (set! waiton (filter (lambda (x)(not (equal? x hed))) waitons)))) (cond ;; OUTER COND ((not items) ;; when false the test is ok to be handed off to launch (but not before) (if (and (not (tests:match test-patts (tests:testqueue-get-testname test-record) item-path required: required-tests)) (not (null? tal))) (loop (car newtal)(cdr newtal) reruns)) (let* ((run-limits-info (runs:can-run-more-tests test-record max-concurrent-jobs)) ;; look at the test jobgroup and tot jobs running (have-resources (car run-limits-info)) (num-running (list-ref run-limits-info 1)) (num-running-in-jobgroup (list-ref run-limits-info 2)) (max-concurrent-jobs (list-ref run-limits-info 3)) (job-group-limit (list-ref run-limits-info 4)) (prereqs-not-met (db:get-prereqs-not-met run-id waitons item-path mode: testmode)) (fails (runs:calc-fails prereqs-not-met)) (non-completed (runs:calc-not-completed prereqs-not-met))) (debug:print-info 8 "have-resources: " have-resources " prereqs-not-met: " (string-intersperse (map (lambda (t) (if (vector? t) (conc (db:test-get-state t) "/" (db:test-get-status t)) (conc " WARNING: t is not a vector=" t ))) prereqs-not-met) ", ") " fails: " fails) (debug:print-info 4 "hed=" hed "\n test-record=" test-record "\n test-name: " test-name "\n item-path: " item-path "\n test-patts: " test-patts) ;; Don't know at this time if the test have been launched at some time in the past ;; i.e. is this a re-launch? (debug:print-info 4 "run-limits-info = " run-limits-info) (cond ;; INNER COND #1 for a launchable test ;; Check item path against item-patts ((not (tests:match test-patts (tests:testqueue-get-testname test-record) item-path required: required-tests)) ;; This test/itempath is not to be run ;; else the run is stuck, temporarily or permanently ;; but should check if it is due to lack of resources vs. prerequisites (debug:print-info 1 "Skipping " (tests:testqueue-get-testname test-record) " " item-path " as it doesn't match " test-patts) ;; (thread-sleep! *global-delta*) (if (not (null? tal)) (loop (car tal)(cdr tal) reruns))) ;; Registry has been started for this test but has not yet completed ;; this should be rare, the case where there are only a couple of tests and the db is slow ;; delay a short while and continue ;; ((eq? (hash-table-ref/default test-registry (runs:make-full-test-name test-name item-path) #f) 'start) ;; (thread-sleep! 0.01) ;; (loop (car newtal)(cdr newtal) reruns)) ;; count number of 'done, if more than 100 then skip on through. (;; (and (< (length (filter (lambda (x)(eq? x 'done))(hash-table-values test-registry))) 100) ;; why get more than 200 ahead? (not (hash-table-ref/default test-registry (runs:make-full-test-name test-name item-path) #f)) ;; ) ;; too many changes required. Implement later. (debug:print-info 4 "Pre-registering test " test-name "/" item-path " to create placeholder" ) ;; NEED TO THREADIFY THIS (let ((th (make-thread (lambda () (mutex-lock! registry-mutex) (hash-table-set! test-registry (runs:make-full-test-name test-name item-path) 'start) (mutex-unlock! registry-mutex) ;; If haven't done it before register a top level test if this is an itemized test (if (not (eq? (hash-table-ref/default test-registry (runs:make-full-test-name test-name "") #f) 'done)) (cdb:tests-register-test *runremote* run-id test-name "")) (cdb:tests-register-test *runremote* run-id test-name item-path) (mutex-lock! registry-mutex) (hash-table-set! test-registry (runs:make-full-test-name test-name item-path) 'done) (mutex-unlock! registry-mutex)) (conc test-name "/" item-path)))) (thread-start! th)) ;; TRY (thread-sleep! *global-delta*) (runs:shrink-can-run-more-tests-count) ;; DELAY TWEAKER (still needed?) (loop (car newtal)(cdr newtal) reruns)) ;; At this point *all* test registrations must be completed. ((not (null? (filter (lambda (x)(eq? 'start x))(hash-table-values test-registry)))) (debug:print-info 0 "Waiting on test registrations: " (string-intersperse (filter (lambda (x) (eq? (hash-table-ref/default test-registry x #f) 'start)) (hash-table-keys test-registry)) ", ")) (thread-sleep! 0.1) (loop hed tal reruns)) ((not have-resources) ;; simply try again after waiting a second (debug:print-info 1 "no resources to run new tests, waiting ...") ;; Have gone back and forth on this but db starvation is an issue. ;; wait one second before looking again to run jobs. (thread-sleep! 1) ;; (+ 2 *global-delta*)) ;; could have done hed tal here but doing car/cdr of newtal to rotate tests (loop (car newtal)(cdr newtal) reruns)) ((and have-resources (or (null? prereqs-not-met) (and (eq? testmode 'toplevel) (null? non-completed)))) (run:test run-id run-info key-vals runname keyvallst test-record flags #f) (hash-table-set! test-registry (runs:make-full-test-name test-name item-path) 'running) (runs:shrink-can-run-more-tests-count) ;; DELAY TWEAKER (still needed?) ;; (thread-sleep! *global-delta*) (if (not (null? tal)) (loop (car tal)(cdr tal) reruns))) (else ;; must be we have unmet prerequisites (debug:print 4 "FAILS: " fails) ;; If one or more of the prereqs-not-met are FAIL then we can issue ;; a message and drop hed from the items to be processed. (if (null? fails) (begin ;; couldn't run, take a breather (debug:print-info 4 "Shouldn't really get here, race condition? Unable to launch more tests at this moment, killing time ...") ;; (thread-sleep! (+ 0.01 *global-delta*)) ;; long sleep here - no resources, may as well be patient ;; we made new tal by sticking hed at the back of the list (loop (car newtal)(cdr newtal) reruns)) ;; the waiton is FAIL so no point in trying to run hed ever again (if (not (null? tal)) (if (vector? hed) (begin (debug:print 1 "WARN: Dropping test " (db:test-get-testname hed) "/" (db:test-get-item-path hed) " from the launch list as it has prerequistes that are FAIL") (runs:shrink-can-run-more-tests-count) ;; DELAY TWEAKER (still needed?) ;; (thread-sleep! *global-delta*) (hash-table-set! test-registry (runs:make-full-test-name test-name item-path) 'removed) (loop (car tal)(cdr tal) (cons hed reruns))) (begin (debug:print 1 "WARN: Test not processed correctly. Could be a race condition in your test implementation? " hed) ;; " as it has prerequistes that are FAIL. (NOTE: hed is not a vector)") (runs:shrink-can-run-more-tests-count) ;; DELAY TWEAKER (still needed?) ;; (thread-sleep! (+ 0.01 *global-delta*)) (loop hed tal reruns))))))))) ;; END OF INNER COND ;; case where an items came in as a list been processed ((and (list? items) ;; thus we know our items are already calculated (not itemdat)) ;; and not yet expanded into the list of things to be done (if (and (debug:debug-mode 1) ;; (>= *verbosity* 1) (> (length items) 0) (> (length (car items)) 0)) (pp items)) (for-each (lambda (my-itemdat) (let* ((new-test-record (let ((newrec (make-tests:testqueue))) (vector-copy! test-record newrec) newrec)) (my-item-path (item-list->path my-itemdat))) (if (tests:match test-patts hed my-item-path required: required-tests) ;; (patt-list-match my-item-path item-patts) ;; yes, we want to process this item, NOTE: Should not need this check here! (let ((newtestname (runs:make-full-test-name hed my-item-path))) ;; test names are unique on testname/item-path (tests:testqueue-set-items! new-test-record #f) (tests:testqueue-set-itemdat! new-test-record my-itemdat) (tests:testqueue-set-item_path! new-test-record my-item-path) (hash-table-set! test-records newtestname new-test-record) (set! tal (cons newtestname tal)))))) ;; since these are itemized create new test names testname/itempath items) (if (not (null? tal)) (begin (debug:print-info 4 "End of items list, looping with next after short delay") ;; (thread-sleep! (+ 0.01 *global-delta*)) (loop (car tal)(cdr tal) reruns)))) ;; if items is a proc then need to run items:get-items-from-config, get the list and loop ;; - but only do that if resources exist to kick off the job ((or (procedure? items)(eq? items 'have-procedure)) (let ((can-run-more (runs:can-run-more-tests test-record max-concurrent-jobs))) (if (and (list? can-run-more) (car can-run-more)) (let* ((prereqs-not-met (db:get-prereqs-not-met run-id waitons item-path mode: testmode)) (fails (runs:calc-fails prereqs-not-met)) (non-completed (runs:calc-not-completed prereqs-not-met))) (debug:print-info 8 "can-run-more: " can-run-more "\n testname: " hed "\n prereqs-not-met: " (runs:pretty-string prereqs-not-met) "\n non-completed: " (runs:pretty-string non-completed) "\n fails: " (runs:pretty-string fails) "\n testmode: " testmode "\n num-retries: " num-retries "\n (eq? testmode 'toplevel): " (eq? testmode 'toplevel) "\n (null? non-completed): " (null? non-completed) "\n reruns: " reruns "\n items: " items "\n can-run-more: " can-run-more) ;; (thread-sleep! (+ 0.01 *global-delta*)) (cond ;; INNER COND #2 ((or (null? prereqs-not-met) ;; all prereqs met, fire off the test ;; or, if it is a 'toplevel test and all prereqs not met are COMPLETED then launch (and (eq? testmode 'toplevel) (null? non-completed))) (let ((test-name (tests:testqueue-get-testname test-record))) (setenv "MT_TEST_NAME" test-name) ;; (setenv "MT_RUNNAME" runname) (set-megatest-env-vars run-id inrunname: runname) ;; these may be needed by the launching process (let ((items-list (items:get-items-from-config tconfig))) (if (list? items-list) (begin (tests:testqueue-set-items! test-record items-list) ;; (thread-sleep! *global-delta*) (loop hed tal reruns)) (begin (debug:print 0 "ERROR: The proc from reading the setup did not yield a list - please report this") (exit 1)))))) ((null? fails) (debug:print-info 4 "fails is null, moving on in the queue but keeping " hed " for now") ;; only increment num-retries when there are no tests runing (if (eq? 0 (list-ref can-run-more 1)) (begin ;; TRY (if (> num-retries 100) ;; first 100 retries are low time cost ;; TRY (thread-sleep! (+ 2 *global-delta*)) ;; TRY (thread-sleep! (+ 0.01 *global-delta*))) (set! num-retries (+ num-retries 1)))) (if (> num-retries max-retries) (if (not (null? tal)) (loop (car tal)(cdr tal) reruns)) (loop (car newtal)(cdr newtal) reruns))) ;; an issue with prereqs not yet met? ((and (not (null? fails))(eq? testmode 'normal)) (debug:print-info 1 "test " hed " (mode=" testmode ") has failed prerequisite(s); " (string-intersperse (map (lambda (t)(conc (db:test-get-testname t) ":" (db:test-get-state t)"/"(db:test-get-status t))) fails) ", ") ", removing it from to-do list") (if (not (null? tal)) (begin ;; (thread-sleep! *global-delta*) (loop (car tal)(cdr tal)(cons hed reruns))))) (else (debug:print 8 "ERROR: No handler for this condition.") ;; TRY (thread-sleep! (+ 1 *global-delta*)) (loop (car newtal)(cdr newtal) reruns)))) ;; END OF IF CAN RUN MORE ;; if can't run more just loop with next possible test (begin (debug:print-info 4 "processing the case with a lambda for items or 'have-procedure. Moving through the queue without dropping " hed) ;; (thread-sleep! (+ 2 *global-delta*)) (loop (car newtal)(cdr newtal) reruns))))) ;; END OF (or (procedure? items)(eq? items 'have-procedure)) ;; this case should not happen, added to help catch any bugs ((and (list? items) itemdat) (debug:print 0 "ERROR: Should not have a list of items in a test and the itemspath set - please report this") (exit 1)) ((not (null? reruns)) (let* ((newlst (tests:filter-non-runnable run-id tal test-records)) ;; i.e. not FAIL, WAIVED, INCOMPLETE, PASS, KILLED, (junked (lset-difference equal? tal newlst))) (debug:print-info 4 "full drop through, if reruns is less than 100 we will force retry them, reruns=" reruns ", tal=" tal) (if (< num-retries max-retries) (set! newlst (append reruns newlst))) (set! num-retries (+ num-retries 1)) ;; (thread-sleep! (+ 1 *global-delta*)) (if (not (null? newlst)) ;; since reruns have been tacked on to newlst create new reruns from junked (loop (car newlst)(cdr newlst)(delete-duplicates junked))))) ((not (null? tal)) (debug:print-info 4 "I'm pretty sure I shouldn't get here.")) (else (debug:print-info 4 "Exiting loop with...\n hed=" hed "\n tal=" tal "\n reruns=" reruns)) )))) ;; LET* ((test-record ;; we get here on "drop through" - loop for next test in queue ;; FIXME!!!! THIS SHOULD NOT REQUIRE AN EXIT!!!!!!! (debug:print-info 1 "All tests launched") (thread-sleep! 0.5) ;; FIXME! This harsh exit should not be necessary.... ;; (if (not *runremote*)(exit)) ;; #f)) ;; return a #f as a hint that we are done ;; Here we need to check that all the tests remaining to be run are eligible to run ;; and are not blocked by failed
;; test-records is a hash table testname:item_path => vector < testname testconfig waitons priority items-info ... >(define (runs:run-tests-queue-new run-id runname test-records keyvallst flags test-patts required-tests reglen) ;; At this point the list of parent tests is expanded ;; NB// Should expand items here and then insert into the run queue. (debug:print 5 "test-records: " test-records ", keyvallst: " keyvallst " flags: " (hash-table->alist flags)) (let ((run-info (cdb:remote-run db:get-run-info #f run-id)) (key-vals (cdb:remote-run db:get-key-vals #f run-id)) (sorted-test-names (tests:sort-by-priority-and-waiton test-records)) (test-registry (make-hash-table)) (registry-mutex (make-mutex)) (num-retries 0) (max-retries (config-lookup *configdat* "setup" "maxretries")) (max-concurrent-jobs (let ((mcj (config-lookup *configdat* "setup" "max_concurrent_jobs"))) (if (and mcj (string->number mcj)) (string->number mcj) 1)))) ;; length of the register queue ahead (set! max-retries (if (and max-retries (string->number max-retries))(string->number max-retries) 100)) (if (not (null? sorted-test-names)) (let loop ((hed (car sorted-test-names)) (tal (cdr sorted-test-names)) (reg '()) ;; registered, put these at the head of tal (reruns '())) (if (not (null? reruns))(debug:print-info 4 "reruns=" reruns)) ;; (print "Top of loop, hed=" hed ", tal=" tal " ,reruns=" reruns) (let* ((test-record (hash-table-ref test-records hed)) (test-name (tests:testqueue-get-testname test-record)) (tconfig (tests:testqueue-get-testconfig test-record)) (testmode (let ((m (config-lookup tconfig "requirements" "mode"))) (if m (string->symbol m) 'normal))) (waitons (tests:testqueue-get-waitons test-record)) (priority (tests:testqueue-get-priority test-record)) (itemdat (tests:testqueue-get-itemdat test-record)) ;; itemdat can be a string, list or #f (items (tests:testqueue-get-items test-record)) (item-path (item-list->path itemdat)) (newtal (append tal (list hed))) (regfull (> (length reg) reglen))) ;; (if (> (length reg) 10) ;; (begin ;; (set! tal (cons hed tal)) ;; (set! hed (car reg)) ;; (set! reg (cdr reg)) ;; (set! newtal tal))) (debug:print 6 "test-name: " test-name "\n hed: " hed "\n itemdat: " itemdat "\n items: " items "\n item-path: " item-path "\n waitons: " waitons "\n num-retries: " num-retries "\n tal: " tal "\n reruns: " reruns) ;; check for hed in waitons => this would be circular, remove it and issue an ;; error (if (member test-name waitons) (begin (debug:print 0 "ERROR: test " test-name " has listed itself as a waiton, please correct this!") (set! waiton (filter (lambda (x)(not (equal? x hed))) waitons)))) (cond ;; OUTER COND ((not items) ;; when false the test is ok to be handed off to launch (but not before) (if (and (not (tests:match test-patts (tests:testqueue-get-testname test-record) item-path)) (not (null? tal))) (loop (car tal)(cdr tal) reg reruns)) (let* ((run-limits-info (runs:can-run-more-tests test-record max-concurrent-jobs)) ;; look at the test jobgroup and tot jobs running (have-resources (car run-limits-info)) (num-running (list-ref run-limits-info 1)) (num-running-in-jobgroup (list-ref run-limits-info 2)) (max-concurrent-jobs (list-ref run-limits-info 3)) (job-group-limit (list-ref run-limits-info 4)) (prereqs-not-met (db:get-prereqs-not-met run-id waitons item-path mode: testmode)) (fails (runs:calc-fails prereqs-not-met)) (non-completed (runs:calc-not-completed prereqs-not-met))) (debug:print-info 8 "have-resources: " have-resources " prereqs-not-met: " (string-intersperse (map (lambda (t) (if (vector? t) (conc (db:test-get-state t) "/" (db:test-get-status t)) (conc " WARNING: t is not a vector=" t ))) prereqs-not-met) ", ") " fails: " fails) (debug:print-info 4 "hed=" hed "\n test-record=" test-record "\n test-name: " test-name "\n item-path: " item-path "\n test-patts: " test-patts) ;; Don't know at this time if the test have been launched at some time in the past ;; i.e. is this a re-launch? (debug:print-info 4 "run-limits-info = " run-limits-info) (cond ;; INNER COND #1 for a launchable test ;; Check item path against item-patts ((not (tests:match test-patts (tests:testqueue-get-testname test-record) item-path)) ;; This test/itempath is not to be run ;; else the run is stuck, temporarily or permanently ;; but should check if it is due to lack of resources vs. prerequisites (debug:print-info 1 "Skipping " (tests:testqueue-get-testname test-record) " " item-path " as it doesn't match " test-patts) ;; (thread-sleep! *global-delta*) (if (not (null? tal)) (loop (runs:queue-next-hed tal reg reglen regfull) (runs:queue-next-tal tal reg reglen regfull) (runs:queue-next-reg tal reg reglen regfull) reruns))) ;; Registry has been started for this test but has not yet completed ;; this should be rare, the case where there are only a couple of tests and the db is slow ;; delay a short while and continue ;; ((eq? (hash-table-ref/default test-registry (runs:make-full-test-name test-name item-path) #f) 'start) ;; (thread-sleep! 0.01) ;; (loop (car newtal)(cdr newtal) reruns)) ;; count number of 'done, if more than 100 then skip on through. ((not (hash-table-ref/default test-registry (runs:make-full-test-name test-name item-path) #f)) ;; ) ;; too many changes required. Implement later. (debug:print-info 4 "Pre-registering test " test-name "/" item-path " to create placeholder" ) (let ((th (make-thread (lambda () (mutex-lock! registry-mutex) (hash-table-set! test-registry (runs:make-full-test-name test-name item-path) 'start) (mutex-unlock! registry-mutex) ;; If haven't done it before register a top level test if this is an itemized test (if (not (eq? (hash-table-ref/default test-registry (runs:make-full-test-name test-name "") #f) 'done)) (cdb:tests-register-test *runremote* run-id test-name "")) (cdb:tests-register-test *runremote* run-id test-name item-path) (mutex-lock! registry-mutex) (hash-table-set! test-registry (runs:make-full-test-name test-name item-path) 'done) (mutex-unlock! registry-mutex)) (conc test-name "/" item-path)))) (thread-start! th)) (runs:shrink-can-run-more-tests-count) ;; DELAY TWEAKER (still needed?) (if (and (null? tal)(null? reg)) (loop hed tal reg reruns) (loop (runs:queue-next-hed tal reg reglen regfull) (runs:queue-next-tal tal reg reglen regfull) (let ((newl (append reg (list hed)))) (if regfull (cdr newl) newl)) reruns))) ;; At this point hed test registration must be completed. ((eq? (hash-table-ref/default test-registry (runs:make-full-test-name test-name item-path) #f) 'start) (debug:print-info 0 "Waiting on test registration(s): " (string-intersperse (filter (lambda (x) (eq? (hash-table-ref/default test-registry x #f) 'start)) (hash-table-keys test-registry)) ", ")) (thread-sleep! 0.1) (loop hed tal reg reruns)) ((not have-resources) ;; simply try again after waiting a second (debug:print-info 1 "no resources to run new tests, waiting ...") ;; Have gone back and forth on this but db starvation is an issue. ;; wait one second before looking again to run jobs. (thread-sleep! 1) ;; (+ 2 *global-delta*)) ;; could have done hed tal here but doing car/cdr of newtal to rotate tests (loop (car newtal)(cdr newtal) reg reruns)) ((and have-resources (or (null? prereqs-not-met) (and (eq? testmode 'toplevel) (null? non-completed)))) (run:test run-id run-info key-vals runname keyvallst test-record flags #f) (hash-table-set! test-registry (runs:make-full-test-name test-name item-path) 'running) (runs:shrink-can-run-more-tests-count) ;; DELAY TWEAKER (still needed?) ;; (thread-sleep! *global-delta*) (if (not (null? tal)) (loop (runs:queue-next-hed tal reg reglen regfull) (runs:queue-next-tal tal reg reglen regfull) (runs:queue-next-reg tal reg reglen regfull) reruns))) (else ;; must be we have unmet prerequisites (debug:print 4 "FAILS: " fails) ;; If one or more of the prereqs-not-met are FAIL then we can issue ;; a message and drop hed from the items to be processed. (if (null? fails) (begin ;; couldn't run, take a breather (debug:print-info 4 "Shouldn't really get here, race condition? Unable to launch more tests at this moment, killing time ...") ;; (thread-sleep! (+ 0.01 *global-delta*)) ;; long sleep here - no resources, may as well be patient ;; we made new tal by sticking hed at the back of the list (loop (car newtal)(cdr newtal) reg reruns)) ;; the waiton is FAIL so no point in trying to run hed ever again (if (not (null? tal)) (if (vector? hed) (begin (debug:print 1 "WARN: Dropping test " (db:test-get-testname hed) "/" (db:test-get-item-path hed) " from the launch list as it has prerequistes that are FAIL") (runs:shrink-can-run-more-tests-count) ;; DELAY TWEAKER (still needed?) ;; (thread-sleep! *global-delta*) (hash-table-set! test-registry (runs:make-full-test-name test-name item-path) 'removed) (loop (runs:queue-next-hed tal reg reglen regfull) (runs:queue-next-tal tal reg reglen regfull) (runs:queue-next-reg tal reg reglen regfull) (cons hed reruns))) (begin (debug:print 1 "WARN: Test not processed correctly. Could be a race condition in your test implementation? " hed) ;; " as it has prerequistes that are FAIL. (NOTE: hed is not a vector)") (runs:shrink-can-run-more-tests-count) ;; DELAY TWEAKER (still needed?) ;; (thread-sleep! (+ 0.01 *global-delta*)) (loop hed tal reg reruns))))))))) ;; END OF INNER COND ;; case where an items came in as a list been processed ((and (list? items) ;; thus we know our items are already calculated (not itemdat)) ;; and not yet expanded into the list of things to be done (if (and (debug:debug-mode 1) ;; (>= *verbosity* 1) (> (length items) 0) (> (length (car items)) 0)) (pp items)) (for-each (lambda (my-itemdat) (let* ((new-test-record (let ((newrec (make-tests:testqueue))) (vector-copy! test-record newrec) newrec)) (my-item-path (item-list->path my-itemdat))) (if (tests:match test-patts hed my-item-path) ;; (patt-list-match my-item-path item-patts) ;; yes, we want to process this item, NOTE: Should not need this check here! (let ((newtestname (runs:make-full-test-name hed my-item-path))) ;; test names are unique on testname/item-path (tests:testqueue-set-items! new-test-record #f) (tests:testqueue-set-itemdat! new-test-record my-itemdat) (tests:testqueue-set-item_path! new-test-record my-item-path) (hash-table-set! test-records newtestname new-test-record) (set! tal (cons newtestname tal)))))) ;; since these are itemized create new test names testname/itempath items) (if (not (null? tal)) (begin (debug:print-info 4 "End of items list, looping with next after short delay") ;; (thread-sleep! (+ 0.01 *global-delta*)) (loop (runs:queue-next-hed tal reg reglen regfull) (runs:queue-next-tal tal reg reglen regfull) (runs:queue-next-reg tal reg reglen regfull) reruns)))) ;; if items is a proc then need to run items:get-items-from-config, get the list and loop ;; - but only do that if resources exist to kick off the job ((or (procedure? items)(eq? items 'have-procedure)) (let ((can-run-more (runs:can-run-more-tests test-record max-concurrent-jobs))) (if (and (list? can-run-more) (car can-run-more)) (let* ((prereqs-not-met (db:get-prereqs-not-met run-id waitons item-path mode: testmode)) (fails (runs:calc-fails prereqs-not-met)) (non-completed (runs:calc-not-completed prereqs-not-met))) (debug:print-info 8 "can-run-more: " can-run-more "\n testname: " hed "\n prereqs-not-met: " (runs:pretty-string prereqs-not-met) "\n non-completed: " (runs:pretty-string non-completed) "\n fails: " (runs:pretty-string fails) "\n testmode: " testmode "\n num-retries: " num-retries "\n (eq? testmode 'toplevel): " (eq? testmode 'toplevel) "\n (null? non-completed): " (null? non-completed) "\n reruns: " reruns "\n items: " items "\n can-run-more: " can-run-more) ;; (thread-sleep! (+ 0.01 *global-delta*)) (cond ;; INNER COND #2 ((or (null? prereqs-not-met) ;; all prereqs met, fire off the test ;; or, if it is a 'toplevel test and all prereqs not met are COMPLETED then launch (and (eq? testmode 'toplevel) (null? non-completed))) (let ((test-name (tests:testqueue-get-testname test-record))) (setenv "MT_TEST_NAME" test-name) ;; (setenv "MT_RUNNAME" runname) (set-megatest-env-vars run-id) ;; these may be needed by the launching process (let ((items-list (items:get-items-from-config tconfig))) (if (list? items-list) (begin (tests:testqueue-set-items! test-record items-list) ;; (thread-sleep! *global-delta*) (loop hed tal reg reruns)) (begin (debug:print 0 "ERROR: The proc from reading the setup did not yield a list - please report this") (exit 1)))))) ((null? fails) (debug:print-info 4 "fails is null, moving on in the queue but keeping " hed " for now") ;; only increment num-retries when there are no tests runing (if (eq? 0 (list-ref can-run-more 1)) (begin ;; TRY (if (> num-retries 100) ;; first 100 retries are low time cost ;; TRY (thread-sleep! (+ 2 *global-delta*)) ;; TRY (thread-sleep! (+ 0.01 *global-delta*))) (set! num-retries (+ num-retries 1)))) (if (> num-retries max-retries) (if (not (null? tal)) (loop (runs:queue-next-hed tal reg reglen regfull) (runs:queue-next-tal tal reg reglen regfull) (runs:queue-next-reg tal reg reglen regfull) reruns)) (loop (car newtal)(cdr newtal) reg reruns))) ;; an issue with prereqs not yet met? ((and (not (null? fails))(eq? testmode 'normal)) (debug:print-info 1 "test " hed " (mode=" testmode ") has failed prerequisite(s); " (string-intersperse (map (lambda (t)(conc (db:test-get-testname t) ":" (db:test-get-state t)"/"(db:test-get-status t))) fails) ", ") ", removing it from to-do list") (if (not (null? tal)) (begin ;; (thread-sleep! *global-delta*) (loop (runs:queue-next-hed tal reg reglen regfull) (runs:queue-next-tal tal reg reglen regfull) (runs:queue-next-reg tal reg reglen regfull) (cons hed reruns))))) (else (debug:print 8 "ERROR: No handler for this condition.") ;; TRY (thread-sleep! (+ 1 *global-delta*)) (loop (car newtal)(cdr newtal) reg reruns)))) ;; END OF IF CAN RUN MORE ;; if can't run more just loop with next possible test (begin (debug:print-info 4 "processing the case with a lambda for items or 'have-procedure. Moving through the queue without dropping " hed) ;; (thread-sleep! (+ 2 *global-delta*)) (loop (car newtal)(cdr newtal) reg reruns))))) ;; END OF (or (procedure? items)(eq? items 'have-procedure)) ;; this case should not happen, added to help catch any bugs ((and (list? items) itemdat) (debug:print 0 "ERROR: Should not have a list of items in a test and the itemspath set - please report this") (exit 1)) ((not (null? reruns)) (let* ((newlst (tests:filter-non-runnable run-id tal test-records)) ;; i.e. not FAIL, WAIVED, INCOMPLETE, PASS, KILLED, (junked (lset-difference equal? tal newlst))) (debug:print-info 4 "full drop through, if reruns is less than 100 we will force retry them, reruns=" reruns ", tal=" tal) (if (< num-retries max-retries) (set! newlst (append reruns newlst))) (set! num-retries (+ num-retries 1)) ;; (thread-sleep! (+ 1 *global-delta*)) (if (not (null? newlst)) ;; since reruns have been tacked on to newlst create new reruns from junked (loop (car newlst)(cdr newlst) reg (delete-duplicates junked))))) ((not (null? tal)) (debug:print-info 4 "I'm pretty sure I shouldn't get here.")) ((not (null? reg)) ;; could we get here with leftovers? (debug:print-info 0 "Have leftovers!") (loop (car reg)(cdr reg) '() reruns)) (else (debug:print-info 4 "Exiting loop with...\n hed=" hed "\n tal=" tal "\n reruns=" reruns)) )))) ;; LET* ((test-record ;; we get here on "drop through" - loop for next test in queue ;; FIXME!!!! THIS SHOULD NOT REQUIRE AN EXIT!!!!!!! (debug:print-info 1 "All tests launched") (thread-sleep! 0.5) ;; FIXME! This harsh exit should not be necessary.... ;; (if (not *runremote*)(exit)) ;; #f)) ;; return a #f as a hint that we are done;; Here we need to check that all the tests remaining to be run are eligible to run;; and are not blocked by failed
;; Copyright 2006-2012, Matthew Welland.
;; Copyright 2006-2013, Matthew Welland.
;;
;; This program is made available under the GNU GPL version 2.0 or
;; greater. See the accompanying file COPYING for details.
;;
;; This program is distributed WITHOUT ANY WARRANTY; without even the
;; implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
;; PURPOSE.
;; strftime('%m/%d/%Y %H:%M:%S','now','localtime')
(use sqlite3 srfi-1 posix regex regex-case srfi-69 dot-locking (srfi 18) posix-extras directory-utils)
(import (prefix sqlite3 sqlite3:))
(declare (unit runs))
(declare (uses db))
(declare (uses common))
(declare (uses items))
(declare (uses runconfig))
(declare (uses tests))
(declare (uses server))
(declare (uses mt))
(include "common_records.scm")
(include "key_records.scm")
(include "db_records.scm")
(include "run_records.scm")
(include "test_records.scm")
;; runs:get-runs-by-patt;; get runs by list of criteria;; register a test run with the db;;;; Use: (db-get-value-by-header (db:get-header runinfo)(db:get-row runinfo));; to extract info from the structure returned;;(define (runs:get-runs-by-patt db keys runnamepatt) ;; test-name) (let* ((keyvallst (keys->vallist keys)) (tmp (runs:get-std-run-fields keys '("id" "runname" "state" "status" "owner" "event_time"))) (keystr (car tmp)) (header (cadr tmp)) (res '()) (key-patt "") (runwildtype (if (substring-index "%" runnamepatt) "like" "glob")) (qry-str #f)) (for-each (lambda (keyval) (let* ((key (vector-ref keyval 0)) (fulkey (conc ":" key)) (patt (args:get-arg fulkey)) (wildtype (if (substring-index "%" patt) "like" "glob"))) (if patt (set! key-patt (conc key-patt " AND " key " " wildtype " '" patt "'")) (begin (debug:print 0 "ERROR: searching for runs with no pattern set for " fulkey) (exit 6))))) keys) (set! qry-str (conc "SELECT " keystr " FROM runs WHERE runname " runwildtype " ? " key-patt ";")) (debug:print-info 4 "runs:get-runs-by-patt qry=" qry-str " " runnamepatt) (sqlite3:for-each-row (lambda (a . r) (set! res (cons (list->vector (cons a r)) res))) db qry-str runnamepatt) (vector header res)))(define (runs:test-get-full-path test)
(let* ((testname (db:test-get-testname test))
(itempath (db:test-get-item-path test)))
(conc testname (if (equal? itempath "") "" (conc "(" itempath ")")))))
;; This is the *new* methodology. One record to inform them and in the chaos, organise them.
;;
(define (runs:create-run-record)
(let* ((mconfig (if *configdat*
*configdat*
(if (setup-for-run)
*configdat*
(begin
(debug:print 0 "ERROR: Called setup in a non-megatest area, exiting")
(exit 1)))))
(runrec (runs:runrec-make-record))
(target (common:args-get-target))
(runname (or (args:get-arg "-runname")
(args:get-arg ":runname")))
(testpatt (or (args:get-arg "-testpatt")
(args:get-arg "-runtests")))
(keys (keys:config-get-fields mconfig))
(keyvals (keys:target->keyval keys target))
(toppath *toppath*)
(envdat keyvals) ;; initial values start with keyvals
(runconfig #f)
(serverdat (if (args:get-arg "-server")
*runremote*
#f)) ;; to be used later
(transport (or (args:get-arg "-transport") 'http))
(db (if (and mconfig
(or (args:get-arg "-server")
(eq? transport 'fs)))
(open-db)
#f))
(run-id #f))
;; Set all the environment vars we know so far, start with keys
(for-each (lambda (keyval)
(setenv (car keyval)(cadr keyval)))
keyvals)
;; Set up various and sundry known vars here
(setenv "MT_RUN_AREA_HOME" toppath)
(setenv "MT_RUNNAME" runname)
(setenv "MT_TARGET" target)
(set! envdat (append
envdat
(list (list "MT_RUN_AREA_HOME" toppath)
(list "MT_RUNNAME" runname)
(list "MT_TARGET" target))))
;; Now can read the runconfigs file
;;
(set! runconfig (read-config (conc *toppath* "/runconfigs.config") #f #t sections: (list "default" target)))
(if (not (hash-table-ref/default runconfig (args:get-arg "-reqtarg") #f))
(begin
(debug:print 0 "ERROR: [" (args:get-arg "-reqtarg") "] not found in " runconfigf)
(if db (sqlite3:finalize! db))
(exit 1)))
;; Now have runconfigs data loaded, set environment vars
(for-each (lambda (section)
(for-each (lambda (varval)
(set! envdat (append envdat (list varval)))
(setenv (car varval)(cadr varval)))
(configf:get-section runconfig section)))
(list "default" target))
(vector target runname testpatt keys keyvals envdat mconfig runconfig serverdat transport db toppath run-id)))
(define (set-megatest-env-vars run-id #!key (inkeys #f)(inrunname #f))
(let ((keys (if inkeys inkeys (cdb:remote-run db:get-keys #f)))
(vals (hash-table-ref/default *env-vars-by-run-id* run-id #f)))
(define (set-megatest-env-vars run-id #!key (inkeys #f)(inrunname #f)(inkeyvals #f))
(let* ((target (or (common:args-get-target)
(get-environment-variable "MT_TARGET")))
(keys (if inkeys inkeys (cdb:remote-run db:get-keys #f)))
(keyvals (if inkeyvals inkeyvals (keys:target->keyval keys target)))
(vals (hash-table-ref/default *env-vars-by-run-id* run-id #f))
(link-tree (configf:lookup *configdat* "setup" "linktree")))
;; get the info from the db and put it in the cache
(if link-tree
(setenv "MT_LINKTREE" link-tree)
(debug:print 0 "ERROR: linktree not set, should be set in megatest.config in [setup] section."))
(if (not vals)
(let ((ht (make-hash-table)))
(hash-table-set! *env-vars-by-run-id* run-id ht)
(set! vals ht)
(for-each
(lambda (key)
(hash-table-set! vals key (cdb:remote-run db:get-run-key-val #f run-id key)))
keys)))
(hash-table-set! vals (car key) (cadr key))) ;; (cdb:remote-run db:get-run-key-val #f run-id (car key))))
keyvals)))
;; from the cached data set the vars
(hash-table-for-each
vals
(lambda (key val)
(debug:print 2 "setenv " (key:get-fieldname key) " " val)
(setenv (key:get-fieldname key) val)))
(debug:print 2 "setenv " key " " val)
(if (and (string? key)
(string? val))
(setenv key val)
(debug:print 0 "ERROR: Malformed environment variable definition: var=" var ", val=" val))))
(if (not (get-environment-variable "MT_TARGET"))(setenv "MT_TARGET" target))
(alist->env-vars (hash-table-ref/default *configdat* "env-override" '()))
;; Lets use this as an opportunity to put MT_RUNNAME in the environment
(setenv "MT_RUNNAME" (if inrunname inrunname (cdb:remote-run db:get-run-name-from-id #f run-id)))
(setenv "MT_RUN_AREA_HOME" *toppath*)
(setenv "MT_RUN_AREA_HOME" *toppath*)))
))
(define (set-item-env-vars itemdat)
(for-each (lambda (item)
(debug:print 2 "setenv " (car item) " " (cadr item))
(setenv (car item) (cadr item)))
itemdat))
(define *last-num-running-tests* 0);; Every time can-run-more-tests is called increment the delay
;; if the cou;;
;; NOTE: We run this server-side!! Do not use this global except in the runs:can-run-more-tests routine
;;
(define *last-num-running-tests* 0)
(define *runs:can-run-more-tests-count* 0)
(define (runs:shrink-can-run-more-tests-count)
(define (runs:shrink-can-run-more-tests-count) ;; the db is a dummy var so we can use cdb:remote-run
(set! *runs:can-run-more-tests-count* 0)) ;; (/ *runs:can-run-more-tests-count* 2)))
;; Temporary globals. Move these into the logic or into common
;;
(define *seen-cant-run-tests* (make-hash-table)) ;; use to track tests that we suspect cannot be run
(define (runs:inc-cant-run-tests testname)
(hash-table-set! *seen-cant-run-tests* testname
(+ (hash-table-ref/default *seen-cant-run-tests* testname 0) 1)))
(define (runs:can-keep-running? testname n)
(< (hash-table-ref/default *seen-cant-run-tests* testname 0) n))
(define *runs:denoise* (make-hash-table)) ;; key => last-time-ran
(define (runs:lownoise key waitval)
(let ((lasttime (hash-table-ref/default *runs:denoise* key 0))
(currtime (current-seconds)))
(if (> (- currtime lasttime) waitval)
(begin
(hash-table-set! *runs:denoise* key currtime)
#t)
#f)))
(define (runs:can-run-more-tests test-record max-concurrent-jobs)
(define (runs:can-run-more-tests jobgroup max-concurrent-jobs)
(thread-sleep! (cond
((> *runs:can-run-more-tests-count* 20) 2);; obviously haven't had any work to do for a while
(else 0)))
(let* ((tconfig (tests:testqueue-get-testconfig test-record)) (jobgroup (config-lookup tconfig "requirements" "jobgroup"))(num-running (cdb:remote-run db:get-count-tests-running #f))
(let* ((num-running (cdb:remote-run db:get-count-tests-running #f))
(num-running-in-jobgroup (cdb:remote-run db:get-count-tests-running-in-jobgroup #f jobgroup))
(job-group-limit (config-lookup *configdat* "jobgroups" jobgroup)))
(job-group-limit (let ((jobg-count (config-lookup *configdat* "jobgroups" jobgroup)))
(if (string? jobg-count)
(string->number jobg-count)
jobg-count))))
(if (> (+ num-running num-running-in-jobgroup) 0)
(set! *runs:can-run-more-tests-count* (+ *runs:can-run-more-tests-count* 1)))
(if (not (eq? *last-num-running-tests* num-running))
(begin
(debug:print 2 "max-concurrent-jobs: " max-concurrent-jobs ", num-running: " num-running)
(set! *last-num-running-tests* num-running)))
(if (not (eq? 0 *globalexitstatus*))
(list #f num-running num-running-in-jobgroup max-concurrent-jobs job-group-limit)
(let ((can-not-run-more (cond
;; if max-concurrent-jobs is set and the number running is greater
;; than it than cannot run more jobs
((and max-concurrent-jobs (>= num-running max-concurrent-jobs))
(if (runs:lownoise "mcj msg" 60)
(debug:print 0 "WARNING: Max running jobs exceeded, current number running: " num-running
", max_concurrent_jobs: " max-concurrent-jobs)
(debug:print 0 "WARNING: Max running jobs exceeded, current number running: " num-running
", max_concurrent_jobs: " max-concurrent-jobs))
#t)
;; if job-group-limit is set and number of jobs in the group is greater
;; than the limit then cannot run more jobs of this kind
((and job-group-limit
(>= num-running-in-jobgroup job-group-limit))
(if (runs:lownoise (conc "maxjobgroup " jobgroup) 60)
(debug:print 1 "WARNING: number of jobs " num-running-in-jobgroup
" in " jobgroup " exceeded, will not run " (tests:testqueue-get-testname test-record))
(debug:print 1 "WARNING: number of jobs " num-running-in-jobgroup
" in jobgroup \"" jobgroup "\" exceeds limit of " job-group-limit))
#t)
(else #f))))
(list (not can-not-run-more) num-running num-running-in-jobgroup max-concurrent-jobs job-group-limit)))))
;;======================================================================;; New methodology. These routines will replace the above in time. For;; now the code is duplicated. This stuff is initially used in the monitor;; based code.;;======================================================================;; This is a duplicate of run-tests (which has been deprecated). Use this one instead of run tests.;; keyvals.;;;; test-names: Comma separated patterns same as test-patts but used in selection
;; of tests to run. The item portions are not respected.
;; FIXME: error out if /patt specified
;;
(define (runs:run-tests target runname test-names test-patts user flags)
(define (runs:run-tests target runname test-patts user flags) ;; test-names
(common:clear-caches) ;; clear all caches
(let* ((db #f)(keys (cdb:remote-run db:get-keys #f))
(keyvallst (keys:target->keyval keys target))
(run-id (cdb:remote-run db:register-run #f keys keyvallst runname "new" "n/a" user)) ;; test-name)))
(let* ((keys (keys:config-get-fields *configdat*))
(keyvals (keys:target->keyval keys target))
(run-id (cdb:remote-run db:register-run #f keyvals runname "new" "n/a" user)) ;; test-name)))
(keyvals (if run-id (cdb:remote-run db:get-key-vals #f run-id) #f))
(deferred '()) ;; delay running these since they have a waiton clause
(deferred '()) ;; delay running these since they have a waiton clause
;; keepgoing is the defacto modality now, will add hit-n-run a bit later ;; (keepgoing (hash-table-ref/default flags "-keepgoing" #f))
(runconfigf (conc *toppath* "/runconfigs.config"))
(runconfigf (conc *toppath* "/runconfigs.config"))
(required-tests '())
(test-records (make-hash-table))
(all-test-names (tests:get-valid-tests *toppath* "%"))) ;; we need a list of all valid tests to check waiton names)
(test-records (make-hash-table))
(all-tests-registry (tests:get-all)) ;; (tests:get-valid-tests (make-hash-table) test-search-path)) ;; all valid tests to check waiton names
(all-test-names (hash-table-keys all-tests-registry))
(test-names (tests:filter-test-names all-test-names test-patts))
(required-tests (lset-intersection equal? (string-split test-patts ",") test-names))) ;; test-names)) ;; Added test-names as initial for required-tests but that failed to work
;; Update the synchronous setting in the db based on the default or what is set by the user
;; This is done once here on a call to run tests rather than on every call to open-db
(set-megatest-env-vars run-id inkeys: keys) ;; these may be needed by the launching process (cdb:remote-run db:set-sync #f)
(set-megatest-env-vars run-id inkeys: keys inrunname: runname) ;; these may be needed by the launching process
(if (file-exists? runconfigf)
(setup-env-defaults runconfigf run-id *already-seen-runconfig-info* keys keyvals "pre-launch-env-vars")
(setup-env-defaults runconfigf run-id *already-seen-runconfig-info* keyvals "pre-launch-env-vars")
(debug:print 0 "WARNING: You do not have a run config file: " runconfigf))
;; look up all tests matching the comma separated list of globs in
;; test-patts (using % as wildcard)
(set! test-names (tests:get-valid-tests *toppath* test-names))
(set! test-names (delete-duplicates test-names))
;; (set! test-names (delete-duplicates (tests:get-valid-tests *toppath* test-patts)))
(debug:print-info 0 "test names " test-names)
;; on the first pass or call to run-tests set FAILS to NOT_STARTED if
;; -keepgoing is specified
(if (eq? *passnum* 0)
(begin
;; have to delete test records where NOT_STARTED since they can cause -keepgoing to
;; get stuck due to becoming inaccessible from a failed test. I.e. if test B depends
;; on test A but test B reached the point on being registered as NOT_STARTED and test
;; A failed for some reason then on re-run using -keepgoing the run can never complete.
(cdb:delete-tests-in-state *runremote* run-id "NOT_STARTED")
(cdb:remote-run db:set-tests-state-status #f run-id test-names #f "FAIL" "NOT_STARTED" "FAIL")))
;; from here on out the db will be opened and closed on every call runs:run-tests-queue
;; (sqlite3:finalize! db) ;; Ensure all tests are registered in the test_meta table
(runs:update-all-test_meta #f)
;; now add non-directly referenced dependencies (i.e. waiton)
;;======================================================================
;; refactoring this block into tests:get-full-data
;;
;; What happended, this code is now duplicated in tests!?
;;
;;======================================================================
(if (not (null? test-names))
(let loop ((hed (car test-names))
(tal (cdr test-names))) ;; 'return-procs tells the config reader to prep running system but return a proc
(debug:print-info 4 "hed=" hed " at top of loop")
(let* ((config (tests:get-testconfig hed 'return-procs))
(change-directory *toppath*) ;; PLEASE OPTIMIZE ME!!! I think this should be a no-op but there are several places where change-directories could be happening.
(setenv "MT_TEST_NAME" hed) ;;
(let* ((config (tests:get-testconfig hed all-tests-registry 'return-procs))
(waitons (let ((instr (if config
(config-lookup config "requirements" "waiton")
(begin ;; No config means this is a non-existant test
(debug:print 0 "ERROR: non-existent required test \"" hed "\"")
(if db (sqlite3:finalize! db)) (exit 1)))))
(debug:print-info 8 "waitons string is " instr)
(let ((newwaitons
(string-split (cond
((procedure? instr)
(let ((res (instr)))
(debug:print-info 8 "waiton procedure results in string " res " for test " hed)
res))
((string? instr) instr)
(else
;; NOTE: This is actually the case of *no* waitons! ;; (debug:print 0 "ERROR: something went wrong in processing waitons for test " hed)
"")))))
(filter (lambda (x)
(if (member x all-test-names)
(if (hash-table-ref/default all-tests-registry x #f)
#t
(begin
(debug:print 0 "ERROR: test " hed " has unrecognised waiton testname " x)
#f)))
newwaitons)))))
(debug:print-info 8 "waitons: " waitons)
;; check for hed in waitons => this would be circular, remove it and issue an
(let ((val (car x)))
(if (procedure? val) val #f)))
(append (if (list? items) items '())
(if (list? itemstable) itemstable '())))
'have-procedure)
((or (list? items)(list? itemstable)) ;; calc now
(debug:print-info 4 "items and itemstable are lists, calc now\n"
" items: " items " itemstable: " itemstable)
" items: " items " itemstable: " itemstable)
(items:get-items-from-config config))
(else #f))) ;; not iterated
#f ;; itemsdat 5
#f ;; spare - used for item-path
)))
(for-each
(lambda (waiton)
(if (and waiton (not (member waiton test-names)))
(begin
(set! required-tests (cons waiton required-tests))
(set! test-names (cons waiton test-names))))) ;; was an append, now a cons
waitons)
(let ((remtests (delete-duplicates (append waitons tal))))
(if (not (null? remtests))
(loop (car remtests)(cdr remtests)))))))
(if (not (null? required-tests))
(debug:print-info 1 "Adding " required-tests " to the run queue"))
;; NOTE: these are all parent tests, items are not expanded yet.
(debug:print-info 4 "test-records=" (hash-table->alist test-records))
(let ((reglen (any->number (configf:lookup *configdat* "setup" "runqueue"))))
(if reglen
(runs:run-tests-queue-new run-id runname test-records keyvallst flags test-patts required-tests reglen)
(runs:run-tests-queue-classic run-id runname test-records keyvallst flags test-patts required-tests)))
(let ((reglen (configf:lookup *configdat* "setup" "runqueue")))
(if (> (length (hash-table-keys test-records)) 0)
(runs:run-tests-queue run-id runname test-records keyvals flags test-patts required-tests (any->number reglen) all-tests-registry)
(debug:print-info 0 "No tests to run")))
(debug:print-info 4 "All done by here")))
;; loop logic. These are used in runs:run-tests-queue to make it a bit more readable.
;;
;; If reg not full and have items in tal then loop with (car tal)(cdr tal) reg reruns
;; If reg is full (i.e. length >= n
;; loop with (car reg) tal (cdr reg) reruns
;; If tal is empty
;; but have items in reg; loop with (car reg)(cdr reg) '() reruns
;; If reg is empty => all done
(define (runs:queue-next-hed tal reg n regfull)
(if regfull
(car reg)
(if (null? tal) ;; tal is used up, pop from reg
(car reg)
(car tal))))
;; (cond
;; ((and regfull (null? reg)(not (null? tal))) (car tal))
;; ((and regfull (not (null? reg))) (car reg))
;; ((and (not regfull)(null? tal)(not (null? reg))) (car reg))
;; ((and (not regfull)(not (null? tal))) (car tal))
;; (else
;; (debug:print 0 "ERROR: runs:queue-next-hed, tal=" tal ", reg=" reg ", n=" n ", regfull=" regfull)
;; #f)))
(define (runs:queue-next-tal tal reg n regfull)
(if regfull
tal
(if (null? tal) ;; must transfer from reg
(cdr reg)
(cdr tal))))
(define (runs:queue-next-reg tal reg n regfull)
(if regfull
(cdr reg)
(if (null? tal) ;; if tal is null and reg not full then '() as reg contents moved to tal
'()
reg)))
(define runs:nothing-left-in-queue-count 0)
(define (runs:expand-items hed tal reg reruns regfull newtal jobgroup max-concurrent-jobs run-id waitons item-path testmode test-record can-run-more items runname tconfig reglen test-registry test-records itemmap)
(let* ((loop-list (list hed tal reg reruns))
(prereqs-not-met (mt:lazy-get-prereqs-not-met run-id waitons item-path mode: testmode itemmap: itemmap))
(fails (runs:calc-fails prereqs-not-met))
(non-completed (runs:calc-not-completed prereqs-not-met)))
(debug:print-info 4 "START OF INNER COND #2 "
"\n can-run-more: " can-run-more
"\n testname: " hed
"\n prereqs-not-met: " (runs:pretty-string prereqs-not-met)
"\n non-completed: " (runs:pretty-string non-completed)
"\n fails: " (runs:pretty-string fails)
"\n testmode: " testmode
"\n (member 'toplevel testmode): " (member 'toplevel testmode)
"\n (null? non-completed): " (null? non-completed)
"\n reruns: " reruns
"\n items: " items
"\n can-run-more: " can-run-more)
(cond
;; all prereqs met, fire off the test
;; or, if it is a 'toplevel test and all prereqs not met are COMPLETED then launch
((and (not (member 'toplevel testmode))
(member (hash-table-ref/default test-registry (runs:make-full-test-name hed item-path) 'n/a)
'(DONOTRUN removed CANNOTRUN))) ;; *common:cant-run-states-sym*) ;; '(COMPLETED KILLED WAIVED UNKNOWN INCOMPLETE)) ;; try to catch repeat processing of COMPLETED tests here
(debug:print-info 1 "Test " hed " set to \"" (hash-table-ref test-registry (runs:make-full-test-name hed item-path)) "\". Removing it from the queue")
(if (or (not (null? tal))
(not (null? reg)))
(list (runs:queue-next-hed tal reg reglen regfull)
(runs:queue-next-tal tal reg reglen regfull)
(runs:queue-next-reg tal reg reglen regfull)
reruns)
(begin
(debug:print-info 0 "Nothing left in the queue!")
;; If get here twice then we know we've tried to expand all items
;; since there must be a logic issue with the handling of loops in the
;; items expand phase we will brute force an exit here.
(if (> runs:nothing-left-in-queue-count 2)
(begin
(debug:print 0 "WARNING: this condition is triggered when there were no items to expand and nothing to run. Please check your run for completeness")
(exit 0))
(set! runs:nothing-left-in-queue-count (+ runs:nothing-left-in-queue-count 1)))
#f)))
;;
((or (null? prereqs-not-met)
(and (member 'toplevel testmode)
(null? non-completed)))
(debug:print-info 4 "runs:expand-items: (or (null? prereqs-not-met) (and (member 'toplevel testmode)(null? non-completed)))")
(let ((test-name (tests:testqueue-get-testname test-record)))
(setenv "MT_TEST_NAME" test-name) ;;
(setenv "MT_RUNNAME" runname)
(set-megatest-env-vars run-id inrunname: runname) ;; these may be needed by the launching process
(let ((items-list (items:get-items-from-config tconfig)))
(if (list? items-list)
(begin
(tests:testqueue-set-items! test-record items-list)
(list hed tal reg reruns))
(begin
(debug:print 0 "ERROR: The proc from reading the items table did not yield a list - please report this")
(exit 1))))))
((and (null? fails)
(not (null? non-completed)))
(let* ((allinqueue (map (lambda (x)(if (string? x) x (db:test-get-testname x)))
(append newtal reruns)))
;; prereqstrs is a list of test names as strings that are prereqs for hed
(prereqstrs (delete-duplicates (map (lambda (x)(if (string? x) x (db:test-get-testname x)))
prereqs-not-met)))
;; a prereq that is not found in allinqueue will be put in the notinqueue list
;;
;; (notinqueue (filter (lambda (x)
;; (not (member x allinqueue)))
;; prereqstrs))
(give-up #f))
;; We can get here when a prereq has not been run due to *it* having a prereq that failed.
;; We need to use this to dequeue this item as CANNOTRUN
;;
(if (member testmode '(toplevel))
(for-each (lambda (prereq)
(if (eq? (hash-table-ref/default test-registry prereq 'justfine) 'CANNOTRUN)
(set! give-up #t)))
prereqstrs))
(if (and give-up
(not (and (null? tal)(null? reg))))
(let ((trimmed-tal (mt:discard-blocked-tests run-id hed tal test-records))
(trimmed-reg (mt:discard-blocked-tests run-id hed reg test-records)))
(debug:print 1 "WARNING: test " hed " has discarded prerequisites, removing it from the queue")
(let ((test-id (cdb:remote-run db:get-test-id-cached #f run-id hed "")))
(mt:test-set-state-status-by-id test-id "DEQUEUED" "PREQ_FAIL" "Failed to run due to failed prerequisites"))
(if (and (null? trimmed-tal)
(null? trimmed-reg))
#f
(list (runs:queue-next-hed trimmed-tal trimmed-reg reglen regfull)
(runs:queue-next-tal trimmed-tal trimmed-reg reglen regfull)
(runs:queue-next-reg trimmed-tal trimmed-reg reglen regfull)
reruns)))
(list (car newtal)(append (cdr newtal) reg) '() reruns))))
;; (debug:print-info 1 "allinqueue: " allinqueue)
;; (debug:print-info 1 "prereqstrs: " prereqstrs)
;; (debug:print-info 1 "notinqueue: " notinqueue)
;; (debug:print-info 1 "tal: " tal)
;; (debug:print-info 1 "newtal: " newtal)
;; (debug:print-info 1 "reg: " reg)
;; == == ;; num-retries code was here
;; == == ;; we use this opportunity to move contents of reg to tal
;; == == ;; but also lets check that the prerequisites are all in the newtal or reruns lists
;; == ==
;; == == (let* ((allinqueue (map (lambda (x)(if (string? x) x (db:test-get-testname x)))
;; == == (append newtal reruns)))
;; == == ;; prereqstrs is a list of test names as strings that are prereqs for hed
;; == == (prereqstrs (map (lambda (x)(if (string? x) x (db:test-get-testname x)))
;; == == prereqs-not-met))
;; == == ;; a prereq that is not found in allinqueue will be put in the notinqueue list
;; == == ;;
;; == == (notinqueue (filter (lambda (x)
;; == == (not (member x allinqueue)))
;; == == prereqstrs)))
;; == == (if (not (null? notinqueue))
;; == == (if (runs:can-keep-running? hed 5) ;; try five times
;; == == (begin
;; == == (debug:print-info 4 "increment cant-run-tests for " hed)
;; == == (runs:inc-cant-run-tests hed)
;; == == (list (car newtal)(append (cdr newtal) reg) '() reruns))
;; == == (begin
;; == ==
;; == == (if (runs:lownoise (conc "no fails prereq, null notinqueue " hed) 30)
;; == == (begin
;; == == (debug:print 1 "WARNING: test " hed " has no failed prerequisites but does have prerequistes that are NOT in the queue: " (string-intersperse notinqueue ", "))
;; == == (debug:print-info 4 "allinqueue: " allinqueue)
;; == == (debug:print-info 4 "prereqstrs: " prereqstrs)
;; == == (debug:print-info 4 "notinqueue: " notinqueue)))
;; == == (if (and (null? tal)(null? reg))
;; == == (list (car newtal)(append (cdr newtal) reg) '() reruns)
;; == == (list (runs:queue-next-hed tal reg reglen regfull)
;; == == (runs:queue-next-tal tal reg reglen regfull)
;; == == (runs:queue-next-reg tal reg reglen regfull)
;; == == reruns))))
;; == == ;; have prereqs in queue, keep going.
;; == == (begin
;; == == (if (runs:lownoise (conc "no fails prereq " hed) 30)
;; == == (debug:print-info 1 "no fails in prerequisites for " hed ", waiting on tests; "
;; == == (string-intersperse (map (lambda (x)
;; == == (if (string? x)
;; == == x
;; == == (runs:make-full-test-name (db:test-get-testname x)
;; == == (db:test-get-item-path x))))
;; == == non-completed) ", ")
;; == == ". Delaying launch of " hed "."))
;; == == (list (car newtal)(append (cdr newtal) reg) '() reruns))))) ;; an issue with prereqs not yet met?
((and (null? fails)
(null? non-completed))
(if (runs:can-keep-running? hed 5)
(begin
(runs:inc-cant-run-tests hed)
(debug:print-info 1 "no fails in prerequisites for " hed " but also none running, keeping " hed " for now. Try count: " (hash-table-ref/default *seen-cant-run-tests* hed 0))
;; num-retries code was here
;; we use this opportunity to move contents of reg to tal
(list (car newtal)(append (cdr newtal) reg) '() reruns)) ;; an issue with prereqs not yet met?
(begin
(debug:print-info 1 "no fails in prerequisites for " hed " but nothing seen running in a while, dropping test " hed " from the run queue")
(list (runs:queue-next-hed tal reg reglen regfull)
(runs:queue-next-tal tal reg reglen regfull)
(runs:queue-next-reg tal reg reglen regfull)
reruns))))
((and (not (null? fails))(member 'normal testmode))
(debug:print-info 1 "test " hed " (mode=" testmode ") has failed prerequisite(s); "
(string-intersperse (map (lambda (t)(conc (db:test-get-testname t) ":" (db:test-get-state t)"/"(db:test-get-status t))) fails) ", ")
", removing it from to-do list")
(if (or (not (null? reg))(not (null? tal)))
(begin
(hash-table-set! test-registry hed 'CANNOTRUN)
(list (runs:queue-next-hed tal reg reglen regfull)
(runs:queue-next-tal tal reg reglen regfull)
(runs:queue-next-reg tal reg reglen regfull)
(cons hed reruns)))
#f)) ;; #f flags do not loop
((and (not (null? fails))(member 'toplevel testmode))
(if (or (not (null? reg))(not (null? tal)))
(list (car newtal)(append (cdr newtal) reg) '() reruns)
#f))
(else
(debug:print 1 "WARNING: FAILS or incomplete tests are preventing completion of this run. Dropping test " hed " from the run queue")
(list (runs:queue-next-hed tal reg reglen regfull)
(runs:queue-next-tal tal reg reglen regfull)
(runs:queue-next-reg tal reg reglen regfull)
reruns))))) ;; (list (car newtal)(cdr newtal) reg reruns)))))
(define (runs:mixed-list-testname-and-testrec->list-of-strings inlst)
(if (null? inlst)
'()
(map (lambda (t)
(cond
((vector? t)
(conc (db:test-get-state t) "/" (db:test-get-status t)))
((string? t)
t)
(else
(conc t))))
inlst)))
(define (runs:process-expanded-tests hed tal reg reruns reglen regfull test-record runname test-name item-path jobgroup max-concurrent-jobs run-id waitons item-path testmode test-patts required-tests test-registry registry-mutex flags keyvals run-info newtal all-tests-registry itemmap)
(let* ((run-limits-info (runs:can-run-more-tests jobgroup max-concurrent-jobs)) ;; look at the test jobgroup and tot jobs running
(have-resources (car run-limits-info))
(num-running (list-ref run-limits-info 1))
(num-running-in-jobgroup (list-ref run-limits-info 2))
(max-concurrent-jobs (list-ref run-limits-info 3))
(job-group-limit (list-ref run-limits-info 4))
(prereqs-not-met (mt:lazy-get-prereqs-not-met run-id waitons item-path mode: testmode itemmap: itemmap))
(fails (runs:calc-fails prereqs-not-met))
(non-completed (runs:calc-not-completed prereqs-not-met))
(loop-list (list hed tal reg reruns)))
(debug:print-info 4 "have-resources: " have-resources " prereqs-not-met: ("
(string-intersperse
(map (lambda (t)
(if (vector? t)
(conc (db:test-get-state t) "/" (db:test-get-status t))
(conc " WARNING: t is not a vector=" t )))
prereqs-not-met) ", ") ") fails: " fails)
(if (not (null? prereqs-not-met))
(debug:print-info 2 "waiting on tests; " (string-intersperse (runs:mixed-list-testname-and-testrec->list-of-strings prereqs-not-met) ", ")))
;; Don't know at this time if the test have been launched at some time in the past
;; i.e. is this a re-launch?
(debug:print-info 4 "run-limits-info = " run-limits-info)
(cond
;; Check item path against item-patts,
;;
((not (tests:match test-patts (tests:testqueue-get-testname test-record) item-path required: required-tests)) ;; This test/itempath is not to be run
;; else the run is stuck, temporarily or permanently
;; but should check if it is due to lack of resources vs. prerequisites
(debug:print-info 1 "Skipping " (tests:testqueue-get-testname test-record) " " item-path " as it doesn't match " test-patts)
(if (or (not (null? tal))(not (null? reg)))
(list (runs:queue-next-hed tal reg reglen regfull)
(runs:queue-next-tal tal reg reglen regfull)
(runs:queue-next-reg tal reg reglen regfull)
reruns)
#f))
;; Register tests
;;
((not (hash-table-ref/default test-registry (runs:make-full-test-name test-name item-path) #f))
(debug:print-info 4 "Pre-registering test " test-name "/" item-path " to create placeholder" )
(if (eq? *transport-type* 'fs) ;; no point in parallel registration if use fs
(begin
(cdb:tests-register-test *runremote* run-id test-name item-path)
(hash-table-set! test-registry (runs:make-full-test-name test-name item-path) 'done))
(let ((th (make-thread (lambda ()
(mutex-lock! registry-mutex)
(hash-table-set! test-registry (runs:make-full-test-name test-name item-path) 'start)
(mutex-unlock! registry-mutex)
;; If haven't done it before register a top level test if this is an itemized test
(if (not (eq? (hash-table-ref/default test-registry (runs:make-full-test-name test-name "") #f) 'done))
(cdb:tests-register-test *runremote* run-id test-name ""))
(cdb:tests-register-test *runremote* run-id test-name item-path)
(mutex-lock! registry-mutex)
(hash-table-set! test-registry (runs:make-full-test-name test-name item-path) 'done)
(mutex-unlock! registry-mutex))
(conc test-name "/" item-path))))
(thread-start! th)))
(runs:shrink-can-run-more-tests-count) ;; DELAY TWEAKER (still needed?)
(if (and (null? tal)(null? reg))
(list hed tal (append reg (list hed)) reruns)
(list (runs:queue-next-hed tal reg reglen regfull)
(runs:queue-next-tal tal reg reglen regfull)
;; NB// Here we are building reg as we register tests
;; if regfull we must pop the front item off reg
(if regfull
(append (cdr reg) (list hed))
(append reg (list hed)))
reruns)))
;; At this point hed test registration must be completed.
;;
((eq? (hash-table-ref/default test-registry (runs:make-full-test-name test-name item-path) #f)
'start)
(debug:print-info 0 "Waiting on test registration(s): "
(string-intersperse
(filter (lambda (x)
(eq? (hash-table-ref/default test-registry x #f) 'start))
(hash-table-keys test-registry))
", "))
(thread-sleep! 0.1)
(list hed tal reg reruns))
;; If no resources are available just kill time and loop again
;;
((not have-resources) ;; simply try again after waiting a second
(if (runs:lownoise "no resources" 60)
(debug:print-info 1 "no resources to run new tests, waiting ..."))
;; Have gone back and forth on this but db starvation is an issue.
;; wait one second before looking again to run jobs.
(thread-sleep! 1)
;; could have done hed tal here but doing car/cdr of newtal to rotate tests
(list (car newtal)(cdr newtal) reg reruns))
;; This is the final stage, everything is in place so launch the test
;;
((and have-resources
(or (null? prereqs-not-met)
(and (eq? testmode 'toplevel)
(null? non-completed))))
;; (hash-table-delete! *max-tries-hash* (runs:make-full-test-name test-name item-path))
;; we are going to reset all the counters for test retries by setting a new hash table
;; this means they will increment only when nothing can be run
(set! *max-tries-hash* (make-hash-table))
(run:test run-id run-info keyvals runname test-record flags #f test-registry all-tests-registry)
(hash-table-set! test-registry (runs:make-full-test-name test-name item-path) 'running)
(runs:shrink-can-run-more-tests-count) ;; DELAY TWEAKER (still needed?)
;; (thread-sleep! *global-delta*)
(if (or (not (null? tal))(not (null? reg)))
(list (runs:queue-next-hed tal reg reglen regfull)
(runs:queue-next-tal tal reg reglen regfull)
(runs:queue-next-reg tal reg reglen regfull)
reruns)
#f))
;; must be we have unmet prerequisites
;;
(else
(debug:print 4 "FAILS: " fails)
;; If one or more of the prereqs-not-met are FAIL then we can issue
;; a message and drop hed from the items to be processed.
;; (runs:mixed-list-testname-and-testrec->list-of-strings prereqs-not-met)
(if (not (null? prereqs-not-met))
(debug:print-info 1 "waiting on tests; " (string-intersperse
(runs:mixed-list-testname-and-testrec->list-of-strings
prereqs-not-met) ", ")))
(if (null? fails)
(begin
;; couldn't run, take a breather
(debug:print-info 0 "Waiting for more work to do...")
(thread-sleep! 1)
(list (car newtal)(cdr newtal) reg reruns))
;; the waiton is FAIL so no point in trying to run hed ever again
(if (or (not (null? reg))(not (null? tal)))
(if (vector? hed)
(begin
(debug:print 1 "WARN: Dropping test " (db:test-get-testname hed) "/" (db:test-get-item-path hed)
" from the launch list as it has prerequistes that are FAIL")
(runs:shrink-can-run-more-tests-count) ;; DELAY TWEAKER (still needed?)
;; (thread-sleep! *global-delta*)
(hash-table-set! test-registry (runs:make-full-test-name test-name item-path) 'removed)
(list (runs:queue-next-hed tal reg reglen regfull)
(runs:queue-next-tal tal reg reglen regfull)
(runs:queue-next-reg tal reg reglen regfull)
reruns ;; WAS: (cons hed reruns) ;; but that makes no sense?
))
(begin
(debug:print 0 "WARNING: Test may not have processed correctly. Could be a race condition in your test implementation? Dropping test " hed) ;; " as it has prerequistes that are FAIL. (NOTE: hed is not a vector)")
(runs:shrink-can-run-more-tests-count) ;; DELAY TWEAKER (still needed?)
;; (list hed tal reg reruns)
;; (list (car newtal)(cdr newtal) reg reruns)
(list (runs:queue-next-hed tal reg reglen regfull)
(runs:queue-next-tal tal reg reglen regfull)
(runs:queue-next-reg tal reg reglen regfull)
reruns) ;; (cons hed reruns))
;;(list (if (null? tal)(car newtal)(car tal))
;; tal
;; reg
;; reruns)
))
;; can't drop this - maybe running? Just keep trying
(list hed
tal
reg
reruns)))))))
;; every time though the loop increment the test/itempatt val.
;; when the min is > max-allowed and none running then force exit
;;
(define *max-tries-hash* (make-hash-table))
;; test-records is a hash table testname:item_path => vector < testname testconfig waitons priority items-info ... >
(define (runs:run-tests-queue run-id runname test-records keyvals flags test-patts required-tests reglen-in all-tests-registry)
;; At this point the list of parent tests is expanded
;; NB// Should expand items here and then insert into the run queue.
(debug:print 5 "test-records: " test-records ", flags: " (hash-table->alist flags))
;; Do mark-and-find clean up of db before starting runing of quue
;;
;; (cdb:remote-run db:find-and-mark-incomplete #f)
(let ((run-info (cdb:remote-run db:get-run-info #f run-id))
(tests-info (mt:get-tests-for-run run-id #f '() '())) ;; qryvals: "id,testname,item_path"))
(sorted-test-names (tests:sort-by-priority-and-waiton test-records))
(test-registry (make-hash-table))
(registry-mutex (make-mutex))
(num-retries 0)
(max-retries (config-lookup *configdat* "setup" "maxretries"))
(max-concurrent-jobs (let ((mcj (config-lookup *configdat* "setup" "max_concurrent_jobs")))
(if (and mcj (string->number mcj))
(string->number mcj)
1))) ;; length of the register queue ahead
(reglen (if (number? reglen-in) reglen-in 1))
(last-time-incomplete (current-seconds))
(last-time-some-running (current-seconds)))
;; Initialize the test-registery hash with tests that already have a record
;; convert state to symbol and use that as the hash value
(for-each (lambda (trec)
(let ((id (db:test-get-id trec))
(tn (db:test-get-testname trec))
(ip (db:test-get-item-path trec))
(st (db:test-get-state trec)))
(if (not (equal? st "DELETED"))
(hash-table-set! test-registry (runs:make-full-test-name tn ip) (string->symbol st)))))
tests-info)
(set! max-retries (if (and max-retries (string->number max-retries))(string->number max-retries) 100))
(let loop ((hed (car sorted-test-names))
(tal (cdr sorted-test-names))
(reg '()) ;; registered, put these at the head of tal
(reruns '()))
(if (not (null? reruns))(debug:print-info 4 "reruns=" reruns))
;; Here we mark any old defunct tests as incomplete. Do this every fifteen minutes
;; (if (> (current-seconds)(+ last-time-incomplete 900))
;; (begin
;; (set! last-time-incomplete (current-seconds))
;; (cdb:remote-run db:find-and-mark-incomplete #f)))
;; (print "Top of loop, hed=" hed ", tal=" tal " ,reruns=" reruns)
(let* ((test-record (hash-table-ref test-records hed))
(test-name (tests:testqueue-get-testname test-record))
(tconfig (tests:testqueue-get-testconfig test-record))
(jobgroup (config-lookup tconfig "test_meta" "jobgroup"))
(testmode (let ((m (config-lookup tconfig "requirements" "mode")))
(if m (map string->symbol (string-split m)) '(normal))))
(itemmap (configf:lookup tconfig "requirements" "itemmap"))
(waitons (tests:testqueue-get-waitons test-record))
(priority (tests:testqueue-get-priority test-record))
(itemdat (tests:testqueue-get-itemdat test-record)) ;; itemdat can be a string, list or #f
(items (tests:testqueue-get-items test-record))
(item-path (item-list->path itemdat))
(tfullname (runs:make-full-test-name test-name item-path))
(newtal (append tal (list hed)))
(regfull (>= (length reg) reglen))
(num-running (cdb:remote-run db:get-count-tests-running-for-run-id #f run-id)))
(if (> num-running 0)
(set! last-time-some-running (current-seconds)))
(if (> (current-seconds)(+ last-time-some-running 60))
(hash-table-set! *max-tries-hash* tfullname (+ (hash-table-ref/default *max-tries-hash* tfullname 0) 1)))
;; (debug:print 0 "max-tries-hash: " (hash-table->alist *max-tries-hash*))
;; Ensure all top level tests get registered. This way they show up as "NOT_STARTED" on the dashboard
;; and it is clear they *should* have run but did not.
(if (not (hash-table-ref/default test-registry (runs:make-full-test-name test-name "") #f))
(begin
(cdb:tests-register-test *runremote* run-id test-name "")
(hash-table-set! test-registry (runs:make-full-test-name test-name "") 'done)))
;; Fast skip of tests that are already "COMPLETED" - NO! Cannot do that as the items may not have been expanded yet :(
;;
(if (member (hash-table-ref/default test-registry tfullname #f)
'(DONOTRUN removed)) ;; *common:cant-run-states-sym*) ;; '(COMPLETED KILLED WAIVED UNKNOWN INCOMPLETE))
(begin
(debug:print-info 0 "Skipping test " tfullname " as it has been marked do not run due to being completed or not runnable")
(if (or (not (null? tal))(not (null? reg)))
(loop (runs:queue-next-hed tal reg reglen regfull)
(runs:queue-next-tal tal reg reglen regfull)
(runs:queue-next-reg tal reg reglen regfull)
reruns))))
;; (loop (car tal)(cdr tal) reg reruns))))
(debug:print 4 "TOP OF LOOP => "
"test-name: " test-name
"\n test-record " test-record
"\n hed: " hed
"\n itemdat: " itemdat
"\n items: " items
"\n item-path: " item-path
"\n waitons: " waitons
"\n num-retries: " num-retries
"\n tal: " tal
"\n reruns: " reruns
"\n regfull: " regfull
"\n reglen: " reglen
"\n length reg: " (length reg)
"\n reg: " reg)
;; check for hed in waitons => this would be circular, remove it and issue an
;; error
(if (member test-name waitons)
(begin
(debug:print 0 "ERROR: test " test-name " has listed itself as a waiton, please correct this!")
(set! waiton (filter (lambda (x)(not (equal? x hed))) waitons))))
(cond
;; We want to catch tests that have waitons that are NOT in the queue and discard them IFF
;; they have been through the wringer 10 or more times
((and (list? waitons)
(not (null? waitons))
(> (hash-table-ref/default *max-tries-hash* tfullname 0) 10)
(not (null? (filter
number?
(map (lambda (waiton)
(if (and (not (member waiton tal)) ;; this waiton is not in the list to be tried to run
(not (member waiton reruns)))
1
#f))
waitons))))) ;; could do this more elegantly with a marker....
(debug:print 0 "WARNING: Marking test " tfullname " as not runnable. It is waiting on tests that cannot be run. Giving up now.")
(hash-table-set! test-registry tfullname 'removed))
;; items is #f then the test is ok to be handed off to launch (but not before)
;;
((not items)
(debug:print-info 4 "OUTER COND: (not items)")
(if (and (not (tests:match test-patts (tests:testqueue-get-testname test-record) item-path required: required-tests))
(not (null? tal)))
(loop (car tal)(cdr tal) reg reruns))
(let ((loop-list (runs:process-expanded-tests hed tal reg reruns reglen regfull test-record runname test-name item-path jobgroup max-concurrent-jobs run-id waitons item-path testmode test-patts required-tests test-registry registry-mutex flags keyvals run-info newtal all-tests-registry itemmap)))
(if loop-list (apply loop loop-list))))
;; items processed into a list but not came in as a list been processed
;;
((and (list? items) ;; thus we know our items are already calculated
(not itemdat)) ;; and not yet expanded into the list of things to be done
(debug:print-info 4 "OUTER COND: (and (list? items)(not itemdat))")
;; Must determine if the items list is valid. Discard the test if it is not.
(if (and (list? items)
(> (length items) 0)
(and (list? (car items))
(> (length (car items)) 0))
(debug:debug-mode 1))
(debug:print 2 (map (lambda (row)
(conc (string-intersperse
(map (lambda (varval)
(string-intersperse varval "="))
row)
" ")
"\n"))
items)))
(for-each
(lambda (my-itemdat)
(let* ((new-test-record (let ((newrec (make-tests:testqueue)))
(vector-copy! test-record newrec)
newrec))
(my-item-path (item-list->path my-itemdat)))
(if (tests:match test-patts hed my-item-path required: required-tests) ;; (patt-list-match my-item-path item-patts) ;; yes, we want to process this item, NOTE: Should not need this check here!
(let ((newtestname (runs:make-full-test-name hed my-item-path))) ;; test names are unique on testname/item-path
(tests:testqueue-set-items! new-test-record #f)
(tests:testqueue-set-itemdat! new-test-record my-itemdat)
(tests:testqueue-set-item_path! new-test-record my-item-path)
(hash-table-set! test-records newtestname new-test-record)
(set! tal (append tal (list newtestname))))))) ;; since these are itemized create new test names testname/itempath
items)
;; (debug:print-info 0 "Test " (tests:testqueue-get-testname test-record) " is itemized but has no items")
;; At this point we have possibly added items to tal but all must be handed off to
;; INNER COND logic. I think loop without rotating the queue
;; (loop hed tal reg reruns))
;; (let ((newtal (append tal (list hed)))) ;; We should discard hed as it has been expanded into it's items? Yes, but only if this *is* an itemized test
;; (loop (car newtal)(cdr newtal) reg reruns)
(if (null? tal)
#f
(loop (car tal)(cdr tal) reg reruns)))
;; if items is a proc then need to run items:get-items-from-config, get the list and loop
;; - but only do that if resources exist to kick off the job
;; EXPAND ITEMS
((or (procedure? items)(eq? items 'have-procedure))
(let ((can-run-more (runs:can-run-more-tests jobgroup max-concurrent-jobs)))
(if (and (list? can-run-more)
(car can-run-more))
(let ((loop-list (runs:expand-items hed tal reg reruns regfull newtal jobgroup max-concurrent-jobs run-id waitons item-path testmode test-record can-run-more items runname tconfig reglen test-registry test-records itemmap)))
(if loop-list
(apply loop loop-list)))
;; if can't run more just loop with next possible test
(loop (car newtal)(cdr newtal) reg reruns))))
;; this case should not happen, added to help catch any bugs
((and (list? items) itemdat)
(debug:print 0 "ERROR: Should not have a list of items in a test and the itemspath set - please report this")
(exit 1))
((not (null? reruns))
(let* ((newlst (tests:filter-non-runnable run-id tal test-records)) ;; i.e. not FAIL, WAIVED, INCOMPLETE, PASS, KILLED,
(junked (lset-difference equal? tal newlst)))
(debug:print-info 4 "full drop through, if reruns is less than 100 we will force retry them, reruns=" reruns ", tal=" tal)
(if (< num-retries max-retries)
(set! newlst (append reruns newlst)))
(set! num-retries (+ num-retries 1))
;; (thread-sleep! (+ 1 *global-delta*))
(if (not (null? newlst))
;; since reruns have been tacked on to newlst create new reruns from junked
(loop (car newlst)(cdr newlst) reg (delete-duplicates junked)))))
((not (null? tal))
(debug:print-info 4 "I'm pretty sure I shouldn't get here."))
((not (null? reg)) ;; could we get here with leftovers?
(debug:print-info 0 "Have leftovers!")
(loop (car reg)(cdr reg) '() reruns))
(else
(debug:print-info 4 "Exiting loop with...\n hed=" hed "\n tal=" tal "\n reruns=" reruns))
))) ;; LET* ((test-record
;; we get here on "drop through". All done!
(debug:print-info 1 "All tests launched")))
(define (runs:calc-fails prereqs-not-met)
(filter (lambda (test)
(and (vector? test) ;; not (string? test))
(equal? (db:test-get-state test) "COMPLETED")
(not (member (db:test-get-status test)
'("PASS" "WARN" "CHECK" "WAIVED" "SKIP")))))
prereqs-not-met))
(if (not (setup-for-run))
(begin
(debug:print 0 "ERROR: cannot find megatest.config, exiting")
(exit))))
(debug:print-info 2 "Starting server using " transport " transport")
(set! *transport-type* transport)
(case transport
((fs) (exit)) ;; there is no "fs" transport
((fs) (exit)) ;; there is no "fs" server transport
((http) (http-transport:launch))
((zmq) (zmq-transport:launch))
(else
(debug:print "WARNING: unrecognised transport " transport)
(exit))))
;;======================================================================
(let ((pub-socket (vector-ref *runremote* 1)))
(send-message pub-socket return-addr send-more: #t)
(send-message pub-socket (db:obj->string (vector success/fail query-sig result)))))
(else
(debug:print 0 "ERROR: unrecognised transport type: " *transport-type*)
result)))
(define (server:ensure-running)
(let loop ((servers (open-run-close tasks:get-best-server tasks:open-db))
(trycount 0))
(if (or (not servers)
(null? servers))
(begin
(if (even? trycount) ;; just do the server start every other time through this loop (every 8 seconds)
(let ((cmdln (conc (if (getenv "MT_MEGATEST") (getenv "MT_MEGATEST") "megatest")
" -server - -daemonize")))
(debug:print 0 "INFO: Starting server (" cmdln ") as none running ...")
;; (server:launch (string->symbol (args:get-arg "-transport" "http"))))
;; no need to use fork, no need to do the list-servers trick. Just start the damn server, it will exit on it's own
;; if there is an existing server
(system cmdln)
(thread-sleep! 3)
;; (process-run (car (argv)) (list "-server" "-" "-daemonize" "-transport" (args:get-arg "-transport" "http")))
)
(begin
(debug:print-info 0 "Waiting for server to start")
(thread-sleep! 4)))
(if (< trycount 10)
(loop (open-run-close tasks:get-best-server tasks:open-db)
(+ trycount 1))
(debug:print 0 "WARNING: Couldn't start or find a server.")))
(debug:print 2 "INFO: Server(s) running " servers)
)))
;; state: 'live, 'shutting-down, 'dead
(define (tasks:server-register mdb pid interface port priority state transport #!key (pubport -1))
(debug:print-info 11 "tasks:server-register " pid " " interface " " port " " priority " " state)
(sqlite3:execute
mdb
"INSERT OR REPLACE INTO servers (pid,hostname,port,pubport,start_time,priority,state,mt_version,heartbeat,interface,transport)
VALUES(?, ?, ?, ?, strftime('%s','now'), ?, ?, ?, strftime('%s','now'),?,?);"
pid (get-host-name) port pubport priority (conc state) megatest-version interface (conc transport)) pid (get-host-name) port pubport priority (conc state)
(common:version-signature)
interface
(conc transport))
(vector
(tasks:server-get-server-id mdb (get-host-name) interface port pid)
interface
port
pubport
transport
))
;; NB// two servers with same pid on different hosts will be removed from the list if pid: is used!
(define (tasks:server-deregister mdb hostname #!key (port #f)(pid #f)(action 'markdead))
(define (tasks:server-deregister mdb hostname #!key (port #f)(pid #f)(action 'delete))
(debug:print-info 11 "server-deregister " hostname ", port " port ", pid " pid)
(if *db-write-access*
(if pid
(case action
((delete)(sqlite3:execute mdb "DELETE FROM servers WHERE pid=?;" pid))
(else (sqlite3:execute mdb "UPDATE servers SET state='dead' WHERE pid=?;" pid)))
(if port
(case action
((delete)(sqlite3:execute mdb "DELETE FROM servers WHERE hostname=? AND port=?;" hostname port))
(else (sqlite3:execute mdb "UPDATE servers SET state='dead' WHERE hostname=? AND port=?;" hostname port)))
((delete)(sqlite3:execute mdb "DELETE FROM servers WHERE (interface=? or hostname=?) AND port=?;" hostname hostname port))
(else (sqlite3:execute mdb "UPDATE servers SET state='dead' WHERE (interface=? or hostname=?) AND port=?;" hostname hostname port)))
(debug:print 0 "ERROR: tasks:server-deregister called with neither pid nor port specified")))))
(define (tasks:server-deregister-self mdb hostname)
(tasks:server-deregister mdb hostname pid: (current-process-id)))
;; need a simple call for robustly removing records given host and port
(define (tasks:server-delete mdb hostname port)
(define (tasks:have-clients? mdb server-id)
(null? (tasks:get-logged-in-clients mdb server-id)))
;; ping each server in the db and return first found that responds.
;; remove any others. will not necessarily remove all!
(define (tasks:get-best-server mdb)
(let ((res '())
(best #f)) (best #f)
(transport (if (and *transport-type*
(not (eq? *transport-type* 'fs)))
(conc *transport-type*)
"%")))
(sqlite3:for-each-row
(lambda (id interface port pubport transport pid hostname)
(set! res (cons (vector id interface port pubport transport pid hostname) res))
;;(debug:print-info 2 "Found existing server " hostname ":" port " registered in db"))
)
mdb
"SELECT id,interface,port,pubport,transport,pid,hostname FROM servers
WHERE strftime('%s','now')-heartbeat < 10
AND mt_version=? AND transport LIKE ?
AND mt_version=? ORDER BY start_time DESC LIMIT 1;" megatest-version)
ORDER BY start_time DESC LIMIT 1;" (common:version-signature) transport)
;; for now we are keeping only one server registered in the db, return #f or first server found
(if (null? res) #f (car res))))
;; BUG: This logic is probably needed unless methodology changes completely...
;;
;; (if (null? res) #f
;; (let loop ((hed (car res))
" EXCEPTION: " ((condition-property-accessor 'exn 'message) exn))
(debug:print 1 "Sending signal/term to " pid " on " hostname)
(process-signal pid signal/term)
(thread-sleep! 5) ;; give it five seconds to die peacefully then do a brutal kill
;;(process-signal pid signal/kill)
) ;; local machine, send sig term
(begin
(debug:print-info 1 "Stopping remote servers not yet supported."))))
;; (debug:print-info 1 "Telling alive server on " hostname ":" port " to commit servercide")
;; (let ((serverdat (list hostname port)))
;; (case (string->symbol transport)
;; ((http)(http-transport:client-connect hostname port))
;; (else (debug:print "ERROR: remote stopping servers of type " transport " not supported yet")))
;; (cdb:kill-server serverdat))))) ;; remote machine, try telling server to commit suicide
;;(debug:print-info 1 "Stopping remote servers not yet supported."))))
(debug:print-info 1 "Telling alive server on " hostname ":" port " to commit servercide")
(let ((serverdat (list hostname port)))
(case (if (string? transport) (string->symbol transport) transport)
((http)(http-transport:client-connect hostname port))
(else (debug:print "ERROR: remote stopping servers of type " transport " not supported yet")))
(cdb:kill-server serverdat pid))))) ;; remote machine, try telling server to commit suicide
(begin
(if status
(if (equal? hostname (get-host-name))
(begin
(debug:print-info 1 "Sending signal/term to " pid " on " hostname)
(process-signal pid signal/term) ;; local machine, send sig term
(thread-sleep! 5) ;; give it five seconds to die peacefully then do a brutal kill
(if (null? tal)
(string-intersperse (append (reverse res)(list qry)) " OR ")
(loop (car tal)(cdr tal)(cons qry res)))))))
#f))
;; get the previous record for when this test was run where all keys match but runname
;; returns #f if no such test found, returns a single test record if found
;;
;; Run this server-side
;;
(define (test:get-previous-test-run-record db run-id test-name item-path)
(let* ((keys (cdb:remote-run db:get-keys #f))
(selstr (string-intersperse (map (lambda (x)(vector-ref x 0)) keys) ","))
(qrystr (string-intersperse (map (lambda (x)(conc (vector-ref x 0) "=?")) keys) " AND "))
(let* ((keys (db:get-keys db))
(selstr (string-intersperse keys ","))
(qrystr (string-intersperse (map (lambda (x)(conc x "=?")) keys) " AND "))
(keyvals #f))
;; first look up the key values from the run selected by run-id
(sqlite3:for-each-row
(lambda (a . b)
(set! keyvals (cons a b)))
db
(conc "SELECT " selstr " FROM runs WHERE id=? ORDER BY event_time DESC;") run-id)
(conc "SELECT id FROM runs WHERE " qrystr " AND id != ?;") (append keyvals (list run-id)))
;; for each run starting with the most recent look to see if there is a matching test
;; if found then return that matching test record
(debug:print 4 "selstr: " selstr ", qrystr: " qrystr ", keyvals: " keyvals ", previous run ids found: " prev-run-ids)
(if (null? prev-run-ids) #f
(let loop ((hed (car prev-run-ids))
(tal (cdr prev-run-ids)))
(let ((results (cdb:remote-run db:get-tests-for-run #f hed (conc test-name "/" item-path)'() '())))
(let ((results (db:get-tests-for-run db hed (conc test-name "/" item-path)'() '() #f #f #f #f #f)))
(debug:print 4 "Got tests for run-id " run-id ", test-name " test-name ", item-path " item-path ": " results)
(if (and (null? results)
(not (null? tal)))
(loop (car tal)(cdr tal))
(if (null? results) #f
(car results))))))))))
;; get the previous records for when these tests were run where all keys match but runname
;; NB// Merge this with test:get-previous-test-run-records? This one looks for all matching tests
;; can use wildcards. Also can likely be factored in with get test paths?
;;
;; Run this remotely!!
;;
(define (test:get-matching-previous-test-run-records db run-id test-name item-path)
(let* ((keys (cdb:remote-run db:get-keys #f))
(let* ((keys (db:get-keys db))
(selstr (string-intersperse (map (lambda (x)(vector-ref x 0)) keys) ","))
(qrystr (string-intersperse (map (lambda (x)(conc (vector-ref x 0) "=?")) keys) " AND "))
(keyvals #f)
(tests-hash (make-hash-table)))
;; first look up the key values from the run selected by run-id
(sqlite3:for-each-row
(lambda (a . b)
(tests:check-waiver-eligibility testdat prev-test))
(set! real-status "WAIVED"))
(debug:print 4 "real-status " real-status ", waived " waived ", status " status)
;; update the primary record IF state AND status are defined
(if (and state status)
(begin
(cdb:test-set-status-state *runremote* test-id real-status state (if waived waived comment)))
(cdb:test-set-status-state *runremote* test-id real-status state (if waived waived comment))
(mt:process-triggers test-id state real-status)))
;; if status is "AUTO" then call rollup (note, this one modifies data in test
;; run area, it does remote calls under the hood.
(if (and test-id state status (equal? status "AUTO"))
(db:test-data-rollup #f test-id status work-area: work-area))
;; add metadata (need to do this way to avoid SQL injection issues)
[fields]
SYSTEM TEXT
RELEASE TEXT
[setup]
# Adjust max_concurrent_jobs to limit how much you load your machines
max_concurrent_jobs 500
max_concurrent_jobs 150
# This is your link path, you can move it but it is generally better to keep it stable
linktree #{shell readlink -f #{getenv PWD}/../simplelinks}
[include testqa/configs/megatest.abc.config]
# Add additional steps here. Format is "stepname script"
[ezsteps]
step1 step1.sh
# Test requirements are specified here
[requirements]
# waiton setup
priority 0
# Iteration for your tests are controlled by the items section
[items]
NUMBER #{scheme (string-intersperse (map number->string (sort (let loop ((a 0)(res '()))(if (< a (or (any->number (get-environment-variable "NUMTESTS")) 1100))(loop (+ a 1)(cons a res)) res)) >)) " ")}
NUMBER #{scheme (string-intersperse (map number->string (sort (let loop ((a 0)(res '()))(if (<= a (or (any->number (get-environment-variable "NUMTESTS")) 2500))(loop (+ a 1)(cons a res)) res)) <)) " ")}
# test_meta is a section for storing additional data on your test
[test_meta]
author matt
owner matt
description An example test
tags tagone,tagtwo
# Add additional steps here. Format is "stepname script"
[ezsteps]
step1 step1.sh
# Test requirements are specified here
[requirements]
# waiton bigrun
waiton bigrun
priority 0
mode itemmatchmode itemwait
itemmap .*/
# Iteration for your tests are controlled by the items section
[items]
NUMBER #{scheme (string-intersperse (map number->string (sort (let loop ((a 0)(res '()))(if (< a 1500)(loop (+ a 1)(consa res)) res)) >)) " ")}NUMBER #{scheme (string-intersperse (map (lambda (x)(conc "blah/" x)) \
(map number->string (sort (let loop ((a 0)(res '())) \
(if (<= a (or (any->number (get-environment-variable "NUMTESTS")) 2500)) \
(loop (+ a 1)(cons a res)) res)) <))) " ")}
# test_meta is a section for storing additional data on your test
[test_meta]
author matt
owner matt
description An example test
tags tagone,tagtwo
# Add additional steps here. Format is "stepname script"
[ezsteps]
step1 step1.sh
# Test requirements are specified here
[requirements]
waiton bigrun2
priority 0
mode itemwait
itemmap .*/
# Iteration for your tests are controlled by the items section
[items]
NUMBER #{scheme (string-intersperse (map (lambda (x)(conc "blah/" x)) \
(map number->string (sort (let loop ((a 0)(res '())) \
(if (<= a (or (any->number (get-environment-variable "NUMTESTS")) 2500)) \
(loop (+ a 1)(cons a res)) res)) <))) " ")}
# test_meta is a section for storing additional data on your test
[test_meta]
author matt
owner matt
description An example test
tags tagone,tagtwo
reviewed never
[fields]
sysname TEXT
fsname TEXT
datapath TEXT
# refareas can be searched to find previous runs
# the path points to where megatest.db exists
[refareas]
area1 /tmp/oldarea/megatest
[include config/mt_include_1.config]
[misc]
home #{shell readlink -f $MT_RUN_AREA_HOME}
parent #{shell readlink -f $MT_RUN_AREA_HOME/..}
[tests-paths]
1 #{get misc parent}/simplerun/tests
[setup]
# Set launchwait to yes to use the oldlaunch run code that waits for the launch process to return before
# Set launchwait to no to use the more agressive code that does not wait for the launch to complete before proceeding
# proceeding.
# launchwait yes# this may save a few milliseconds on launching tests
# launchwait no
waivercommentpatt ^WW\d+ [a-z].*
# Use http instead of direct filesystem access
# transport http
transport fs
# If defined the runs:run-tests-queue-new queue code is used with the register test depth
# given. Otherwise the old code is used. The old code will be removed in the future and
# If set to "default" the old code is used. Otherwise defaults to 200 or uses
# a default of 10 used.# runqueue 2
# numeric value given.
#
runqueue 20
# Default runtimelim 1d 1h 1m 10s
#
runtimelim 20m
# Deadtime - when to consider tests dead (i.e. haven't heard from them in too long)
# Number in seconds, set to 20 seconds here to trigger a little trouble. Default is
# 1800
#
deadtime 600
# It is possible (but not recommended) to override the rsync command used
# to populate the test directories. For test development the following
# example can be useful
#
# testcopycmd cp --remove-destination -rsv TEST_SRC_PATH/. TEST_TARG_PATH/. >> TEST_TARG_PATH/mt_launch.log 2>> TEST_TARG_PATH/mt_launch.log
testcopycmd cp --remove-destination -rsv TEST_SRC_PATH/. TEST_TARG_PATH/. >> TEST_TARG_PATH/mt_launch.log 2>> TEST_TARG_PATH/mt_launch.log
# or for hard links
# testcopycmd cp --remove-destination -rlv TEST_SRC_PATH/. TEST_TARG_PATH/.
# FULL or 2, NORMAL or 1, OFF or 0
synchronous OFF
# Throttle roughly scales the db access milliseconds to seconds delay
throttle 0.2
# Max retries allows megatest to re-check that a tests status has changed
# as tests can have transient FAIL status occasionally
maxretries 20
# Setup continued.
[setup]
# override the logview command
#
logviewer (%MTCMD%) 2> /dev/null > /dev/null
# override the html viewer launch command
#
# htmlviewercmd firefox -new-window
htmlviewercmd konqueror
[validvalues]
state start end 0 1 - 2
status pass fail n/a 0 1 running - 2
# These are set before all tests, override them
# in the testconfig [pre-launch-env-overrides] section
[env-override]
# MT_XTERM_CMD overrides the terminal command
# MT_XTERM_CMD xterm -bg lightgreen -fg black
SPECIAL_ENV_VARS overide them here - should be seen at launch and in the runs
TESTVAR [system realpath .]
TESTVAR [system echo $PWD]
DEADVAR [system ls]
VARWITHDOLLAR $HOME/.zshrc
WACKYVAR #{system ls > /dev/null}
WACKYVAR2 #{get validvalues state}
WACKYVAR3 #{getenv USER}
WACKYVAR4 #{scheme (+ 5 6 7)}
WACKYVAR5 #{getenv sysname}/#{getenv fsname}/#{getenv datapath}
WACKYVAR6 #{scheme (args:get-arg "-target")}
PREDICTABLE the_ans
MRAH MT_RUN_AREA_HOME=#{getenv MT_RUN_AREA_HOME}
# The empty var should have a definition with null string
EMPTY_VAR
WRAPPEDVAR This var should have the work blah thrice: \
blah \
blah
# Set MAX_ALLOWED_LOAD for nbload. 150 percent is a good value.
MAX_ALLOWED_LOAD 150
# XTERM [system xterm]
# RUNDEAD [system exit 56]
[server]
# If the server can't be started on this port it will try the next port until
## -or-
## name /path/to/area
[disks]
disk0 /foobarbazz
[include config/mt_include_2.config]
[include #{getenv USER}_testing.config]
[jobgroups]
# NOTE: job groups will falsely count the toplevel test as a job. If possible add N
# to your jobgroups where N is the number of parallel runs you are likely to see
#
sqlite3 5
blockz 5
[default]
SOMEVAR This should show up in SOMEVAR3
# target based getting of config file, look at afs.config and nfs.config
[include #{getenv fsname}.config]
[include #{getenv MT_RUN_AREA_HOME}/common_runconfigs.config]
# #{system echo 'VACKYVAR #{shell pwd}' > $MT_RUN_AREA_HOME/config/$USER.config}
[include ./config/#{getenv USER}.config]
WACKYVAR0 #{get ubuntu/nfs/none CURRENT}
WACKYVAR1 #{scheme (args:get-arg "-target")}
[default/ubuntu/nfs]
WACKYVAR2 #{runconfigs-get CURRENT}
[ubuntu/nfs/none]
WACKYVAR2 #{runconfigs-get CURRENT}
SOMEVAR2 This should show up in SOMEVAR4 if the target is ubuntu/nfs/none
[default]
SOMEVAR3 #{rget SOMEVAR}
SOMEVAR4 #{rget SOMEVAR2}
SOMEVAR5 #{runconfigs-get SOMEVAR2}
[this/a/test]
BLAHFOO 123
#!/bin/bash
$MT_MEGATEST -test-status :state $THESTATE :status $THESTATUS -setlog "nada.html"
# By exiting with non-zero we tell Megatest to preseve the state and status
exit 1
[setup]
runscript main.sh
[items]
THESTATE UNKNOWN INCOMPLETE KILLED KILLREQ STUCK BOZZLEBLONKED STUCK/DEAD
THESTATUS PASS FAIL STUCK/DEAD SKIP
[requirements]
waiton sqlitespeed
[test_meta]
author matt
owner bob
description This test will fail causing the dependent test "testxz"\
to never run. This triggers the code that must determine\
that a test will never be run and thus remove it from\
the queue of tests to be run.
tags first,single
reviewed 1/1/1965
jobgroup blockz
[setup]
[ezsteps]
exit2 exit 2
lookithome ls /home
[test_meta]
author matt
owner bob
description This test runs two steps; the first exits with
code 2 (a fail because not using logpro) and the second
is a pass
description This test runs two steps; the first exits with\
code 2 (a fail because not using logpro) and the second\
is a pass
tags first,single
reviewed 09/10/2011, by Matt
[requirements]
priority 10
[ezsteps]
# should fail on next step
lookitnada ls /nada
[triggers]
# run like this: cmd test-id test-rundir trigger
COMPLETED/FAIL xterm;echo
[test_meta]
author matt
owner bob
description This test runs a single ezstep which fails immediately.
tags first,single
reviewed 09/10/2011, by Matt
[setup]
[ezsteps]
firststep main.sh
[test_meta]
author matt
owner bob
description This test runs a single ezstep which is logpro clean
but fails based on -test-data loaded.
description This test runs a single ezstep which is logpro clean\
but fails based on -test-data loaded.
tags first,single
reviewed 09/10/2011, by Matt
[setup]
[ezsteps]
setup ./runsetupxterm.sh
# launch launchxterm
[test_meta]
author matt
owner bob
description This test runs a single ezstep which is expected to pass
using a simple logpro file.
description This test runs a single ezstep which is expected to pass\
using a simple logpro file.
tags first,single
reviewed 09/10/2011, by Matt
[ezsteps]
listfiles ls
[items]
FOO
[test_meta]
author matt
owner bob
description This test runs a single ezstep which is expected to pass \
but there is an items definition with no items. This should evoke an \
error.
tags first,single
reviewed 09/10/2011, by Matt
[setup]
runscript main.sh
[requirements]
priority 1
[test_meta]
jobgroup sqlite3
author matt
owner bob
description This test checks that a multi-lineitem test with mix of pass and non-fail rolls up a PASS
tags first,single
reviewed 09/10/2011, by Matt
[setup]
runscript main.sh
[requirements]
priority 2
# runtimelim 1d 1h 1m 10s
runtimelim 20s
[test_meta]
jobgroup sqlite3
author matt
owner bob
description This test checks that a multi-lineitem test with mix of pass and non-fail rolls up a PASS
tags first,single
reviewed 09/10/2011, by Matt
[setup]
runscript main.sh
[requirements]
priority 3
[test_meta]
jobgroup sqlite3
author matt
owner bob
description This test checks that a multi-lineitem test with mix of pass and non-fail rolls up a PASS
tags first,single
reviewed 09/10/2011, by Matt
[setup]
runscript main.sh
[requirements]
priority 4
[test_meta]
jobgroup sqlite3
author matt
owner bob
description This test checks that a multi-lineitem test with mix of pass and non-fail rolls up a PASS
tags first,single
reviewed 09/10/2011, by Matt
[setup]
runscript main.sh
[requirements]
priority 5
[skip]
prevrunning #t
[test_meta]
author matt
owner bob
description This test checks that a multi-lineitem test with mix of pass and non-fail rolls up a PASS
tags first,single
reviewed 09/10/2011, by Matt
[setup]
runscript main.sh
[pre-launch-env-vars]
# These are set before the test is launched on the originating
# host. This can be used to control remote launch tools, e.g. to
# to choose the target host, select the launch tool etc.
SPECIAL_ENV_VAR override with everything after the first space.
[items]
SEASON summer winter fall spring
SEASON summer winter fall spring
[itemstable]
BLOCK a b
TOCK 1 2
[test_meta]
author matt
owner bob
description This test must
description This test must\
be run before the other tests
tags first,single
reviewed 1/1/1965
#!/bin/bash
# megatest -step wasting_time :state start :status n/a -m "This is a test step comment"
# sleep 20
# megatest -step wasting_time :state end :status $?
$MT_MEGATEST -runstep wasting_time -logpro wasting_time.logpro "sleep 5;echo all done eh?" -m "This is a test step comment"
$MT_MEGATEST -runstep wasting_time -logpro wasting_time.logpro "sleep 5;echo alldone" -m "This is a test step comment"
$MT_MEGATEST -test-status :state COMPLETED :status $? -m "This is a test level comment" -set-toplog the_top_log.html :first_err "This is the first error"
[ezsteps]
# calcresults megatest -list-runs $MT_RUNNAME -target $MT_TARGET
[requirements]
waiton #{rget TESTSTORUN}
# This is a "toplevel" test, it does not require waitons to be non-FAIL to run
mode toplevel
#! /usr/bin/env ruby
require "#{ENV['MT_RUN_AREA_HOME']}/../supportfiles/ruby/librunscript.rb"
require "#{ENV['MT_RUN_AREA_HOME']}/../resources/ruby/librunscript.rb"
# run_record(stepname, cmd) - will record in db if exit code of script was zero or not
run_and_record('create db',"sqlite3 testing.db << EOF\ncreate table if not exists blah(id INTEGER PRIMARY KEY,name TEXT);\n.q\nEOF","")
if (! File.exists?("../../runfirst/I_was_here"))
puts "ERROR: This test was started before the prerequisites ran!"
system "megatest -test-status :state INCOMPLETE :status FAIL"
[setup]
runscript runscript.rb
tags non important,dumb junk
[requirements]
waiton runfirst
[items]
MANYITEMS [system (env > envfile.txt;echo aa ab ac ad ae af ag ah ai)]
# BORKED
[test_meta]
jobgroup sqlite3
# Add additional steps here. Format is "stepname script"
[ezsteps]
listfiles ls
# Test requirements are specified here
[requirements]
waiton blocktestxz
# test_meta is a section for storing additional data on your test
[test_meta]
author mrwellan
owner mrwellan
description This test should never get run due to blocktestxz failing
tags tagone,tagtwo
reviewed never
[ezsteps]
listfiles ls
[requirements]
waiton no_items
[items]
[test_meta]
author matt
owner bob
description This test runs a single ezstep which is expected to pass \
but there is an items definition with no items. This should evoke an \
error.
tags first,single
reviewed 09/10/2011, by Matt
[ezsteps]
listfiles ls
[requirements]
waiton wait_no_items1
[items]
[test_meta]
author matt
owner bob
description This test runs a single ezstep which is expected to pass \
but there is an items definition with no items. This should evoke an \
error.
tags first,single
reviewed 09/10/2011, by Matt
[ezsteps]
listfiles ls
[requirements]
waiton wait_no_items2
[items]
[test_meta]
author matt
owner bob
description This test runs a single ezstep which is expected to pass \
but there is an items definition with no items. This should evoke an \
error.
tags first,single
reviewed 09/10/2011, by Matt
[ezsteps]
listfiles ls
[requirements]
waiton wait_no_items3
[items]
[test_meta]
author matt
owner bob
description This test runs a single ezstep which is expected to pass \
but there is an items definition with no items. This should evoke an \
error.
tags first,single
reviewed 09/10/2011, by Matt
[.............]
#
# [CHICKEN_VERSION/MEGATEST_VERSION/IUPMODE/PLATFORM/BUILD_TAG]
#
[default]
ALLTESTS see this variable
PREFIX #{getenv MT_RUN_AREA_HOME}/#{getenv BUILD_TAG}/#{getenv MT_RUNNAME}
DOWNLOADS #{getenv MT_RUN_AREA_HOME}/downloads
IUPLIB 26g4
PLATFORM linux
LOGPRO_VERSION v1.05
BUILDSQLITE yes
SQLITE3_VERSION 3071401
ZEROMQ_VERSION 2.2.0
logpro_VERSION v1.08
stml_VERSION v0.901
megatest_VERSION v1.5511
[include configs/hicken-#{getenv CHICKEN_VERSION}.config]
# Currently must have at least one variable in a section
[4.8.0/trunk/bin/std]
IUP_VERSION na
[4.8.0.4/trunk/src/std]
CHICKEN_URL http://code.call-cc.org/releases/4.8.0/chicken-4.8.0.4.tar.gz
IUP_VERSION na
[4.8.1/trunk/src/std]
IUP_VERSION na
[4.8.0/v1.5508/opt]
IUP_VERSION na
PREFIX /opt/chicken/4.8.0
[4.8.0/trunk/centos5.7vm]
BUILDSQLITE no
;; You should have at least one expect:required. This ensures that your process ran
(expect:required in "LogFileBody" > 0 "Put description here" #/put pattern here/)
;; You may need ignores to suppress false error or warning hits from the later expects
;; NOTE: Order is important here!
(expect:ignore in "LogFileBody" < 99 "Ignore the word error in comments" #/^\/\/.*error/)
(expect:warning in "LogFileBody" = 0 "Any warning" #/warn/)
(expect:error in "LogFileBody" = 0 "Any error" (list #/ERROR/ #/error/i)) ;; but disallow any other errors
# Add additional steps here. Format is "stepname script"
[ezsteps]
install install.sh
# Test requirements are specified here
[requirements]
waiton iuplib setup
# Iteration for your tests are controlled by the items section
[items]
# test_meta is a section for storing additional data on your test
[test_meta]
author matt
owner matt
description Install the canvas-draw egg
tags tagone,tagtwo
reviewed never
;; You should have at least one expect:required. This ensures that your process ran
(expect:required in "LogFileBody" > 0 "Leaving directory ..." #/Leaving directory/)
;; You may need ignores to suppress false error or warning hits from the later expects
;; NOTE: Order is important here!
(expect:ignore in "LogFileBody" < 99 "Ignore the word error in comments" #/^\/\/.*error/)
(expect:ignore in "LogFileBody" >= 0 "Ignore HAVE_STRERROR" #/HAVE_STRERROR/)
(expect:warning in "LogFileBody" = 0 "Any warning" #/warn/)
(expect:error in "LogFileBody" = 0 "Any error" (list #/ERROR/ #/error/i)) ;; but disallow any other errors
;; You should have at least one expect:required. This ensures that your process ran
(expect:required in "LogFileBody" > 0 "README file must be seen" #/README$/)
;; You may need ignores to suppress false error or warning hits from the later expects
;; NOTE: Order is important here!
(expect:ignore in "LogFileBody" >= 0 "Ignore error flagged by finalizer-error-test" #/\w+-error/)
(expect:ignore in "LogFileBody" < 99 "Ignore the word error in comments" #/^\/\/.*error/)
(expect:warning in "LogFileBody" = 0 "Any warning" #/warn/)
(expect:error in "LogFileBody" = 0 "Any error" (list #/ERROR/ #/error/i)) ;; but disallow any other errors
#!/usr/bin/env bash
# Run your step here
source $PREFIX/buildsetup.sh
if [ ! -e ${DOWNLOADS}/chicken-${CHICKEN_VERSION}.tar.gz ]; then
if [ "${CHICKEN_URL}" == "" ]; then
CHICKEN_URL=http://code.call-cc.org/releases/${CHICKEN_VERSION}/chicken-${CHICKEN_VERSION}.tar.gz
fi
echo "Downloading $CHICKEN_URL"
(cd ${DOWNLOADS};wget ${CHICKEN_URL})
fi
ls -l ${DOWNLOADS}/chicken-${CHICKEN_VERSION}.tar.gz
tar xfvz ${DOWNLOADS}/chicken-${CHICKEN_VERSION}.tar.gz
ls -l chicken-${CHICKEN_VERSION}
;; You should have at least one expect:required. This ensures that your process ran
(expect:required in "LogFileBody" > 0 "Leaving directory" #/Leaving directory/)
;; You may need ignores to suppress false error or warning hits from the later expects
;; NOTE: Order is important here!
(expect:ignore in "LogFileBody" >= 0 "Ignore error in some filenames" #/\w+-errors/)
(expect:ignore in "LogFileBody" < 99 "Ignore the word error in comments" #/^\/\/.*error/)
(expect:warning in "LogFileBody" = 0 "Any warning" #/warn/)
(expect:error in "LogFileBody" = 0 "Any error" (list #/ERROR/ #/error/i)) ;; but disallow any other errors
#!/usr/bin/env bash
# Run your step here
source $PREFIX/buildsetup.sh
# source $PREFIX
cd chicken-${CHICKEN_VERSION}
make PLATFORM=${PLATFORM} PREFIX=${PREFIX} install
ls -l ${PREFIX}/bin
# Add additional steps here. Format is "stepname script"
[ezsteps]
download download.sh
compile compile.sh
install install.sh
# Test requirements are specified here
[requirements]
waiton setup
# priority 10
# Iteration for your tests are controlled by the items section
[items]
# CHICKEN_VERSION 4.8.0
# test_meta is a section for storing additional data on your test
[test_meta]
author matt
owner matt
description Download and install chicken scheme
tags tagone,tagtwo
reviewed never
;; You should have at least one expect:required. This ensures that your process ran
(expect:required in "LogFileBody" > 0 "Last thing done is chmod ..." #/chmod /)
;; You may need ignores to suppress false error or warning hits from the later expects
;; NOTE: Order is important here!
(expect:ignore in "LogFileBody" >= 0 "Ignore someword-errors" #/\w+-error/)
(expect:ignore in "LogFileBody" < 99 "Ignore the word error in comments" #/^\/\/.*error/)
(expect:warning in "LogFileBody" = 0 "Any warning" #/warn/)
(expect:error in "LogFileBody" = 0 "Any error" (list #/ERROR/ #/error/i)) ;; but disallow any other errors
# Add additional steps here. Format is "stepname script"
[ezsteps]
install install.sh
# Test requirements are specified here
[requirements]
waiton chicken setup
priority 9
# Iteration for your tests are controlled by the items section
[items]
EGG_NAME matchable readline apropos base64 regex-literals format regex-case test coops trace csv dot-locking posix-utils posix-extras directory-utils hostinfo tcp-server rpc csv-xml fmt json md5 ssax sxml-serializer sxml-modifications salmonella sql-de-lite postgresql
# test_meta is a section for storing additional data on your test
[test_meta]
author matt
owner matt
description Download and install eggs with no significant prerequisites
tags tagone,tagtwo
reviewed never
;; You should have at least one expect:required. This ensures that your process ran
(expect:required in "LogFileBody" > 0 "Leaving directory" #/Leaving directory/)
;; You may need ignores to suppress false error or warning hits from the later expects
;; NOTE: Order is important here!
(expect:ignore in "LogFileBody" < 99 "Ignore the word error in comments" #/^\/\/.*error/)
(expect:warning in "LogFileBody" = 0 "Any warning" #/warn/)
(expect:error in "LogFileBody" = 0 "Any error" (list #/ERROR/ #/error/i)) ;; but disallow any other errors
;; You should have at least one expect:required. This ensures that your process ran
(expect:required in "LogFileBody" > 0 "VERSION" #/ VERSION/)
;; You may need ignores to suppress false error or warning hits from the later expects
;; NOTE: Order is important here!
(expect:ignore in "LogFileBody" < 99 "Ignore the word error in comments" #/^\/\/.*error/)
(expect:warning in "LogFileBody" = 0 "Any warning" #/warn/)
(expect:error in "LogFileBody" = 0 "Any error" (list #/ERROR/ #/error/i)) ;; but disallow any other errors
#!/usr/bin/env bash
# Run your step here
source $PREFIX/buildsetup.sh
if ! [[ -e ${DOWNLOADS}/ffcall.tar.gz ]] ; then
(cd ${DOWNLOADS};wget http://www.kiatoa.com/matt/iup/ffcall.tar.gz )
fi
tar xfvz ${DOWNLOADS}/ffcall.tar.gz
ls -l ffcall
;; You should have at least one expect:required. This ensures that your process ran
(expect:required in "LogFileBody" > 0 "Leaving directory" #/Leaving directory/)
;; You may need ignores to suppress false error or warning hits from the later expects
;; NOTE: Order is important here!
(expect:ignore in "LogFileBody" < 99 "Ignore the word error in comments" #/^\/\/.*error/)
(expect:warning in "LogFileBody" = 0 "Any warning" #/warn/)
(expect:error in "LogFileBody" = 0 "Any error" (list #/ERROR/ #/error/i)) ;; but disallow any other errors
# Add additional steps here. Format is "stepname script"
[ezsteps]
download download.sh
compile compile.sh
install install.sh
# Test requirements are specified here
[requirements]
waiton setup
# Iteration for your tests are controlled by the items section
[items]
# test_meta is a section for storing additional data on your test
[test_meta]
author matt
owner matt
description Install the ffcall library
tags tagone,tagtwo
reviewed never
;; You should have at least one expect:required. This ensures that your process ran
(expect:required in "LogFileBody" > 0 "chmod is roughly last thing that happens" #/chmod /)
;; You may need ignores to suppress false error or warning hits from the later expects
;; NOTE: Order is important here!
(expect:ignore in "LogFileBody" >= 0 "Ignore setup-error-handling" #/\w+-error/)
(expect:ignore in "LogFileBody" < 99 "Ignore the word error in comments" #/^\/\/.*error/)
(expect:warning in "LogFileBody" = 0 "Any warning" #/warn/)
(expect:error in "LogFileBody" = 0 "Any error" (list #/ERROR/ #/error/i)) ;; but disallow any other errors
# Add additional steps here. Format is "stepname script"
[ezsteps]
install install.sh
# Test requirements are specified here
[requirements]
waiton iup#{getenv IUPMODE}lib tougheggs
# Iteration for your tests are controlled by the items section
[items]
# test_meta is a section for storing additional data on your test
[test_meta]
author matt
owner matt
description Install iup egg
tags tagone,tagtwo
reviewed never
;; You should have at least one expect:required. This ensures that your process ran
(expect:required in "LogFileBody" > 0 "Put description here" #/put pattern here/)
;; You may need ignores to suppress false error or warning hits from the later expects
;; NOTE: Order is important here!
(expect:ignore in "LogFileBody" < 99 "Ignore the word error in comments" #/^\/\/.*error/)
(expect:warning in "LogFileBody" = 0 "Any warning" #/warn/)
(expect:error in "LogFileBody" = 0 "Any error" (list #/ERROR/ #/error/i)) ;; but disallow any other errors
;; You should have at least one expect:required. This ensures that your process ran
(expect:required in "LogFileBody" > 0 "README file should show up" #/README/)
;; You may need ignores to suppress false error or warning hits from the later expects
;; NOTE: Order is important here!
(expect:ignore in "LogFileBody" < 99 "Ignore the word error in comments" #/^\/\/.*error/)
(expect:warning in "LogFileBody" = 0 "Any warning" #/warn/)
(expect:error in "LogFileBody" = 0 "Any error" (list #/ERROR/ #/error/i)) ;; but disallow any other errors
#!/usr/bin/env bash
# Run your step here
source $PREFIX/buildsetup.sh
# source $PREFIX/setup-chicken4x.sh
if [[ `uname -a | grep x86_64` == "" ]]; then
export ARCHSIZE=''
else
export ARCHSIZE=64_
fi
# export files="cd-5.4.1_Linux${IUPLIB}_lib.tar.gz im-3.6.3_Linux${IUPLIB}_lib.tar.gz iup-3.5_Linux${IUPLIB}_lib.tar.gz"
if [[ x$USEOLDIUP == "x" ]];then
export files="cd-5.5.1_Linux${IUPLIB}_${ARCHSIZE}lib.tar.gz im-3.8_Linux${IUPLIB}_${ARCHSIZE}lib.tar.gz iup-3.6_Linux${IUPLIB}_${ARCHSIZE}lib.tar.gz"
else
echo WARNING: Using old IUP libraries
export files="cd-5.4.1_Linux${IUPLIB}_${ARCHSIZE}lib.tar.gz im-3.6.3_Linux${IUPLIB}_${ARCHSIZE}lib.tar.gz iup-3.5_Linux${IUPLIB}_${ARCHSIZE}lib.tar.gz"
fi
mkdir -p $PREFIX/iuplib
for a in `echo $files` ; do
if ! [[ -e ${DOWNLOADS}/$a ]] ; then
(cd ${DOWNLOADS};wget http://www.kiatoa.com/matt/iup/$a)
fi
echo Untarring $a into $PREFIX/lib
(cd $PREFIX/lib;tar xfvz ${DOWNLOADS}/$a;mv include/* ../include)
done
;; You should have at least one expect:required. This ensures that your process ran
(expect:required in "LogFileBody" > 0 "Put description here" #/put pattern here/)
;; You may need ignores to suppress false error or warning hits from the later expects
;; NOTE: Order is important here!
(expect:ignore in "LogFileBody" < 99 "Ignore the word error in comments" #/^\/\/.*error/)
(expect:warning in "LogFileBody" = 0 "Any warning" #/warn/)
(expect:error in "LogFileBody" = 0 "Any error" (list #/ERROR/ #/error/i)) ;; but disallow any other errors
# Add additional steps here. Format is "stepname script"
[ezsteps]
download download.sh
# Test requirements are specified here
[requirements]
waiton ffcall setup
# Iteration for your tests are controlled by the items section
[items]
# test_meta is a section for storing additional data on your test
[test_meta]
author matt
owner matt
description Install the iup library if it is not already installed
tags tagone,tagtwo
reviewed never
;; You should have at least one expect:required. This ensures that your process ran
(expect:required in "LogFileBody" > 0 "Put description here" #/put pattern here/)
;; You may need ignores to suppress false error or warning hits from the later expects
;; NOTE: Order is important here!
(expect:ignore in "LogFileBody" < 99 "Ignore the word error in comments" #/^\/\/.*error/)
(expect:warning in "LogFileBody" = 0 "Any warning" #/warn/)
(expect:error in "LogFileBody" = 0 "Any error" (list #/ERROR/ #/error/i)) ;; but disallow any other errors
;; You should have at least one expect:required. This ensures that your process ran
(expect:required in "LogFileBody" > 0 "Completed signature" #/(Dynamic Library.*Done|Leaving directory|Nothing to be done)/)
;; You may need ignores to suppress false error or warning hits from the later expects
;; NOTE: Order is important here!
(expect:ignore in "LogFileBody" >= 0 "Ignore files with error in name" #/error.[ch]/)
(expect:ignore in "LogFileBody" >= 0 "Ignore files with errors in name" #/errors.[ch]/)
(expect:ignore in "LogFileBody" >= 0 "Ignore files with warn in name" #/warning.[ch]/)
(expect:ignore in "LogFileBody" < 99 "Ignore the word error in comments" #/^\/\/.*error/)
(expect:warning in "LogFileBody" = 0 "Any warning" #/warn/)
(expect:error in "LogFileBody" = 0 "Any error" (list #/ERROR/ #/error/i)) ;; but disallow any other errors
#!/usr/bin/env bash
# Run your step here
pkg=$1
source $PREFIX/buildsetup.sh
export LUA_SUFFIX=
export LUA_INC=$MT_TEST_RUN_DIR/lua52/include
if [[ $pkg == "lua52" ]]; then
(cd $pkg/src;make $PLATFORM)
else
(cd $pkg/src;make)
fi
;; You should have at least one expect:required. This ensures that your process ran
(expect:required in "LogFileBody" > 0 "README file should show up" #/README/)
;; You may need ignores to suppress false error or warning hits from the later expects
;; NOTE: Order is important here!
(expect:ignore in "LogFileBody" >= 0 "Ignore files with error in name" #/error.[ch]/)
(expect:ignore in "LogFileBody" >= 0 "Ignore files with errors in name" #/errors.[ch]/)
(expect:ignore in "LogFileBody" >= 0 "Ignore files with warn in name" #/warning.[ch]/)
(expect:ignore in "LogFileBody" < 99 "Ignore the word error in comments" #/^\/\/.*error/)
(expect:warning in "LogFileBody" = 0 "Any warning" #/warn/)
(expect:error in "LogFileBody" = 0 "Any error" (list #/ERROR/ #/error/i)) ;; but disallow any other errors
#!/usr/bin/env bash
# Run your step here
source $PREFIX/buildsetup.sh
mkdir -p $PREFIX/iuplib
for a in cd-5.6.1_Sources.tar.gz im-3.8.1_Sources.tar.gz iup-3.8_Sources.tar.gz lua-5.2.1_Sources.tar.gz; do
if ! [[ -e ${DOWNLOADS}/$a ]] ; then
(cd ${DOWNLOADS};wget http://www.kiatoa.com/matt/iup/$a)
fi
tar xfvz ${DOWNLOADS}/$a
done
find . -type d -exec chmod ug+x {} \;
;; You should have at least one expect:required. This ensures that your process ran
(expect:required in "LogFileBody" > 0 "Put description here" #/put pattern here/)
;; You may need ignores to suppress false error or warning hits from the later expects
;; NOTE: Order is important here!
(expect:ignore in "LogFileBody" < 99 "Ignore the word error in comments" #/^\/\/.*error/)
(expect:warning in "LogFileBody" = 0 "Any warning" #/warn/)
(expect:error in "LogFileBody" = 0 "Any error" (list #/ERROR/ #/error/i)) ;; but disallow any other errors
# Add additional steps here. Format is "stepname script"
[ezsteps]
download download.sh
lua compile.sh lua52
im compile.sh im
cd compile.sh cd
iup compile.sh iup
# Test requirements are specified here
[requirements]
waiton ffcall setup
# Iteration for your tests are controlled by the items section
[items]
# test_meta is a section for storing additional data on your test
[test_meta]
author matt
owner matt
description Install the iup library if it is not already installed
tags tagone,tagtwo
reviewed never
;; You should have at least one expect:required. This ensures that your process ran
(expect:required in "LogFileBody" > 0 "Put description here" #/put pattern here/)
;; You may need ignores to suppress false error or warning hits from the later expects
;; NOTE: Order is important here!
(expect:ignore in "LogFileBody" < 99 "Ignore the word error in comments" #/^\/\/.*error/)
(expect:warning in "LogFileBody" = 0 "Any warning" #/warn/)
(expect:error in "LogFileBody" = 0 "Any error" (list #/ERROR/ #/error/i)) ;; but disallow any other errors
;; You should have at least one expect:required. This ensures that your process ran
(expect:required in "LogFileBody" > 0 "Output from fossil" #/^repository:\s+/)
;; You may need ignores to suppress false error or warning hits from the later expects
;; NOTE: Order is important here!
(expect:ignore in "LogFileBody" < 99 "Ignore the word error in comments" #/^\/\/.*error/)
(expect:warning in "LogFileBody" = 0 "Any warning" #/warn/i)
(expect:error in "LogFileBody" = 0 "Any error" (list #/ERROR/ #/error/i)) ;; but disallow any other errors
#!/usr/bin/env bash
# Run your step here
source $PREFIX/buildsetup.sh
fossil clone http://www.kiatoa.com/fossils/$FSLPKG $FSLPKG.fossil
mkdir src
cd src
fossil open ../$FSLPKG.fossil --nested
fossil co ${$FSLPKG}_VERSION}
;; You should have at least one expect:required. This ensures that your process ran
(expect:required in "LogFileBody" > 0 "Always get a chmod at the end of install" #/chmod.*logpro.setup-info/)
;; You may need ignores to suppress false error or warning hits from the later expects
;; NOTE: Order is important here!
(expect:ignore in "LogFileBody" < 99 "Ignore the word error in comments" #/^\/\/.*error/)
(expect:ignore in "LogFileBody" < 99 "Ignore the word error in setup-error-handling" #/setup-error-handling/)
(expect:warning in "LogFileBody" = 0 "Any warning" #/warn/i)
(expect:error in "LogFileBody" = 0 "Any error" (list #/ERROR/ #/error/i)) ;; but disallow any other errors
#!/usr/bin/env bash
# Run your step here
source $PREFIX/buildsetup.sh
cd src
if [ $FSLPKG == "logpro" ];then
chicken-install
elif [ $FSLPKG == "stml" ];then
cp install.cfg.template install.cfg
cp requirements.scm.template requirements.scm
make
make install
else
make
make install PREFIX=$PREFIX
fi
# Add additional steps here. Format is "stepname script"
[ezsteps]
clone clone.sh
install install.sh
# Test requirements are specified here
[requirements]
waiton eggs setup
# Iteration for your tests are controlled by the items section
[items]
FSLPKG logpro stml megatest
# test_meta is a section for storing additional data on your test
[test_meta]
author matt
owner matt
description Install the logpro tool
tags tagone,tagtwo
reviewed never
;; You should have at least one expect:required. This ensures that your process ran
(expect:required in "LogFileBody" > 0 "Output from fossil" (list #/^repository:\s+/ #/comment:/))
;; You may need ignores to suppress false error or warning hits from the later expects
;; NOTE: Order is important here!
(expect:ignore in "LogFileBody" < 99 "Ignore the word error in comments" #/^\/\/.*error/)
(expect:warning in "LogFileBody" = 0 "Any warning" #/warn/i)
(expect:error in "LogFileBody" = 0 "Any error" (list #/ERROR/ #/error/i)) ;; but disallow any other errors
;; You should have at least one expect:required. This ensures that your process ran
(expect:required in "LogFileBody" > 0 "Always get a chmod at the end of install" #/chmod.*.setup-info/)
;; You may need ignores to suppress false error or warning hits from the later expects
;; NOTE: Order is important here!
(expect:ignore in "LogFileBody" < 99 "Ignore the word error in comments" #/^\/\/.*error/)
(expect:ignore in "LogFileBody" < 99 "Ignore the word error in setup-error-handling" #/setup-error-handling/)
(expect:warning in "LogFileBody" = 0 "Any warning" #/warn/i)
(expect:error in "LogFileBody" = 0 "Any error" (list #/ERROR/ #/error/i)) ;; but disallow any other errors
# Add additional steps here. Format is "stepname script"
[ezsteps]
clone clone.sh
install install.sh
# Test requirements are specified here
[requirements]
waiton eggs setup sqlite3
# Iteration for your tests are controlled by the items section
[items]
MODULE_NAME dbi margs qtree vcd xfig mutils
# test_meta is a section for storing additional data on your test
[test_meta]
author matt
owner matt
description Install the eggs from the opensrc fossil
tags tagone,tagtwo
reviewed never
;; You should have at least one expect:required. This ensures that your process ran
(expect:required in "LogFileBody" > 0 "ALL DONE" #/ALL DONE$/)
;; You may need ignores to suppress false error or warning hits from the later expects
;; NOTE: Order is important here!
(expect:ignore in "LogFileBody" < 99 "Ignore the word error in comments" #/^\/\/.*error/)
(expect:warning in "LogFileBody" = 0 "Any warning" #/warn/)
(expect:error in "LogFileBody" = 0 "Any error" (list #/ERROR/ #/error/i)) ;; but disallow any other errors
#!/usr/bin/env bash
# Run your step here
cksetupsh=$PREFIX/setup-chicken4x.sh
cksetupcsh=$PREFIX/setup-chicken4x.csh
setupsh=$PREFIX/buildsetup.sh
# make a cache dir
mkdir -p $DOWNLOADS
mkdir -p $PREFIX
# File for users to source to run chicken
echo "# Source me to setup to to run chicken" > $cksetupsh
echo "export PATH=$PREFIX/bin:\$PATH" > $cksetupsh
echo "export LD_LIBRARY_PATH=$PREFIX/lib" >> $cksetupsh
# tcsh version
echo "setenv PATH $PREFIX/bin:\$PATH" > $cksetupcsh
echo "setenv LD_LIBRARY_PATH $PREFIX/lib" >> $cksetupcsh
# File to source for build process
echo "export PATH=$PREFIX/bin:\$PATH" > $setupsh
echo "export LD_LIBRARY_PATH=$PREFIX/lib" >> $setupsh
if [[ $proxy == "" ]]; then
echo 'Please set the environment variable "proxy" to host.com:port (e.g. foo.com:1234) to use a proxy'
else
echo "export http_proxy=http://$proxy" >> $setupsh
echo "export PROX=\"-proxy $proxy\"" >> $setupsh
fi
echo "export PREFIX=$PREFIX" >> $setupsh
echo ALL DONE
# Add additional steps here. Format is "stepname script"
[ezsteps]
setup setup.sh
# Test requirements are specified here
[requirements]
# priority 10
# Iteration for your tests are controlled by the items section
[items]
# test_meta is a section for storing additional data on your test
[test_meta]
author matt
owner matt
description Download and install chicken scheme
tags tagone,tagtwo
reviewed never
;; You should have at least one expect:required. This ensures that your process ran
(expect:required in "LogFileBody" > 0 "Leaving directory" #/(Leaving directory|Nothing to be done for|creating sqlite3)/)
;; You may need ignores to suppress false error or warning hits from the later expects
;; NOTE: Order is important here!
(expect:ignore in "LogFileBody" >= 0 "Ignore strerror_r" #/strerror_r/i)
(expect:ignore in "LogFileBody" < 99 "Ignore the word error in comments" #/^\/\/.*error/)
(expect:warning in "LogFileBody" = 0 "Any warning" #/warn/)
(expect:error in "LogFileBody" = 0 "Any error" (list #/ERROR/ #/error/i)) ;; but disallow any other errors
;; You should have at least one expect:required. This ensures that your process ran
(expect:required in "LogFileBody" > 0 "sqlite-autoconf" #/sqlite-autoconf/)
;; You may need ignores to suppress false error or warning hits from the later expects
;; NOTE: Order is important here!
(expect:ignore in "LogFileBody" < 99 "Ignore the word error in comments" #/^\/\/.*error/)
(expect:warning in "LogFileBody" = 0 "Any warning" #/warn/)
(expect:error in "LogFileBody" = 0 "Any error" (list #/ERROR/ #/error/i)) ;; but disallow any other errors
#!/usr/bin/env bash
# Run your step here
source $PREFIX/buildsetup.sh
echo Install sqlite3
if ! [[ -e ${DOWNLOADS}/sqlite-autoconf-${SQLITE3_VERSION}.tar.gz ]]; then
(cd ${DOWNLOADS};wget http://www.sqlite.org/sqlite-autoconf-${SQLITE3_VERSION}.tar.gz)
fi
tar xfz ${DOWNLOADS}/sqlite-autoconf-${SQLITE3_VERSION}.tar.gz
ls -l sqlite-autoconf-${SQLITE3_VERSION}.tar.gz
;; You should have at least one expect:required. This ensures that your process ran
(expect:required in "LogFileBody" > 0 "Leaving directory" #/Leaving directory/)
;; You may need ignores to suppress false error or warning hits from the later expects
;; NOTE: Order is important here!
(expect:ignore in "LogFileBody" < 99 "Ignore the word error in comments" #/^\/\/.*error/)
(expect:warning in "LogFileBody" = 0 "Any warning" #/warn/)
(expect:error in "LogFileBody" = 0 "Any error" (list #/ERROR/ #/error/i)) ;; but disallow any other errors
;; You should have at least one expect:required. This ensures that your process ran
(expect:required in "LogFileBody" > 0 "chmod sqlite3" #/chmod.*sqlite3/)
;; You may need ignores to suppress false error or warning hits from the later expects
;; NOTE: Order is important here!
(expect:ignore in "LogFileBody" >= 0 "Ignore setup-error-handling" #/setup-error-handling/)
(expect:ignore in "LogFileBody" < 99 "Ignore the word error in comments" #/^\/\/.*error/)
(expect:warning in "LogFileBody" = 0 "Any warning" #/warn/)
(expect:error in "LogFileBody" = 0 "Any error" (list #/ERROR/ #/error/i)) ;; but disallow any other errors
#!/usr/bin/env bash
# Run your step here
source $PREFIX/buildsetup.sh
CSC_OPTIONS="-I$PREFIX/include -L$PREFIX/lib" $PREFIX/bin/chicken-install $PROX sqlite3
# Add additional steps here. Format is "stepname script"
[ezsteps]
download download.sh
compile compile.sh
install install.sh
installegg installegg.sh
# Test requirements are specified here
[requirements]
# We waiton chicken because this one installs the egg. It would behove us to split this
# into two tests ...
waiton tougheggs
priority 2
# Iteration for your tests are controlled by the items section
[items]
# test_meta is a section for storing additional data on your test
[test_meta]
author matt
owner matt
description Install sqlite3 library for systems where it is not installed
tags tagone,tagtwo
reviewed never
;; You should have at least one expect:required. This ensures that your process ran
(expect:required in "LogFileBody" > 0 "Last thing done is chmod ..." #/chmod /)
;; You may need ignores to suppress false error or warning hits from the later expects
;; NOTE: Order is important here!
(expect:ignore in "LogFileBody" >= 0 "Ignore someword-errors" #/\w+-error/)
(expect:ignore in "LogFileBody" < 99 "Ignore the word error in comments" #/^\/\/.*error/)
(expect:warning in "LogFileBody" = 0 "Any warning" #/warn/)
(expect:error in "LogFileBody" = 0 "Any error" (list #/ERROR/ #/error/i)) ;; but disallow any other errors
# Add additional steps here. Format is "stepname script"
[ezsteps]
install install.sh
# Test requirements are specified here
[requirements]
waiton eggs
# Iteration for your tests are controlled by the items section
[items]
EGG_NAME intarweb http-client awful uri-common spiffy-request-vars spiffy apropos spiffy-directory-listing
# test_meta is a section for storing additional data on your test
[test_meta]
author matt
owner matt
description Download and install eggs with no significant prerequisites
tags tagone,tagtwo
reviewed never
;; You should have at least one expect:required. This ensures that your process ran
(expect:required in "LogFileBody" > 0 "Put description here" #/put pattern here/)
;; You may need ignores to suppress false error or warning hits from the later expects
;; NOTE: Order is important here!
(expect:ignore in "LogFileBody" < 99 "Ignore the word error in comments" #/^\/\/.*error/)
(expect:warning in "LogFileBody" = 0 "Any warning" #/warn/)
(expect:error in "LogFileBody" = 0 "Any error" (list #/ERROR/ #/error/i)) ;; but disallow any other errors
# Add additional steps here. Format is "stepname script"
[ezsteps]
install install.sh
# Test requirements are specified here
[requirements]
waiton zmqlib chicken setup
# Iteration for your tests are controlled by the items section
[items]
# test_meta is a section for storing additional data on your test
[test_meta]
author matt
owner matt
description Install the zmq egg
tags tagone,tagtwo
reviewed never
;; You should have at least one expect:required. This ensures that your process ran
(expect:required in "LogFileBody" > 0 "Put description here" #/put pattern here/)
;; You may need ignores to suppress false error or warning hits from the later expects
;; NOTE: Order is important here!
(expect:ignore in "LogFileBody" < 99 "Ignore the word error in comments" #/^\/\/.*error/)
(expect:warning in "LogFileBody" = 0 "Any warning" #/warn/)
(expect:error in "LogFileBody" = 0 "Any error" (list #/ERROR/ #/error/i)) ;; but disallow any other errors
;; You should have at least one expect:required. This ensures that your process ran
(expect:required in "LogFileBody" > 0 "Put description here" #/put pattern here/)
;; You may need ignores to suppress false error or warning hits from the later expects
;; NOTE: Order is important here!
(expect:ignore in "LogFileBody" < 99 "Ignore the word error in comments" #/^\/\/.*error/)
(expect:warning in "LogFileBody" = 0 "Any warning" #/warn/)
(expect:error in "LogFileBody" = 0 "Any error" (list #/ERROR/ #/error/i)) ;; but disallow any other errors
;; You should have at least one expect:required. This ensures that your process ran
(expect:required in "LogFileBody" > 0 "Put description here" #/put pattern here/)
;; You may need ignores to suppress false error or warning hits from the later expects
;; NOTE: Order is important here!
(expect:ignore in "LogFileBody" < 99 "Ignore the word error in comments" #/^\/\/.*error/)
(expect:warning in "LogFileBody" = 0 "Any warning" #/warn/)
(expect:error in "LogFileBody" = 0 "Any error" (list #/ERROR/ #/error/i)) ;; but disallow any other errors
# Add additional steps here. Format is "stepname script"
[ezsteps]
download download.sh
untar untar.sh
compile compile.sh
install install.sh
# Test requirements are specified here
[requirements]
waiton setup
# Iteration for your tests are controlled by the items section
[items]
# test_meta is a section for storing additional data on your test
[test_meta]
author matt
owner matt
description Install the zmq library if it doesn't already exist
tags tagone,tagtwo
reviewed never
;; You should have at least one expect:required. This ensures that your process ran
(expect:required in "LogFileBody" > 0 "Put description here" #/put pattern here/)
;; You may need ignores to suppress false error or warning hits from the later expects
;; NOTE: Order is important here!
(expect:ignore in "LogFileBody" < 99 "Ignore the word error in comments" #/^\/\/.*error/)
(expect:warning in "LogFileBody" = 0 "Any warning" #/warn/)
(expect:error in "LogFileBody" = 0 "Any error" (list #/ERROR/ #/error/i)) ;; but disallow any other errors
# This is the library of stuff for megatest
def run_and_record(stepname, cmd, checks)
system "megatest -step #{stepname} :state start :status n/a"
system cmd
exitcode=$?
if exitcode==0
exitcode='pass'
else
exitcode='fail'
end
system "megatest -step #{stepname} :state end :status #{exitcode}"
end
def record_step(stepname,state,status)
system "megatest -step #{stepname} :state #{state} :status #{status}"
end
def test_status(state,status)
system "megatest -test-status :state #{state} :status #{status}"
end
# WARNING: This example is deprecated. Don't use the -test-status command
# unless you know for sure what you are doing.
def file_size_checker(stepname,filename,minsize,maxsize)
fsize=File.size(filename)
if fsize > maxsize or fsize < minsize
system "megatest -test-status :state COMPLETED :status fail"
else
system "megatest -test-status :state COMPLETED :status pass"
end
end
def wait_for_step(testname,stepname)
end
;; You should have at least one expect:required. This ensures that your process ran
(expect:required in "LogFileBody" > 0 "Put description here" #/put pattern here/)
;; (expect:required in "LogFileBody" > 0 "Put description here" #/put pattern here/)
;; You may need ignores to suppress false error or warning hits from the later expects
;; NOTE: Order is important here!
(expect:ignore in "LogFileBody" < 99 "Ignore the word error in comments" #/^\/\/.*error/)
(expect:warning in "LogFileBody" = 0 "Any warning" #/warn/)
(expect:error in "LogFileBody" = 0 "Any error" (list #/ERROR/ #/error/)) ;; but disallow any other errors
;; You should have at least one expect:required. This ensures that your process ran
(expect:required in "LogFileBody" > 0 "Put description here" #/put pattern here/)
;; (expect:required in "LogFileBody" > 0 "Put description here" #/put pattern here/)
;; You may need ignores to suppress false error or warning hits from the later expects
;; NOTE: Order is important here!
(expect:ignore in "LogFileBody" < 99 "Ignore the word error in comments" #/^\/\/.*error/)
(expect:warning in "LogFileBody" = 0 "Any warning" #/warn/)
(expect:error in "LogFileBody" = 0 "Any error" (list #/ERROR/ #/error/)) ;; but disallow any other errors
;; You should have at least one expect:required. This ensures that your process ran
(expect:required in "LogFileBody" > 0 "Put description here" #/put pattern here/)
;; (expect:required in "LogFileBody" > 0 "Put description here" #/put pattern here/)
;; You may need ignores to suppress false error or warning hits from the later expects
;; NOTE: Order is important here!
(expect:ignore in "LogFileBody" < 99 "Ignore the word error in comments" #/^\/\/.*error/)
(expect:warning in "LogFileBody" = 0 "Any warning" #/warn/)
(expect:error in "LogFileBody" = 0 "Any error" (list #/ERROR/ #/error/)) ;; but disallow any other errors
;; You should have at least one expect:required. This ensures that your process ran
(expect:required in "LogFileBody" > 0 "Put description here" #/put pattern here/)
;; (expect:required in "LogFileBody" > 0 "Put description here" #/put pattern here/)
;; You may need ignores to suppress false error or warning hits from the later expects
;; NOTE: Order is important here!
(expect:ignore in "LogFileBody" < 99 "Ignore the word error in comments" #/^\/\/.*error/)
(expect:warning in "LogFileBody" = 0 "Any warning" #/warn/)
(expect:error in "LogFileBody" = 0 "Any error" (list #/ERROR/ #/error/)) ;; but disallow any other errors
[fields]
sysname TEXT
fsname TEXT
datapath TEXT
[setup]
transport #{scheme (if (getenv "USEHTTP") "http" "fs")}
max_concurrent_jobs 50
# It is possible (but not recommended) to override the rsync command used
# to populate the test directories. For test development the following
# example can be useful
#
testcopycmd cp --remove-destination -rsv TEST_SRC_PATH/. TEST_TARG_PATH/. >> TEST_TARG_PATH/mt_launch.log 2>> TEST_TARG_PATH/mt_launch.log
# FULL or 2, NORMAL or 1, OFF or 0
synchronous OFF
# override the logview command
#
logviewer (%MTCMD%) 2> /dev/null > /dev/null
# override the html viewer launch command
#
# htmlviewercmd firefox -new-window
htmlviewercmd konqueror
[jobtools]
launcher nbfake
[server]
# If the server can't be started on this port it will try the next port until
# it succeeds
port 8080
# This server will keep running this number of hours after last access.
# Three minutes is 0.05 hours
timeout 0.025
## disks are:
## name host:/path/to/area
## -or-
## name /path/to/area
[disks]
disk0 #{getenv MT_RUN_AREA_HOME}/tmp_run
[setup]
runscript main.sh
[requirements]
priority 1
[items]
SETLOG 0 1
TEST_DELAY 0 1 2 3 4 5 6 7 8 9 10
ITERATIONS 0 1 2 3 4 5 6 7 8 9 10
[test_meta]
author matt
owner bob
description This test checks that a multi-lineitem test with mix of pass and non-fail rolls up a PASS
tags first,single
reviewed 09/10/2011, by Matt
;; You should have at least one expect:required. This ensures that your process ran
(expect:required in "LogFileBody" > 0 "All tests launched" #/INFO:.*All tests launched/)
;; You may need ignores to suppress false error or warning hits from the later expects
;; NOTE: Order is important here!
(expect:ignore in "LogFileBody" < 99 "Ignore the word error in comments" #/^\/\/.*error/)
(expect:warning in "LogFileBody" = 0 "Any warning" #/warn/)
(expect:error in "LogFileBody" = 0 "Any error" (list #/ERROR/ #/error/)) ;; but disallow any other errors
;;======================================================================
;; Copyright 2006-2013, Matthew Welland.
;;
;; This program is made available under the GNU GPL version 2.0 or
;; greater. See the accompanying file COPYING for details.
;;
;; This program is distributed WITHOUT ANY WARRANTY; without even the
;; implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
;; PURPOSE.
;;======================================================================
(use format)
(require-library iup)
(import (prefix iup iup:))
(use canvas-draw)
(use sqlite3 srfi-1 posix regex regex-case srfi-69)
(import (prefix sqlite3 sqlite3:))
(declare (unit tree))
(declare (uses margs))
(declare (uses launch))
(declare (uses megatest-version))
(declare (uses gutils))
(declare (uses db))
(declare (uses server))
(declare (uses synchash))
(declare (uses dcommon))
(include "common_records.scm")
(include "db_records.scm")
(include "key_records.scm")
;;======================================================================
;; T R E E S T U F F
;;======================================================================
;; path is a list of nodes, each the child of the previous
;; this routine returns the id so another node can be added
;; either as a leaf or as a branch
;;
;; BUG: This needs a stop sensor for when a branch is exhausted
;;
(define (tree:find-node obj path)
;; start at the base of the tree
(if (null? path)
#f ;; or 0 ????
(let loop ((hed (car path))
(tal (cdr path))
(depth 0)
(nodenum 0))
;; nodes in iup tree are 100% sequential so iterate over nodenum
(if (iup:attribute obj (conc "DEPTH" nodenum)) ;; end when no more nodes
(let ((node-depth (string->number (iup:attribute obj (conc "DEPTH" nodenum))))
(node-title (iup:attribute obj (conc "TITLE" nodenum))))
(if (and (equal? depth node-depth)
(equal? hed node-title)) ;; yep, this is the one!
(if (null? tal) ;; end of the line
nodenum
(loop (car tal)(cdr tal)(+ depth 1)(+ 1 nodenum)))
;; this is the case where we found part of the hierarchy but not
;; all of it, i.e. the node-depth went from deep to less deep
(if (> depth node-depth) ;; (+ 1 node-depth))
#f
(loop hed tal depth (+ nodenum 1)))))
#f))))
;; top is the top node name zeroeth node VALUE=0
(define (tree:add-node obj top nodelst #!key (userdata #f))
(if (not (iup:attribute obj "TITLE0"))
(iup:attribute-set! obj "ADDBRANCH0" top))
(cond
((not (string=? top (iup:attribute obj "TITLE0")))
(print "ERROR: top name " top " doesn't match " (iup:attribute obj "TITLE0")))
((null? nodelst))
(else
(let loop ((hed (car nodelst))
(tal (cdr nodelst))
(depth 1)
(pathl (list top)))
;; Because the tree dialog changes node numbers when
;; nodes are added or removed we must look up nodes
;; each and every time. 0 is the top node so default
;; to that.
(let* ((newpath (append pathl (list hed)))
(parentnode (tree:find-node obj pathl))
(nodenum (tree:find-node obj newpath)))
;; Add the branch under lastnode if not found
(if (not nodenum)
(begin
(iup:attribute-set! obj (conc "ADDBRANCH" parentnode) hed)
(if userdata
(iup:attribute-set! obj (conc "USERDATA" parentnode) userdata))
(if (null? tal)
#t
;; reset to top
(loop (car nodelst)(cdr nodelst) 1 (list top))))
(if (null? tal) ;; if null here then this path has already been added
#t
(loop (car tal)(cdr tal)(+ depth 1) newpath))))))))
(define (tree:node->path obj nodenum)
(let loop ((currnode 0)
(path '()))
(let* ((node-depth (string->number (iup:attribute obj (conc "DEPTH" currnode))))
(node-title (iup:attribute obj (conc "TITLE" currnode)))
(trimpath (if (and (not (null? path))
(> (length path) node-depth))
(take path node-depth)
path))
(newpath (append trimpath (list node-title))))
(if (>= currnode nodenum)
newpath
(loop (+ currnode 1)
newpath)))))
[Time]
BLANKVAL
A 0.324305555555556
B 0.33125
C 0.334722222222222
D 0.336805555555556
E 0.338888888888889
F 0.340972222222222
G 0.343055555555556
H 0.345833333333333
I 0.347916666666667
J 0.351388888888889
K 0.366666666666667
L 0.379166666666667
M 0.395833333333333
N 0.422222222222222
O 0.452083333333333
P 0.491666666666667
Q 0.570833333333333
[DeltaTime]
A 0
B =days(B3,$B$2)*24*60
C
D
E
F
G
H
I
J
K
L
M
N
O
P
Q
[Ambient]
A 35.4
B 35.4
C 35
D 35
E 35
F 35
G 35
H 36
I 36
J 37
K 37
L 38
M 39
N 40
O 41
P 41
Q 41.5
[Firebox]
A 34.3
B 72
C 100
D 130
E 145
F 150
G 150
H 158
I 156
J 152
K 134
L 117
M 100
N 91
O 79
P 68
Q 51
[2nd_row]
A 34.3
B 60
C 90
D 116
E 121
F 125
G 128
H 129
I 128
J 126
K 117
L 108
M 100
N 90
O 78
P 63
Q 51
[3rd_row]
A 34.1
B 42
C 57
D 69
E 73
F 78
G 82
H 86
I 87
J 89
K 94
L 96
M 93
N 88
O 77
P 64
Q 51
[4th_row]
A 34
B 39
C 46
D 52
E 54
F 56
G 60
H 62
I 65
J 67
K 77
L 82
M 82
N 81
O 72
P 62
Q 51
[Exit]
A 34
B 68
C 68
D 68
E 68
F 68
G 69
H 70
I 72
J 75
K 107
L 106
M 106
N 100
O 79
P 68
Q 51
[2]
V2 X
V6 Y
V8 Z
V12 E
V15 B
V17 +
[A1]
V8 Z
V17 =
# Just a test really
#
V1 X
V3 X
V5 Y
V7 Y
V10 Z
V11 E
V13 E
V14 B
V16 B
[3]
V2 John,
V6 Tom
V8 Fred
V17 ~
# a deeply held belief is a danger to sanity
#
V4 X
row-11 Z
row-18 B
# Copyright 2013, Matthew Welland.
#
# This program is made available under the GNU GPL version 2.0 or
# greater. See the accompanying file COPYING for details.
#
# This program is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
# PURPOSE.
help :
@echo You may need to do the following first:
@echo sudo apt-get install libreadline-dev
@echo sudo apt-get install libwebkitgtk-dev
@echo sudo apt-get install libmotif3 -OR- set KTYPE=26g4
@echo KTYPE can be 26, 26g4, or 32
@echo KTYPE=$KTYPE
@echo You are using PREFIX=$PREFIX
@echo You are using proxy="$(proxy)"
@echo If needed set proxy to host.dom:port
@echo
@echo "Set additional_libpath to help find gtk or other libraries, don't forget a leading :"
@echo ADDITIONAL_LIBPATH=$(ADDITIONAL_LIBPATH)
@echo
@echo To use previous IUP libraries set USEOLDIUP to yes
@echo USEOLDIUP=$(USEOLDIUP)
@echo
@echo To make all do: make all
# Put the installation here
ifeq ($(PREFIX),)
PREFIX=$(PWD)/target
endif
# Set this on the command line of your make call if needed: make PROXY=host.com:1234
PROXY=
# Select IUP library type
KTYPE=26g4
# Select version of chicken, sqlite3 etc
CHICKEN_VERSION=4.8.0
SQLITE3_VERSION=3071401
# Eggs to install (straightforward ones)
EGGS=matchable readline apropos base64 regex-literals format regex-case test coops trace csv \
dot-locking posix-utils posix-extras directory-utils hostinfo tcp-server rpc csv-xml fmt \
json md5 awful http-client spiffy uri-common intarweb spiffy-request-vars \
spiffy-directory-listing ssax sxml-serializer sxml-modifications
#
# Derived variables
#
ifeq ($(PROXY),)
PROX=
else
http_proxy=http://$(PROXY)
PROX="-proxy $(PROXY)"
endif
BUILDHOME=$(PWD)
PATH:=$(PREFIX)/bin:$(PATH)
LIBPATH=$(PREFIX)/lib$(ADDITIONAL_LIBPATH)
LD_LIBRARY_PATH=$(LIBPATH)
CHICKEN_INSTALL=$(PREFIX)/bin/chicken-install
CHICKEN_EGG_DIR=$(PREFIX)/lib/chicken/6
VPATH=$(CHICKEN_EGG_DIR):$(PWD)/eggflags
vpath %.so $(CHICKEN_EGG_DIR)
vpath %.flag eggflags
EGGSOFILES=$(addprefix $(CHICKEN_EGG_DIR)/,$(addsuffix .so,$(EGGS)))
EGGFLAGS=$(addprefix eggflags/,$(addsuffix .flag,$(EGGS)))
# Stuff needed for IUP
ISARCHX86_64=$(shell uname -a | grep x86_64)
ifeq ($(ISARCHX86_64),)
ARCHSIZE=
else
ARCHSIZE=64_
endif
IUPFILES=cd-5.5.1_Linux$(KTYPE)_$(ARCHSIZE)lib.tar.gz im-3.8_Linux$(KTYPE)_$(ARCHSIZE)lib.tar.gz iup-3.6_Linux$(KTYPE)_$(ARCHSIZE)lib.tar.gz
CSCLIBS=$(shell echo $(LD_LIBRARY_PATH) | sed 's/:/ -L/g')
CSC_OPTIONS=-I$(PREFIX)/include -L$(CSCLIBS)
all : chkn eggs libiup sqlite3 logprobin mutils
chkn : $(CHICKEN_INSTALL)
eggs : $(EGGSOFILES)
sqlite3 : $(CHICKEN_EGG_DIR)/sqlite3.so
libiup : $(PREFIX)/lib/libavcall.a $(CHICKEN_EGG_DIR)/iup.so $(CHICKEN_EGG_DIR)/canvas-draw.so
logprobin : $(PREFIX)/bin/logpro
$(PREFIX)/bin/logpro : $(CHICKEN_EGG_DIR)/regex-literals.so
$(CHICKEN_INSTALL) logpro
# Silly rule to make installing eggs more makeish, I don't understand why I need the basename
$(CHICKEN_EGG_DIR)/%.so : eggflags/%.flag
$(CHICKEN_INSTALL) $(PROX) $(shell basename $*)
$(EGGFLAGS) : # $(CHICKEN_INSTALL)
mkdir -p eggflags
touch $(EGGFLAGS)
# some setup stuff
#
setup-chicken4x.sh : $(EGGFLAGS)
(echo "export PATH=$(PATH)" > setup-chicken4x.sh)
(echo "export LD_LIBRARY_PATH=$(LD_LIBRARY_PATH)" >> setup-chicken4x.sh)
mkdir -p $(PREFIX)
# Download chicken source
chicken-$(CHICKEN_VERSION).tar.gz :
wget http://code.call-cc.org/releases/$(CHICKEN_VERSION)/chicken-$(CHICKEN_VERSION).tar.gz
# NB// Must touch csi.scm since tar puts original date on it and deps are wrong then
chicken-$(CHICKEN_VERSION)/csi.scm : chicken-$(CHICKEN_VERSION).tar.gz
tar xfvz chicken-$(CHICKEN_VERSION).tar.gz
touch -c chicken-$(CHICKEN_VERSION)/csi.scm
$(CHICKEN_INSTALL) : chicken-$(CHICKEN_VERSION)/csi.scm setup-chicken4x.sh
cd chicken-$(CHICKEN_VERSION);make PLATFORM=linux PREFIX=$(PREFIX)
cd chicken-$(CHICKEN_VERSION);make PLATFORM=linux PREFIX=$(PREFIX) install
sqlite-autoconf-$(SQLITE3_VERSION).tar.gz :
wget http://www.sqlite.org/sqlite-autoconf-$(SQLITE3_VERSION).tar.gz
sqlite-autoconf-$(SQLITE3_VERSION) : sqlite-autoconf-$(SQLITE3_VERSION).tar.gz
tar xfz sqlite-autoconf-$(SQLITE3_VERSION).tar.gz
$(PREFIX)/bin/sqlite3 : sqlite-autoconf-$(SQLITE3_VERSION)
(cd sqlite-autoconf-$(SQLITE3_VERSION);./configure --prefix=$(PREFIX);make;make install)
$(CHICKEN_EGG_DIR)/sqlite3.so : $(PREFIX)/bin/sqlite3
CSC_OPTIONS="-I$(PREFIX)/include -L$(PREFIX)/lib" $(CHICKEN_INSTALL) $(PROX) sqlite3
mutils : $(CHICKEN_EGG_DIR)/margs.so $(CHICKEN_EGG_DIR)/qtree.so # $(CHICKEN_EGG_DIR)/dbi.so
# Get and install my various utilities that haven't been eggified yet.
opensrc/margs/margs.scm opensrc/dbi/dbi.scm opensrc/qtree/qtree.scm : $(CHICKEN_INSTALL) $(CHICKEN_EGG_DIR)/sqlite3.so
mkdir -p opensrc
cd opensrc;if [ ! -e opensrc.fossil ]; then fossil clone http://www.kiatoa.com/fossils/opensrc opensrc.fossil; fi
cd opensrc;if [ -e dbi/dbi.scm ]; then fossil update; else fossil open opensrc.fossil; fi
$(CHICKEN_EGG_DIR)/dbi.so : opensrc/dbi/dbi.scm
cd opensrc/dbi;chicken-install
$(CHICKEN_EGG_DIR)/margs.so : opensrc/margs/margs.scm
cd opensrc/margs;chicken-install
$(CHICKEN_EGG_DIR)/qtree.so : opensrc/qtree/qtree.scm
cd opensrc/qtree;chicken-install
#
# IUP
#
ffcall.tar.gz :
wget http://www.kiatoa.com/matt/iup/ffcall.tar.gz
ffcall/README : ffcall.tar.gz
tar xfvz ffcall.tar.gz
touch -c ffcall/README
$(PREFIX)/lib/libavcall.a : ffcall/README
cd ffcall;./configure --prefix=$(PREFIX) --enable-shared && make && make install
$(IUPFILES) :
wget http://www.kiatoa.com/matt/iup/$@
cd $(PREFIX)/lib;tar xfvz $(BUILDHOME)/$@
mv $(PREFIX)/lib/include/* $(PREFIX)/include
$(PREFIX)/lib/libiup.so : $(IUPFILES)
touch -c $(PREFIX)/lib/libiup.so
$(CHICKEN_EGG_DIR)/iup.so : $(PREFIX)/lib/libiup.so
$(CHICKEN_INSTALL) $(PROX) -D no-library-checks -feature disable-iup-web iup
$(CHICKEN_EGG_DIR)/canvas-draw.so : $(PREFIX)/lib/libiup.so
$(CHICKEN_INSTALL) $(PROX) -D no-library-checks canvas-draw
clean :
rm -rf chicken-4.8.0 eggflags ffcall sqlite-autoconf-$(SQLITE3_VERSION)
# Copyright 2013,2014 Matthew Welland.
#
# This program is made available under the GNU GPL version 2.0 or
# greater. See the accompanying file COPYING for details.
#
# This program is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
# PURPOSE.
# make PREFIX=/mfs/pkgs/chicken/chicken-core all
help :
@echo You may need to do the following first:
@echo sudo apt-get install libreadline-dev
@echo sudo apt-get install libwebkitgtk-dev
@echo sudo apt-get install libmotif3
@echo You are using PREFIX=$(PREFIX)
@echo You are using proxy="$(proxy)"
@echo If needed set proxy to host.dom:port
@echo
@echo To make all do: make all
# Put the installation here
ifeq ($(PREFIX),)
PREFIX=$(PWD)/target
endif
# Set this on the command line of your make call if needed: make PROXY=host.com:1234
PROXY=
# Select version of chicken, sqlite3 etc
CHICKEN_VERSION=4.8.0
SQLITE3_VERSION=3071401
# Eggs to install (straightforward ones)
EGGS=matchable readline apropos base64 regex-literals format regex-case test coops trace csv \
dot-locking posix-utils posix-extras directory-utils hostinfo tcp-server rpc csv-xml fmt \
json md5 awful http-client spiffy uri-common intarweb spiffy-request-vars \
spiffy-directory-listing ssax sxml-serializer sxml-modifications sqlite3
#
# Derived variables
#
ifeq ($(PROXY),)
PROX=
else
http_proxy=http://$(PROXY)
PROX=-proxy $(PROXY)
endif
BUILDHOME=$(PWD)
PATH:=$(PREFIX)/bin:$(PATH)
LIBPATH=$(PREFIX)/lib$(ADDITIONAL_LIBPATH)
LD_LIBRARY_PATH=$(LIBPATH)
CHICKEN_INSTALL=$(PREFIX)/bin/chicken-install
CHICKEN_EGG_DIR=$(PREFIX)/lib/chicken/6
VPATH=$(CHICKEN_EGG_DIR):$(PWD)/eggflags
vpath %.so $(CHICKEN_EGG_DIR)
vpath %.flag eggflags
EGGSOFILES=$(addprefix $(CHICKEN_EGG_DIR)/,$(addsuffix .so,$(EGGS)))
EGGFLAGS=$(addprefix eggflags/,$(addsuffix .flag,$(EGGS)))
# Stuff needed for IUP
ISARCHX86_64=$(shell uname -a | grep x86_64)
ifeq ($(ISARCHX86_64),)
ARCHSIZE=
else
ARCHSIZE=64_
endif
CSCLIBS=$(shell echo $(LD_LIBRARY_PATH) | sed 's/:/ -L/g')
# CSC_OPTIONS=-I$(PREFIX)/include -L$(CSCLIBS)
all : chkn eggs libiup logprobin
chkn : $(CHICKEN_INSTALL)
eggs : $(EGGSOFILES)
libiup : $(PREFIX)/lib/libavcall.a $(CHICKEN_EGG_DIR)/iup.so $(CHICKEN_EGG_DIR)/canvas-draw.so
logprobin : $(PREFIX)/bin/logpro
$(PREFIX)/bin/logpro : $(CHICKEN_EGG_DIR)/regex-literals.so
$(CHICKEN_INSTALL) logpro
# Silly rule to make installing eggs more makeish, I don't understand why I need the basename
$(CHICKEN_EGG_DIR)/%.so : eggflags/%.flag
$(CHICKEN_INSTALL) $(PROX) -keep-installed $(shell basename $*)
$(EGGFLAGS) : # $(CHICKEN_INSTALL)
mkdir -p eggflags
touch $(EGGFLAGS)
# some setup stuff
#
setup-chicken4x.sh : $(EGGFLAGS)
(echo "export PATH=$(PATH)" > setup-chicken4x.sh)
(echo "export LD_LIBRARY_PATH=$(LD_LIBRARY_PATH)" >> setup-chicken4x.sh)
mkdir -p $(PREFIX)
chicken-core/chicken.scm :
git clone http://code.call-cc.org/git/chicken-core.git
# git clone git://code.call-cc.org/chicken-core
$(CHICKEN_INSTALL) : chicken-core/chicken.scm setup-chicken4x.sh
cd chicken-core;make PLATFORM=linux PREFIX=$(PREFIX)
cd chicken-core;make PLATFORM=linux PREFIX=$(PREFIX) install
#
# IUP
#
ffcall.fossil :
fossil clone http://www.kiatoa.com/fossils/ffcall ffcall.fossil
ffcall/README : ffcall.fossil
mkdir -p ffcall
cd ffcall && if [ -e README ];then fossil update; else fossil open ../ffcall.fossil; fi
# NOTE: This worked fine *without* the enable-shared
#
$(PREFIX)/lib/libavcall.a : ffcall/README
cd ffcall;./configure --prefix=$(PREFIX) --enable-shared && make && make install
iuplib.fossil :
fossil clone http://www.kiatoa.com/fossils/iuplib iuplib.fossil
iup/installall.sh : iuplib.fossil
mkdir -p iup
cd iup && if [ -e installall.sh ];then fossil update; else fossil open ../iuplib.fossil; fi
iup/iup/alldone : iup/installall.sh
cd iup && ./makeall.sh
$(PREFIX)/lib/libiup.so : iup/iup/alldone
touch -c $(PREFIX)/lib/libiup.so
$(CHICKEN_EGG_DIR)/iup.so : $(PREFIX)/lib/libiup.so
$(CHICKEN_INSTALL) $(PROX) -D no-library-checks -feature disable-iup-web iup
$(CHICKEN_EGG_DIR)/canvas-draw.so : $(PREFIX)/lib/libiup.so
$(CHICKEN_INSTALL) $(PROX) -D no-library-checks canvas-draw
clean :
rm -rf chicken-4.8.0 eggflags ffcall sqlite-autoconf-$(SQLITE3_VERSION)
#! /usr/bin/env bash
# set -x
# Copyright 2007-2010, Matthew Welland.
# Copyright 2007-2014, Matthew Welland.
#
# This program is made available under the GNU GPL version 2.0 or
# greater. See the accompanying file COPYING for details.
#
# This program is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
# PURPOSE.
echo You may need to do the following first:
echo sudo apt-get install libreadline-dev
echo sudo apt-get install libwebkitgtk-dev
echo sudo apt-get install libmotif3 -OR- set KTYPE=26g4
echo KTYPE can be 26, 26g4, or 32
echo
echo KTYPE=$KTYPE
echo You are using PREFIX=$PREFIX
echo You are using proxy="$proxy"
echo
echo "Set additional_libpath to help find gtk or other libraries, don't forget a leading :"
# NOTES:
#
# Centos with security setup may need to do commands such as following as root:
#
# NB// fix the paths first
#
# for a in /localdisk/chicken/4.8.0/lib/*.so;do chcon -t textrel_shlib_t $a; done
echo ADDITIONAL_LIBPATH=$ADDITIONAL_LIBPATH
echo
echo To use previous IUP libraries set USEOLDIUP to yes
echo USEOLDIUP=$USEOLDIUP
echo
echo Hit ^C now to do that
if [[ $KTYPE == "" ]]; then
echo 'Using KTYPE=26'
export KTYPE=26
else
echo Using KTYPE=$KTYPE
fi
# Put all the downloaded tar files in tgz
mkdir -p tgz
# http://code.call-cc.org/releases/4.8.0/chicken-4.8.0.5.tar.gz
export CHICKEN_VERSION=4.8.0
if ! [[ -echicken-${CHICKEN_VERSION}.tar.gz ]]; then
wget http://code.call-cc.org/releases/${CHICKEN_VERSION}/chicken-${CHICKEN_VERSION}.tar.gzexport CHICKEN_VERSION=4.8.0.5
export CHICKEN_BASEVER=4.8.0
chicken_targz=chicken-${CHICKEN_VERSION}.tar.gz
if ! [[ -e tgz/$chicken_targz ]]; then
wget http://code.call-cc.org/releases/${CHICKEN_BASEVER}/${chicken_targz}
mv $chicken_targz tgz
fi
BUILDHOME=$PWD
DEPLOYTARG=$BUILDHOME/deploy
if [[ $PREFIX == "" ]]; then
PREFIX=$PWD/inst
fi
export PATH=$PREFIX/bin:$PATH
export LIBPATH=$PREFIX/lib$ADDITIONAL_LIBPATH
export LD_LIBRARY_PATH=$LIBPATH
export CHICKEN_INSTALL=$PREFIX/bin/chicken-install
echo "export PATH=$PREFIX/bin:\$PATH" > setup-chicken4x.sh
echo "export LD_LIBRARY_PATH=$LD_LIBRARY_PATH" >> setup-chicken4x.sh
echo PATH=$PATH
echo LD_LIBRARY_PATH=$LD_LIBRARY_PATH
if ! [[ -e $PREFIX/bin/csi ]]; then
tar xfvz chicken-${CHICKEN_VERSION}.tar.gz
tar xfvz tgz/$chicken_targz
cd chicken-${CHICKEN_VERSION}
# make PLATFORM=linux PREFIX=$PREFIX spotless
make PLATFORM=linux PREFIX=$PREFIX
make PLATFORM=linux PREFIX=$PREFIX install
cd $BUILDHOME
fi
# Some eggs are quoted since they are reserved to Bash
# for f in matchable readline apropos base64 regex-literals format "regex-case" "test" coops trace csv dot-locking posix-utils posix-extras directory-utils hostinfo tcp rpc csv-xml fmt json md5; do
$CHICKEN_INSTALL $PROX -keep-installed matchable readline apropos base64 regex-literals format "regex-case" "test" coops trace csv dot-locking posix-utils posix-extras directory-utils hostinfo tcp rpc csv-xml fmt json md5 awful http-client spiffy uri-common intarweb http-client spiffy-request-vars
$CHICKEN_INSTALL $PROX -keep-installed matchable readline apropos base64 regex-literals format "regex-case" "test" coops trace csv dot-locking posix-utils posix-extras directory-utils hostinfo tcp rpc csv-xml fmt json md5 awful http-client spiffy uri-common intarweb http-client spiffy-request-vars spiffy-directory-listing ssax sxml-serializer sxml-modifications logpro
# if ! [[ -e $PREFIX/lib/chicken/6/$f.so ]];then
# $CHICKEN_INSTALL $PROX $f
# # $CHICKEN_INSTALL -deploy -prefix $DEPLOYTARG $PROX $f
# else
# echo Skipping install of egg $f as it is already installed
# fi
# done
cd $BUILDHOME
for a in `ls */*.meta|cut -f1 -d/` ; do
echo $a
(cd $a;$CHICKEN_INSTALL)
done
export LIBPATH=$PREFIX/lib$ADDITIONAL_LIBPATH
export LD_LIBRARY_PATH=$LIBPATH
export SQLITE3_VERSION=3071401
echo Install sqlite3
if ! [[ -esqlite-autoconf-$SQLITE3_VERSION.tar.gz ]]; then
wget http://www.sqlite.org/sqlite-autoconf-$SQLITE3_VERSION.tar.gz
sqlite3_tgz=sqlite-autoconf-$SQLITE3_VERSION.tar.gz
if ! [[ -e tgz/$sqlite3_tgz ]]; then
wget http://www.sqlite.org/$sqlite3_tgz
mv $sqlite3_tgz tgz
fi
if ! [[ -e $PREFIX/bin/sqlite3 ]] ; then
if [[ -e sqlite-autoconf-$SQLITE3_VERSION.tar.gz ]]; then
tar xfz sqlite-autoconf-$SQLITE3_VERSION.tar.gz
if [[ -e tgz/sqlite-autoconf-$SQLITE3_VERSION.tar.gz ]]; then
tar xfz tgz/sqlite-autoconf-$SQLITE3_VERSION.tar.gz
(cd sqlite-autoconf-$SQLITE3_VERSION;./configure --prefix=$PREFIX;make;make install)
# CSC_OPTIONS="-I$PREFIX/include -L$PREFIX/lib" $CHICKEN_INSTALL -prefix $DEPLOYTARG -deploy $PROX sqlite3
CSC_OPTIONS="-I$PREFIX/include -L$PREFIX/lib" $CHICKEN_INSTALL $PROX sqlite3
fi
fi
# $CHICKEN_INSTALL $PROX sqlite3
# IUP versions
CDVER=5.7
IUPVER=3.8
IMVER=3.8
if [[ `uname -a | grep x86_64` == "" ]]; then
export ARCHSIZE=''
else
export ARCHSIZE=64_
fi
# export files="cd-5.4.1_Linux${KTYPE}_lib.tar.gz im-3.6.3_Linux${KTYPE}_lib.tar.gz iup-3.5_Linux${KTYPE}_lib.tar.gz"
if [[ x$USEOLDIUP == "x" ]];then
export files="cd-5.5.1_Linux${KTYPE}_${ARCHSIZE}lib.tar.gz im-3.8_Linux${KTYPE}_${ARCHSIZE}lib.tar.gz iup-3.6_Linux${KTYPE}_${ARCHSIZE}lib.tar.gz"
export files="cd-${CDVER}_Linux${KTYPE}_${ARCHSIZE}lib.tar.gz im-${IMVER}_Linux${KTYPE}_${ARCHSIZE}lib.tar.gz iup-${IUPVER}_Linux${KTYPE}_${ARCHSIZE}lib.tar.gz"
else
echo WARNING: Using old IUP libraries
export files="cd-5.4.1_Linux${KTYPE}_${ARCHSIZE}lib.tar.gz im-3.6.3_Linux${KTYPE}_${ARCHSIZE}lib.tar.gz iup-3.5_Linux${KTYPE}_${ARCHSIZE}lib.tar.gz"
fi
mkdir -p $PREFIX/iuplib
for a in `echo $files` ; do
if ! [[ -e $a ]] ; then
if ! [[ -e tgz/$a ]] ; then
wget http://www.kiatoa.com/matt/iup/$a
fi
mv $a tgz/$a
echo Untarring $a into $BUILDHOME/lib
(cd $PREFIX/lib;tar xfvz $BUILDHOME/$a;mv include/* ../include)
echo Untarring tgz/$a into $BUILDHOME/lib
(cd $PREFIX/lib;tar xfvz $BUILDHOME/tgz/$a;mv include/* ../include)
# (cd $DEPLOYTARG;tar xfvz $BUILDHOME/$a)
done
# ffcall obtained from:
# cvs -z3 -d:pserver:anonymous@cvs.savannah.gnu.org:/sources/libffcall co ffcall
if ! [[ -e ffcall.tar.gz ]] ; then
if ! [[ -e tgz/ffcall.tar.gz ]] ; then
wget http://www.kiatoa.com/matt/iup/ffcall.tar.gz
mv ffcall.tar.gz tgz
fi
tar xfvz ffcall.tar.gz
tar xfvz tgz/ffcall.tar.gz
cd ffcall
./configure --prefix=$PREFIX --enable-shared
make
make install
cd $BUILDHOME
export CSCLIBS=`echo $LD_LIBRARY_PATH | sed 's/:/ -L/g'`
CSC_OPTIONS="-I$PREFIX/include -L$CSCLIBS" $CHICKEN_INSTALL $PROX -D no-library-checks -feature disable-iup-web iup
# CSC_OPTIONS="-I$PREFIX/include -L$CSCLIBS" $CHICKEN_INSTALL $PROX -D no-library-checks -feature disable-iup-web -deploy -prefix $DEPLOYTARG iup
# iup:1.0.2
CSC_OPTIONS="-I$PREFIX/include -L$CSCLIBS" $CHICKEN_INSTALL $PROX -D no-library-checks canvas-draw
# CSC_OPTIONS="-I$PREFIX/include -L$CSCLIBS" $CHICKEN_INSTALL $PROX -D no-library-checks -deploy -prefix $DEPLOYTARG canvas-draw
# disabled zmq ##======================================================================# disabled zmq # # Note uuid needed only for zmq 2.x series# NB// Removed bunch of zmq compiling tricks. Look at older versions of this file if you need to recreate...
# disabled zmq # #======================================================================# disabled zmq # # disabled zmq # # http://download.zeromq.org/zeromq-3.2.1-rc2.tar.gz# disabled zmq # # zpatchlev=-rc2# disabled zmq # # http://download.zeromq.org/zeromq-2.2.0.tar.gz# disabled zmq # # disabled zmq # if [[ -e /usr/lib/libzmq.so ]]; then# disabled zmq # echo "Using system installed zmq library"# disabled zmq # $CHICKEN_INSTALL zmq# disabled zmq # else# disabled zmq # ZEROMQ=zeromq-2.2.0# disabled zmq # # ZEROMQ=zeromq-3.2.2# disabled zmq # # disabled zmq # # wget http://www.kernel.org/pub/linux/utils/util-linux/v2.22/util-linux-2.22.tar.gz# disabled zmq # UTIL_LINUX=2.21# disabled zmq # # UTIL_LINUX=2.20.1# disabled zmq # if ! [[ -e util-linux-${UTIL_LINUX}.tar.gz ]] ; then# disabled zmq # # wget http://www.kiatoa.com/matt/util-linux-2.20.1.tar.gz# disabled zmq # wget http://www.kernel.org/pub/linux/utils/util-linux/v${UTIL_LINUX}/util-linux-${UTIL_LINUX}.tar.gz# disabled zmq # fi# disabled zmq # # disabled zmq # if [[ -e util-linux-${UTIL_LINUX}.tar.gz ]] ; then# disabled zmq # tar xfz util-linux-${UTIL_LINUX}.tar.gz# disabled zmq # cd util-linux-${UTIL_LINUX}# disabled zmq # mkdir -p build# disabled zmq # cd build# disabled zmq # if [[ $UTIL_LINUX = "2.22" ]] ; then# disabled zmq # ../configure --prefix=$PREFIX \# disabled zmq # --enable-shared \# disabled zmq # --disable-use-tty-group \# disabled zmq # --disable-makeinstall-chown \# disabled zmq # --disable-makeinstall-setuid \# disabled zmq # --disable-libtool-lock \# disabled zmq # --disable-login \# disabled zmq # --disable-sulogin \# disabled zmq # --disable-su \# disabled zmq # --disable-schedutils \# disabled zmq # --disable-libmount \# disabled zmq # --disable-mount \# disabled zmq # --disable-losetup \# disabled zmq # --disable-fsck \# disabled zmq # --disable-partx \# disabled zmq # --disable-mountpoint \# disabled zmq # --disable-fallocate \# disabled zmq # --disable-unshare \# disabled zmq # --disable-eject \# disabled zmq # --disable-agetty \# disabled zmq # --disable-cramfs \# disabled zmq # --disable-switch_root \# disabled zmq # --disable-pivot_root \# disabled zmq # --disable-kill \# disabled zmq # --disable-libblkid \# disabled zmq # --disable-utmpdump \# disabled zmq # --disable-rename \# disabled zmq # --disable-chsh-only-listed \# disabled zmq # --disable-wall \# disabled zmq # --disable-pg-bell \# disabled zmq # --disable-require-password \# disabled zmq # --disable-libtool-lock \# disabled zmq # --disable-nls \# disabled zmq # --disable-dmesg \# disabled zmq # --without-ncurses # disabled zmq # else# disabled zmq # ../configure --prefix=$PREFIX \# disabled zmq # --enable-shared \# disabled zmq # --disable-mount \# disabled zmq # --disable-fsck \# disabled zmq # --disable-partx \# disabled zmq # --disable-largefile \# disabled zmq # --disable-tls \# disabled zmq # --disable-libmount \# disabled zmq # --disable-mountpoint \# disabled zmq # --disable-nls \# disabled zmq # --disable-rpath \# disabled zmq # --disable-agetty \# disabled zmq # --disable-cramfs \# disabled zmq # --disable-switch_root \# disabled zmq # --disable-pivot_root \# disabled zmq # --disable-fallocate \# disabled zmq # --disable-unshare \# disabled zmq # --disable-rename \# disabled zmq # --disable-schedutils \# disabled zmq # --disable-libblkid \# disabled zmq # --disable-wall CFLAGS='-fPIC'# disabled zmq # # disabled zmq # # --disable-makeinstall-chown \# disabled zmq # # --disable-makeinstall-setuid \# disabled zmq # # disabled zmq # # --disable-chsh-only-listed# disabled zmq # # --disable-pg-bell let pg not ring the bell on invalid keys# disabled zmq # # --disable-require-password# disabled zmq # # --disable-use-tty-group do not install wall and write setgid tty# disabled zmq # # --disable-makeinstall-chown# disabled zmq # # --disable-makeinstall-setuid# disabled zmq # fi# disabled zmq # # disabled zmq # (cd libuuid;make install)# disabled zmq # # make# disabled zmq # # make install# disabled zmq # cp $PREFIX/include/uuid/uuid.h $PREFIX/include/uuid.h# disabled zmq # fi# disabled zmq # # disabled zmq # # disabled zmq # cd $BUILDHOME
# disabled zmq # cd $BUILDHOME
# disabled zmq # if ! [[ -e ${ZEROMQ}${zpatchlev}.tar.gz ]] ; then# disabled zmq # wget http://download.zeromq.org/${ZEROMQ}${zpatchlev}.tar.gz# disabled zmq # fi# disabled zmq # git clone https://bitbucket.org/DerGuteMoritz/zmq/commits/branch/3.2 zmq-3.2
cd zmq-3.2
# disabled zmq # if [[ -e ${ZEROMQ}${zpatchlev}.tar.gz ]] ; then# disabled zmq # tar xfz ${ZEROMQ}.tar.gz# disabled zmq # cd ${ZEROMQ}# disabled zmq # ln -s $PREFIX/include/uuid src# disabled zmq # # LDFLAGS=-L$PREFIX/lib ./configure --prefix=$PREFIX # disabled zmq # # disabled zmq # ./configure --enable-static --prefix=$PREFIX --with-uuid=$PREFIX LDFLAGS="-L$PREFIX/lib" CPPFLAGS="-fPIC -I$PREFIX/include" LIBS="-lgcc"# disabled zmq # # --disable-shared CPPFLAGS="-fPIC # disabled zmq # # LDFLAGS="-L/usr/lib64 -L$PREFIX/lib" ./configure --enable-static --prefix=$PREFIX # disabled zmq # make# disabled zmq # makeinstall
# disabled zmq # CSC_OPTIONS="-I$PREFIX/include -L$CSCLIBS" $CHICKEN_INSTALL $PROX zmqchicken-install
# disabled zmq # # CSC_OPTIONS="-I$PREFIX/include -L$CSCLIBS" $CHICKEN_INSTALL $PROX -deploy -prefix $DEPLOYTARG zmq# disabled zmq # fi# disabled zmq # fi # if zmq is in /usr/lib# disabled zmq #
cd $BUILDHOMEcd $BUILDHOME
## WEBKIT=WebKit-r131972
## if ! [[ -e ${WEBKIT}.tar.bz2 ]] ; then
## # http://builds.nightly.webkit.org/files/trunk/src/WebKit-r131972.tar.bz2
## wget http://builds.nightly.webkit.org/files/trunk/src/${WEBKIT}.tar.bz2
## fi
##
322
323
324
325
326
327
328
229
230
231
232
233
234
235
236
237
238
+
+
+
# unzip -o Canvas_Draw-$CD_REL.zip
#
# cd "Canvas Draw-$CD_REL/chicken"
# CSC_OPTIONS="-I$PREFIX/include -L$LIBPATH" $CHICKEN_INSTALL $PROX -D no-library-checks
echo You may need to add $LD_LIBRARY_PATH to your LD_LIBRARY_PATH variable, a setup-chicken4x.sh
echo file can be found in the current directory which should work for setting up to run chicken4x
echo Testing iup
$PREFIX/bin/csi -b -eval '(use iup)(print "Success")'
#!/bin/bash
usage="mt_ezstep stepname prevstepname command [args ...]"
if [ "$MT_CMDINFO" == "" ];then
if [ -e megatest.sh ];then
source megatest.sh
else
echo "ERROR: $0 should be run within a megatest test environment"
echo "Usage: $usage"
exit
echo "ERROR: $0 should be run within a megatest test environment"
echo "Usage: $usage"
exit
fi
fi
# Purpose: This is for the [ezsteps] secton in your testconfig file.
# DO NOT USE IN YOUR SCRIPTS!
#
# Call like this:
# mt_ezstep stepname prevstepname command ....
#!/bin/bash
MT_TMPDISPLAY=$DISPLAY
if [ -e megatest.sh ];then
source megatest.sh
fi
export DISPLAY=$MT_TMPDISPLAY
if [ x"$MT_XTERM_CMD" == "x" ];then
exec xterm "$@"
else
exec $MT_XTERM_CMD
fi
#!/usr/bin/env bash
# Copyright 2012, Matthew Welland.
#
# This program is made available under the GNU GPL version 2.0 or
# greater. See the accompanying file COPYING for details.
#
# This program is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
# PURPOSE.
#
# VERSION:
# set -e
# set -u
# set -x
# Usage: mtrunscript scriptname params
#
# Look for scriptname in this order
#
# $MT_TEST_RUN_DIR/scripts => $MT_RUN_AREA_HOME/scripts
# => $MT_RUN_AREA_HOME/../scripts => $PATH
#
# In each area look for the script with the name like this:
#
# scriptname_$TARGET[1]_$TARGET[2]_...$TARGET[n]_$MT_TESTNAME_$MT_ITEMPATH(s#/#_)
#
echo "NOT IMPLEMENTED YET!"
exit
case "x$1" in
# repo
xrep*)
fsl_dbinit
case "x$2" in
xhelp)
fsl_help
exit
;;
# repo get
xget)
hook_pre_repo_get "$@"
fsl_repo_get $3 $4
hook_post_repo_get "$@"
exit
;;
xaddarea)
fsl_add_area $3 $4
exit
;;
xdroparea)
fsl_remove_area $3
exit
;;
xdbinit)
fsl_dbinit
exit
;;
xls|xlist)
shift
shift
fsl_ls "$@"
exit
;;
xcreate)
hook_pre_repo_create
fsl_repo_create $3 $4 $5 $6
hook_post_repo_create
exit
;;
ximport)
fsl_repo_import $3 $4 $5
exit
;;
*)
fsl_help
exit
esac
;;
"xmv")
if [ "x$2" = "x-f" ];then
# echo "Force mode"
fsl_force=1
shift
shift
# change this to exec when happy!
# fsl mv -f f1 [f2 f3...] targ
fsl_mv "$@"
# args=("$@")
# echo $@ -> echo $@
# use $# variable to print out
# number of arguments passed to the bash script
# echo Number of arguments passed: $# -> echo Number of arguments passed: $#
exit
else
# echo No force
shift
fsl_mv "$@"
exit
fi
;;
xtim*)
fsl_fork_find
shift
$FOSSILEXE timeline "$@" | sed -e :a -e '$!N;s/\n / /;ta' -e 'P;D'
exit
;;
# leaves output needs to be niceified, no need for a function
xle*)
fsl_fork_find
shift
$FOSSILEXE leaves "$@" | sed -e :a -e '$!N;s/\n / /;ta' -e 'P;D'
exit
;;
# changes and status
xcha* | xstat*)
fsl_fork_find
fsl_conflicts "$@"
rm -f $CONFLICT_FLAG_FILE
exit
;;
# ci/commit
xci | xcom*)
fsl_conflicts changes "$@"
trap "$FOSSILUTIL releaselock $FSLUTIL_PARAMS" SIGINT
# Set up for remote locking
if [ ! -e $CONFLICT_FLAG_FILE ]; then
rm -f $CONFLICT_FLAG_FILE
read -p "ERROR: Conflicts detected. Type \"yes\" to continue: " -e ANSWER
if [ $ANSWER = "yes" ]; then
$FOSSILUTIL commitlock $FSLUTIL_PARAMS
$FOSSILEXE "$@"
$FOSSILUTIL releaselock $FSLUTIL_PARAMS
else
exit 1
fi
else
$FOSSILUTIL commitlock $FSLUTIL_PARAMS
$FOSSILEXE "$@"
$FOSSILUTIL releaselock $FSLUTIL_PARAMS
fi
exit
;;
xtag)
case "x$2" in
xadd | xcancel)
$FOSSILEXE "$@"
$FOSSILEXE sync
exit
;;
*)
$FOSSILEXE "$@"
exit
;;
esac
;;
# add mention of repo to help
"xhelp")
if [ $# -gt 1 ]; then
case "x$2" in
xrepo)
fsl_help
exit
;;
*)
$FOSSILEXE "$@"
;;
esac
else
$FOSSILEXE help | sed -e 's/sync/sync repo/'
fi
exit
;;
xup* | xco)
fsl_fork_find
$FOSSILEXE "$@"
exit
;;
esac
exec $FOSSILEXE "$@"