Index: db.scm ================================================================== --- db.scm +++ db.scm @@ -62,11 +62,10 @@ run_duration INTEGER DEFAULT 0, comment TEXT DEFAULT '', event_time TIMESTAMP, fail_count INTEGER DEFAULT 0, pass_count INTEGER DEFAULT 0, - tags TEXT DEFAULT '', CONSTRAINT testsconstraint UNIQUE (run_id, testname, item_path) );") (sqlite3:execute db "CREATE INDEX tests_index ON tests (run_id, testname);") (sqlite3:execute db "CREATE VIEW runs_tests AS SELECT * FROM runs INNER JOIN tests ON runs.id=tests.run_id;") (sqlite3:execute db "CREATE TABLE test_steps @@ -344,12 +343,12 @@ (define (db:estimated-tests-remaining db run-id) (let ((res 0)) (sqlite3:for-each-row (lambda (count) (set! res count)) - db - "SELECT count(id) FROM tests WHERE state in ('LAUNCHED','NOT_STARTED','REMOTEHOSTSTART','RUNNING') AND run_id=?;" run-id) + db ;; NB// KILLREQ means the jobs is still probably running + "SELECT count(id) FROM tests WHERE state in ('LAUNCHED','NOT_STARTED','REMOTEHOSTSTART','RUNNING','KILLREQ') AND run_id=?;" run-id) res)) ;; NB// Sync this with runs:get-test-info (define (db:get-test-info db run-id testname item-path) (let ((res #f)) @@ -458,28 +457,28 @@ db "SELECT id,test_id,stepname,state,status,event_time FROM test_steps WHERE test_id=? ORDER BY id ASC;" ;; event_time DESC,id ASC; test-id) (reverse res))) -;; check that *all* the prereqs are "COMPLETED" -(define (db-get-prereqs-met db run-id waiton) - (let ((res #f) - (not-complete 0) - (tests (db-get-tests-for-run db run-id))) - (for-each - (lambda (test-name) - (for-each - (lambda (test) - (if (equal? (db:test-get-testname test) test-name) - (begin - (set! res #t) - (if (not (equal? (db:test-get-state test) "COMPLETED")) - (set! not-complete (+ 1 not-complete)))))) - tests)) - waiton) - (and (or (null? waiton) res) - (eq? not-complete 0)))) +;; ;; check that *all* the prereqs are "COMPLETED" +;; (define (db-get-prereqs-met db run-id waiton) +;; (let ((res #f) +;; (not-complete 0) +;; (tests (db-get-tests-for-run db run-id))) +;; (for-each +;; (lambda (test-name) +;; (for-each +;; (lambda (test) +;; (if (equal? (db:test-get-testname test) test-name) +;; (begin +;; (set! res #t) +;; (if (not (equal? (db:test-get-state test) "COMPLETED")) +;; (set! not-complete (+ 1 not-complete)))))) +;; tests)) +;; waiton) +;; (and (or (null? waiton) res) +;; (eq? not-complete 0)))) ;; USE: (lset-difference string=? '("a" "b" "c") '("d" "c" "e" "a")) ;; ;; Return a list of prereqs that were NOT met ;; Tests (and all items) in waiton list must be "COMPLETED" and "PASS" Index: launch.scm ================================================================== --- launch.scm +++ launch.scm @@ -145,11 +145,11 @@ (set! fullcmd (append launcher (list remote-megatest "-execute" cmdparms)))) (else (set! fullcmd (list remote-megatest "-execute" cmdparms)))) (if (args:get-arg "-xterm")(set! fullcmd (append fullcmd (list "-xterm")))) (debug:print 1 "Launching megatest for test " test-name " in " work-area" ...") - (test-set-status! db run-id test-name "LAUNCHED" "n/a" itemdat) ;; (if launch-results launch-results "FAILED")) + (test-set-status! db run-id test-name "LAUNCHED" "n/a" itemdat #f #f) ;; (if launch-results launch-results "FAILED")) ;; set ;; set pre-launch-env-vars before launching, keep the vars in prevvals and put the envionment back when done (let* ((commonprevvals (alist->env-vars (hash-table-ref/default *configdat* "env-override" '()))) (testprevvals (alist->env-vars Index: megatest-version.scm ================================================================== --- megatest-version.scm +++ megatest-version.scm @@ -1,3 +1,3 @@ ;; Always use two digit decimal ;; 1.01, 1.02...1.10,1.11 ... 1.99,2.00.. -(define megatest-version 1.20) +(define megatest-version 1.21) Index: megatest.scm ================================================================== --- megatest.scm +++ megatest.scm @@ -37,10 +37,18 @@ Run data :runname : required, name for this particular test run :state : required if updating step state; e.g. start, end, completed :status : required if updating step status; e.g. pass, fail, n/a +Values and record errors and warnings + -set-values : update or set values in the megatest db + :value : value measured + :expected_value : value expected + :tol : tolerance |value-expect| <= tol + :first_err : record an error message + :first_warn : record a warning message + Queries -list-runs patt : list runs matching pattern \"patt\", % is the wildcard -testpatt patt : in list-runs show only these tests, % is the wildcard -itempatt patt : in list-runs show only tests with items that match patt -showkeys : show the keys used in this megatest setup @@ -95,17 +103,25 @@ "-m" "-rerun" "-days" "-rename-run" "-to" + ;; values and messages + ":first_err" + ":first_warn" + ":value" + ":expected_value" + ":tol" + ;; misc "-debug" ;; for *verbosity* > 2 ) (list "-h" "-force" "-xterm" "-showkeys" "-test-status" + "-set-values" "-summarize-items" "-gui" "-runall" ;; run all tests "-remove-runs" "-keepgoing" @@ -360,11 +376,11 @@ (alist->env-vars env-ovrd) (set-megatest-env-vars db run-id) (set-item-env-vars itemdat) (save-environment-as-files "megatest") (test-set-meta-info db run-id test-name itemdat) - (test-set-status! db run-id test-name "REMOTEHOSTSTART" "n/a" itemdat (args:get-arg "-m")) + (test-set-status! db run-id test-name "REMOTEHOSTSTART" "n/a" itemdat (args:get-arg "-m") #f) (if (args:get-arg "-xterm") (set! fullrunscript "xterm") (if (not (file-execute-access? fullrunscript)) (system (conc "chmod ug+x " fullrunscript)))) ;; We are about to actually kick off the test @@ -448,11 +464,11 @@ ;; (sqlite3:finalize! db) ;; (exit 1))))) (begin (debug:print 0 "WARNING: Request received to kill job but problem with process, attempting to kill manager process") (test-set-status! db run-id test-name "KILLED" "FAIL" - itemdat (args:get-arg "-m")) + itemdat (args:get-arg "-m") #f) (sqlite3:finalize! db) (exit 1)))) ;; (thread-terminate! job-thread))) (set! kill-tries (+ 1 kill-tries)) (mutex-unlock! m))) @@ -492,11 +508,11 @@ (if (vector-ref exit-info 1) ;; look at the exit-status (if (and (not kill-job?) (eq? (vector-ref exit-info 2) 0)) "PASS" "FAIL") - "FAIL") itemdat (args:get-arg "-m")))) + "FAIL") itemdat (args:get-arg "-m") #f))) ;; for automated creation of the rollup html file this is a good place... (if (not (equal? item-path "")) (tests:summarize-items db run-id test-name #f)) ;; don't force - just update if no ) (mutex-unlock! m) @@ -540,10 +556,11 @@ (set! *didsomething* #t)))) (if (or (args:get-arg "-setlog") ;; since setting up is so costly lets piggyback on -test-status (args:get-arg "-set-toplog") (args:get-arg "-test-status") + (args:get-arg "-set-values") (args:get-arg "-runstep") (args:get-arg "-summarize-items")) (if (not (getenv "MT_CMDINFO")) (begin (debug:print 0 "ERROR: MT_CMDINFO env var not set, commands -test-status, -runstep and -setlog must be called *inside* a megatest environment!") @@ -618,22 +635,33 @@ (if (not (eq? exitstat 0)) (exit 254)) ;; (exit exitstat) doesn't work?!? ;; open the db ;; mark the end of the test ))) - (if (args:get-arg "-test-status") + (if (or (args:get-arg "-test-status") + (args:get-arg "-set-values")) (let ((newstatus (cond ((number? status) (if (equal? status 0) "PASS" "FAIL")) - ((string->number status)(if (equal? (string->number status) 0) "PASS" "FAIL")) - (else status)))) - (test-set-status! db run-id test-name state newstatus itemdat (args:get-arg "-m"))) - (if (and state status) - (if (not (args:get-arg "-setlog")) - (begin - (debug:print 0 "ERROR: You must specify :state and :status with every call to -test-status\n" help) - (sqlite3:finalize! db) - (exit 6))))) + ((and (string? status) + (string->number status))(if (equal? (string->number status) 0) "PASS" "FAIL")) + (else status))) + ;; transfer relevant keys into a hash to be passed to test-set-status! + ;; could use an assoc list I guess. + (otherdata (let ((res (make-hash-table))) + (for-each (lambda (key) + (if (args:get-arg key) + (hash-table-set! res key (args:get-arg key)))) + (list ":value" ":tol" ":expected_value" ":first_err" ":first_warn")) + res))) + (if (and (args:get-arg "-test-status") + (or (not state) + (not status))) + (begin + (debug:print 0 "ERROR: You must specify :state and :status with every call to -test-status\n" help) + (sqlite3:finalize! db) + (exit 6))) + (test-set-status! db run-id test-name state newstatus itemdat (args:get-arg "-m") otherdata))) (sqlite3:finalize! db) (set! *didsomething* #t)))) (if (args:get-arg "-showkeys") (let ((db #f) Index: runs.scm ================================================================== --- runs.scm +++ runs.scm @@ -73,31 +73,62 @@ db (conc "SELECT " keystr " FROM runs WHERE runname like ? " key-patt ";") runnamepatt) (vector header res))) -(define (register-test db run-id test-name item-path tags) +(define (register-test db run-id test-name item-path) (let ((item-paths (if (equal? item-path "") (list item-path) (list item-path "")))) (for-each (lambda (pth) - (sqlite3:execute db "INSERT OR IGNORE INTO tests (run_id,testname,event_time,item_path,state,status,tags) VALUES (?,?,strftime('%s','now'),?,'NOT_STARTED','n/a',?);" + (sqlite3:execute db "INSERT OR IGNORE INTO tests (run_id,testname,event_time,item_path,state,status) VALUES (?,?,strftime('%s','now'),?,'NOT_STARTED','n/a');" run-id test-name pth - (conc "," (string-intersperse tags ",") ","))) + ;; (conc "," (string-intersperse tags ",") ",") + )) item-paths ))) ;; (define db (open-db)) ;; (test-set-status! db 2 "runfirst" "COMPLETED" "PASS" "summer") -(define (test-set-status! db run-id test-name state status itemdat-or-path . comment) - (let ((item-path (if (string? itemdat-or-path) itemdat-or-path (item-list->path itemdat-or-path)))) - (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) - (if (and (not (equal? item-path "")) ;; need to update the top test record if PASS or FAIL and this is a subtest +(define (test-set-status! db run-id test-name state status itemdat-or-path comment dat) + (let ((item-path (if (string? itemdat-or-path) itemdat-or-path (item-list->path itemdat-or-path))) + (otherdat (if dat dat (make-hash-table)))) + ;; update the primary record IF state AND status are defined + (if (and state status) + (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)) + ;; add metadata (need to do this way to avoid SQL injection issues) + ;; :value + (let ((val (hash-table-ref/default otherdat ":value" #f))) + (if val + (sqlite3:execute db "UPDATE tests SET value=? WHERE run_id=? AND testname=? AND item_path=?;" val run-id test-name item-path))) + ;; :expected_value + (let ((val (hash-table-ref/default otherdat ":expected_value" #f))) + (if val + (sqlite3:execute db "UPDATE tests SET expected_value=? WHERE run_id=? AND testname=? AND item_path=?;" val run-id test-name item-path))) + ;; :tol + (let ((val (hash-table-ref/default otherdat ":tol" #f))) + (if val + (sqlite3:execute db "UPDATE tests SET tol=? WHERE run_id=? AND testname=? AND item_path=?;" val run-id test-name item-path))) + ;; :first_err + (let ((val (hash-table-ref/default otherdat ":first_err" #f))) + (if val + (sqlite3:execute db "UPDATE tests SET first_err=? WHERE run_id=? AND testname=? AND item_path=?;" val run-id test-name item-path))) + ;; :first_warn + (let ((val (hash-table-ref/default otherdat ":first_warn" #f))) + (if val + (sqlite3:execute db "UPDATE tests SET first_warn=? WHERE run_id=? AND testname=? AND item_path=?;" val run-id test-name item-path))) + ;; :tol_perc + (let ((val (hash-table-ref/default otherdat ":tol_perc" #f))) + (if val + (sqlite3:execute db "UPDATE tests SET tol_perc=? WHERE run_id=? AND testname=? AND item_path=?;" val run-id test-name item-path))) + + ;; need to update the top test record if PASS or FAIL and this is a subtest + (if (and (not (equal? item-path "")) (or (equal? status "PASS") (equal? status "WARN") (equal? status "FAIL"))) (begin (sqlite3:execute @@ -114,14 +145,15 @@ 'RUNNING' ELSE 'COMPLETED' END, status=CASE WHEN fail_count > 0 THEN 'FAIL' WHEN pass_count > 0 AND fail_count=0 THEN 'PASS' ELSE 'UNKNOWN' END WHERE run_id=? AND testname=? AND item_path='';" run-id test-name run-id test-name))) - (if (and (not (null? comment)) - (car comment)) + (if (and (string? comment) + (string-match (regexp "\\S+") comment)) (sqlite3:execute db "UPDATE tests SET comment=? WHERE run_id=? AND testname=? AND item_path=?;" - (car comment) run-id test-name item-path)))) + (car comment) run-id test-name item-path)) + )) (define (test-set-log! db run-id test-name itemdat logf) (let ((item-path (item-list->path itemdat))) (sqlite3:execute db "UPDATE tests SET final_logf=? WHERE run_id=? AND testname=? AND item_path=?;" logf run-id test-name item-path))) @@ -404,11 +436,15 @@ (testexists (and (file-exists? test-configf)(file-read-access? test-configf))) (test-conf (if testexists (read-config test-configf #f #t) (make-hash-table))) (waiton (let ((w (config-lookup test-conf "requirements" "waiton"))) (if (string? w)(string-split w)'()))) (tags (let ((t (config-lookup test-conf "setup" "tags"))) - (if (string? t)(string-split t ",") '())))) + ;; we want our tags to be separated by commas and fully delimited by commas + ;; so that queries with "like" can tie to the commas at either end of each tag + ;; while also allowing the end user to freely use spaces and commas to separate tags + (if (string? t)(string-substitute (regexp "[,\\s]+") "," (conc "," t ",") #t) + '())))) (if (not testexists) (begin (debug:print 0 "ERROR: Can't find config file " test-configf) (exit 2)) ;; put top vars into convenient variables and open the db @@ -453,11 +489,11 @@ (let loop2 ((ts (db:get-test-info db run-id test-name item-path)) ;; #f) (ct 0)) (if (and (not ts) (< ct 10)) (begin - (register-test db run-id test-name item-path tags) + (register-test db run-id test-name item-path) (db:test-set-comment db run-id test-name item-path "") (loop2 (db:get-test-info db run-id test-name item-path) (+ ct 1))) (if ts (set! testdat ts) Index: tests/tests/runfirst/main.sh ================================================================== --- tests/tests/runfirst/main.sh +++ tests/tests/runfirst/main.sh @@ -6,6 +6,6 @@ touch ../I_was_here $MT_MEGATEST -runstep wasting_time -logpro wasting_time.logpro "sleep 8;echo all done eh?" -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 +$MT_MEGATEST -test-status :state COMPLETED :status $? -m "This is a test level comment" -set-toplog the_top_log.html :value 1e6 :expected_value 1.1e6 :tol 100e3 Index: tests/tests/singletest/main.sh ================================================================== --- tests/tests/singletest/main.sh +++ tests/tests/singletest/main.sh @@ -4,6 +4,6 @@ # 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 -test-status :state COMPLETED :status $? -m "This is a test level comment" -set-toplog the_top_log.html +$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" Index: tests/tests/singletest2/main.sh ================================================================== --- tests/tests/singletest2/main.sh +++ tests/tests/singletest2/main.sh @@ -4,6 +4,6 @@ # 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 -test-status :state COMPLETED :status $? -m "This is a test level comment" -set-toplog the_top_log.html +$MT_MEGATEST -test-status :state COMPLETED :status $? -m "This is a test level comment" -set-toplog the_top_log.html :first_warn "This is the first warning"