(library (process) (export build-command-line run run-ok run-fail run-ok-rcv run-fail-rcv) (import (chezscheme) (fail) (fmt fmt) (logging) (list-utils) (srfi s8 receive) (temp-file) (utils)) ;;;-------------------------------------------------------------------- ;;; Run a sub process and capture it's output. ;;; Ideally we'd use open-process-ports, but that loses us the exit code which ;;; we need for testing. So we use system, and redirect stderr and stdout to ;;; temporary files, and subsequently read them in. Messy, but fine for tests. (define (build-command-line cmd-and-args) (apply fmt #f (map dsp (intersperse " " cmd-and-args)))) (define (run . cmd-and-args) (with-temp-file ((stdout-file "stdout") (stderr-file "stderr")) (let* ((short-cmd (build-command-line cmd-and-args)) (cmd (fmt #f (dsp (build-command-line cmd-and-args)) (dsp " > ") (dsp stdout-file) (dsp " 2> ") (dsp stderr-file)))) (info (dsp "cmd: ") (dsp short-cmd)) (let ((exit-code (system cmd))) (let ((out (slurp-file stdout-file)) (err (slurp-file stderr-file))) (info (dsp "stdout: ") (dsp out)) (info (dsp "stderr: ") (dsp err)) (values exit-code out err)))))) (define (run-with-exit-code pred cmd-and-args) (receive (exit-code stdout stderr) (apply run cmd-and-args) (if (pred exit-code) (values stdout stderr) (begin (let ((msg (fmt #f "unexpected exit code (" exit-code ")"))) (info msg) (fail msg)))))) (define (run-ok . cmd-and-args) (run-with-exit-code zero? cmd-and-args)) ;; Exit code 139 is a segfault, which is not acceptable (define (run-fail . cmd-and-args) (define (fails? x) (not (or (= 139 x) (zero? x)))) (run-with-exit-code fails? cmd-and-args)) (define-syntax run-ok-rcv (syntax-rules () ((_ (stdout stderr) cmd b1 b2 ...) (receive (stdout stderr) (run-ok cmd) b1 b2 ...)))) (define-syntax run-fail-rcv (syntax-rules () ((_ (stdout stderr) cmd b1 b2 ...) (receive (stdout stderr) (run-fail cmd) b1 b2 ...)))) )