       ::read-error)))
 
 (defn start-process [command encoding]
-  ;; TODO: what if there is execption here? need to report that we
+  ;; TODO: what if there is exception here? need to report that we
   ;; couldn't start the process?
   ;; TODO: how do we communicate that the encoding is not supported?
   (let [p (.start (ProcessBuilder. [command (name encoding)]))
         out (BufferedOutputStream. (.getOutputStream p))
         in (BufferedInputStream. (.getInputStream p))]
-    {:out out :in in :p p}))
+    ;; r/reader does not return until data starts to flow over in
+    {:out out :p p :reader (future (r/reader in encoding))}))
 
 (defn stop-process [proc]
   (try
     (.write (:out proc) 3) ;; send Ctrl+C
     (.flush (:out proc))
     (.destroy (:p proc))
-    (catch Throwable _
-      ;; TODO: report this?
-      )))
-
+    (catch Throwable e
+      (println "WARNING! Exception while stopping process.")
+      (println (.toString e)))))
+
+;; TODO: failure to write means that the process has died
+;; catch and throw the way we do with read-response
 (defn write-to-stream [proc transit-data]
-  #_(println "Send bytes:")
-  #_(println (Hex/encodeHexString transit-data))
   (.write (:out proc) transit-data 0 (count transit-data))
   (.flush (:out proc)))
 
-(defn read-from-stream [proc]
-  (let [bytes (ByteArrayOutputStream.)]
-    (loop [state `(:wait)
-           counter 0
-           prev nil]
-      (let [b (.read (:in proc))]
-        (if (== b -1)
-          (.toByteArray bytes)
-          (let [c (char b)]
-            (case (first state)
-              :wait (cond (= c \[) (do (.write bytes b)
-                                       (recur '(:array) (inc counter) c))
-                          (= c \{) (do (.write bytes b)
-                                       (recur '(:object) (inc counter) c))
-                          :else (recur state counter c))
-              :string (do (.write bytes b)
-                          (cond (and (= prev \\) (= c \\))
-                                ;; set prev to non-escape char
-                                (recur state counter \space)
-                                (and (not= prev \\) (= c \"))
-                                (recur (rest state) counter c)
-                                :else (recur state counter c)))
-              :array (do (.write bytes b)
-                         (cond (and (not= prev \\) (= c \"))
-                               (recur (conj state :string) counter c)
-                               (and (= c \[))
-                               (recur state (inc counter) c)
-                               (and (= c \]) (pos? (dec counter)))
-                               (recur state (dec counter) c)
-                               (= c \])
-                               (.toByteArray bytes)
-                               :else (recur state counter c)))
-              :object (do (.write bytes b)
-                          (cond (and (not= prev \\) (= c \"))
-                                (recur (conj state :string) counter c)
-                                (and (= c \{))
-                                (recur state (inc counter) c)
-                                (and (= c \}) (pos? (dec counter)))
-                                (recur state (dec counter) c)
-                                (= c \})
-                                (.toByteArray bytes)
-                                :else (recur state counter c))))))))))
-
 (defn read-response [proc transit-out timeout-ms]
-  (let [f (future (read-from-stream proc))]
+  (let [f (future (r/read @(:reader proc)))]
     (let [response (deref f timeout-ms ::timeout)]
       (if (= response ::timeout)
         (throw (ex-info "Response timeout" {:status :timeout :p proc :transit transit-out}))


(defn roundtrip-edn [proc edn encoding]
  (try
    (let [transit-out (write-transit edn encoding)
          result (roundtrip-transit proc transit-out encoding)]
      (if (not (or (= (:status result) :error)
                   (= edn (:data-actual result) (:data-expected result))))
        (assoc result :status :warning :edn edn)
        result))
    (catch Throwable e
      (if (= (-> e ex-data :status) :timeout)
        (throw (ex-info "Response timeout" (assoc (ex-data e) :edn edn)))
        (throw e)))))

(def extension {:json ".json"
                :msgpack ".mp"})

(defn test-exemplar-files [proc encoding]
  (let [files (filter #(and (.isFile %) (.endsWith (.getName %) (extension encoding)))
                      (file-seq (io/file "../transit/simple-examples")))]
    (mapv #(roundtrip-transit proc (read-bytes %) encoding)
          files)))

(defn test-corners-of-edn [proc encoding]
  (mapv #(roundtrip-edn proc % encoding) cc/forms))

(defn test-corners-of-transit-json [proc encoding]
  (mapv #(roundtrip-transit proc (.getBytes %) encoding) cc/transit-json))

(defn test-generated-edn [forms proc encoding]
  (mapv #(roundtrip-edn proc % encoding) forms))

(defn verify-impl-encoding [command encoding opts]
  (assert (contains? extension encoding)


(defn roundtrip-transit [proc transit-out encoding]
  (write-to-stream proc transit-out)
  (let [data-in (read-response proc transit-out TIMEOUT)
        data-out (read-transit transit-out encoding)]
    {:transit-expected (String. transit-out)
     :data-expected data-out
     :data-actual data-in
     ;; only checks the top level type
