Skip to content

Commit

Permalink
Refactor output capture
Browse files Browse the repository at this point in the history
Fixes capture output buffer not clearing.
  • Loading branch information
weavejester committed Oct 26, 2017
1 parent ed75d52 commit 163942e
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 64 deletions.
60 changes: 31 additions & 29 deletions eftest/src/eftest/output_capture.clj
Original file line number Diff line number Diff line change
@@ -1,54 +1,56 @@
(ns eftest.output-capture
(:require [clojure.string :as str])
(:import (java.io OutputStream ByteArrayOutputStream PrintStream PrintWriter)
(java.nio.charset StandardCharsets)))
(:import [java.io OutputStream ByteArrayOutputStream PrintStream PrintWriter]))

(def captured-output-contexts (atom {}))
(def context (atom {}))

(defn thread-id []
(-> (Thread/currentThread)
(.getId)))

(defn init-capture-buffer [buffer]
(defn- init-buffer [buffer]
(or buffer (ByteArrayOutputStream.)))

(defn get-capture-buffer ^ByteArrayOutputStream []
(let [id (thread-id)]
(-> (swap! captured-output-contexts update id init-capture-buffer)
(get id))))

(defn flush-captured-output ^String []
(let [output (some-> (get-capture-buffer)
.toByteArray
(String. StandardCharsets/UTF_8)
str/trim)]
(when-not (str/blank? output)
output)))
(defn- get-local-buffer ^ByteArrayOutputStream []
(let [id (.getId (Thread/currentThread))]
(-> (swap! context update id init-buffer) (get id))))

(defn create-proxy-output-stream ^OutputStream []
(defn- create-proxy-output-stream ^OutputStream []
(proxy [OutputStream] []
(write
([data]
(when-let [target (get-capture-buffer)]
(when-let [target (get-local-buffer)]
(if (instance? Integer data)
(.write target ^int data)
(.write target ^bytes data 0 (alength ^bytes data)))))
([data off len]
(when-let [target (get-capture-buffer)]
(when-let [target (get-local-buffer)]
(.write target data off len))))))

(defn init-capture []
(let [old-out System/out
old-err System/err
(reset! context {})
(let [old-out System/out
old-err System/err
proxy-output-stream (create-proxy-output-stream)
new-stream (PrintStream. proxy-output-stream)
new-writer (PrintWriter. proxy-output-stream)]
new-stream (PrintStream. proxy-output-stream)
new-writer (PrintWriter. proxy-output-stream)]
(System/setOut new-stream)
(System/setErr new-stream)
{:captured-writer new-writer
:old-system-out old-out
:old-system-err old-err}))
:old-system-out old-out
:old-system-err old-err}))

(defn restore-capture [{:keys [old-system-out old-system-err]}]
(System/setOut old-system-out)
(System/setErr old-system-err))

(defmacro with-capture [& body]
`(let [context# (init-capture)
writer# (:captured-writer context#)]
(try
(binding [*out* writer#, *err* writer#]
~@body)
(finally
(restore-capture context#)))))

(defn clear-local-output []
(.reset (get-local-buffer)))

(defn read-local-output []
(some-> (get-local-buffer) .toByteArray String. str/trim))
17 changes: 10 additions & 7 deletions eftest/src/eftest/report/pretty.clj
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
:omitted-frame ansi/reset-font
:pass ansi/green-font
:fail ansi/red-font
:error ansi/red-font})
:error ansi/red-font
:divider ansi/yellow-font})

(def ^:dynamic *divider*
"The divider to use between test failure and error reports."
Expand Down Expand Up @@ -82,10 +83,12 @@
(puget/format-doc p actual))]])))

(defn- print-output [output]
(when-not (str/blank? output)
(println "--- Test output ---")
(println output)
(println "-------------------")))
(let [c (:divider *fonts*)
r (:reset *fonts*)]
(when-not (str/blank? output)
(println (str c "---" r " Test output " c "---" r))
(println output)
(println (str c "-------------------" r)))))

(defmulti report
"A reporting function compatible with clojure.test. Uses ANSI colors and
Expand All @@ -108,7 +111,7 @@
(= (first expected) '=))
(equals-fail-report m)
(predicate-fail-report m))
(print-output (capture/flush-captured-output))))
(print-output (capture/read-local-output))))

(defmethod report :error [{:keys [message expected actual] :as m}]
(test/with-test-out
Expand All @@ -118,7 +121,7 @@
(when (seq test/*testing-contexts*) (println (test/testing-contexts-str)))
(when message (println message))
(error-report m)
(print-output (capture/flush-captured-output))))
(print-output (capture/read-local-output))))

(defn- pluralize [word count]
(if (= count 1) word (str word "s")))
Expand Down
50 changes: 22 additions & 28 deletions eftest/src/eftest/runner.clj
Original file line number Diff line number Diff line change
Expand Up @@ -16,41 +16,34 @@
(or (-> v meta :eftest/synchronized true?)
(-> v meta :ns meta :eftest/synchronized true?)))

(defn- test-vars [ns vars {:as opts :keys [catch-out? fail-fast?] :or {catch-out? true}}]
(defn- failed-test? []
(or (< 0 (:error @test/*report-counters* 0))
(< 0 (:fail @test/*report-counters* 0))))

(defn- test-vars [ns vars {:as opts :keys [fail-fast?]}]
(let [once-fixtures (-> ns meta ::test/once-fixtures test/join-fixtures)
each-fixtures (-> ns meta ::test/each-fixtures test/join-fixtures)
capture-context (when catch-out?
(capture/init-capture))
report (synchronize test/report)
test-var (if catch-out?
(fn [v]
(binding [test/report report
*out* (:captured-writer capture-context)
*err* (:captured-writer capture-context)]
(test/test-var v)))
(fn [v]
(binding [test/report report]
(test/test-var v))))
test-var (fn [v]
(when-not (and fail-fast?
(or (< 0 (:error @test/*report-counters* 0))
(< 0 (:fail @test/*report-counters* 0)) ))
(each-fixtures #(test-var v))))]
test-var (fn [v]
(when-not (and fail-fast? (failed-test?))
(each-fixtures
#(binding [test/report report]
(capture/clear-local-output)
(test/test-var v)))))]
(once-fixtures
(fn []
(if (:multithread? opts true)
(let [test (bound-fn [v] (test-var v))]
(dorun (->> vars (filter synchronized?) (map test)))
(dorun (->> vars (remove synchronized?) (pmap test))))
(doseq [v vars] (test-var v)))))
(when catch-out?
(capture/restore-capture capture-context))))

(defn- test-ns [ns vars opts]
#(if (:multithread? opts true)
(let [test (bound-fn* test-var)]
(dorun (->> vars (filter synchronized?) (map test)))
(dorun (->> vars (remove synchronized?) (pmap test))))
(doseq [v vars] (test-var v))))))

(defn- test-ns [ns vars {:as opts :keys [catch-out?] :or {catch-out? true}}]
(let [ns (the-ns ns)]
(binding [test/*report-counters* (ref test/*initial-report-counters*)]
(test/do-report {:type :begin-test-ns, :ns ns})
(test-vars ns vars opts)
(if catch-out?
(capture/with-capture (test-vars ns vars opts))
(test-vars ns vars opts))
(test/do-report {:type :end-test-ns, :ns ns})
@test/*report-counters*)))

Expand Down Expand Up @@ -116,4 +109,5 @@
duration (/ (- (System/nanoTime) start-time) 1e6)
summary (assoc counters :type :summary, :duration duration)]
(test/do-report summary)
(capture/flush-captured-output)
summary))))))

0 comments on commit 163942e

Please sign in to comment.