core-async

2024-09-18T17:29:45.897269Z

(s/def ::a nat-int?)
(s/def ::data
  (s/keys :req [::a]))

(deftest a-test
  (let [data (gen/sample (s/gen ::data) 200)
        out-chan (a/chan 10)
        in-chan (a/chan 10)
        f (fn [{::keys [a]}]
            (when (odd? a)
              a))
        data-filtered (into [] (keep f) data)]
    (a/pipeline 4
                out-chan
                (keep f)
                in-chan)
    (a/go (doseq [d data]
            (a/>! in-chan d)))
    (a/go-loop [i 0]
      (println (a/<! out-chan))
      (is (= (nth (reverse data-filtered) i)
             (a/<! out-chan)))
      (recur (inc i)))))
in this case, this test prints and run

2024-09-18T17:56:39.282299Z

you are spinning off logical threads (go blocks) but not waiting around for them, which will break things a lot of the time when running tests

2024-09-18T17:58:23.266149Z

running the test a-test expects the testing to be complete when the function of the test returns

2024-09-18T17:58:41.212859Z

imagine every go block is a use of future

2024-09-18T18:08:31.161019Z

channels are mutable, you are taking something out of the channel to print it, then taking something out of the channel to compare it to something in data-filtered

👍 1
Bob B 2024-09-18T18:15:15.221909Z

also, the reversing isn't necessary...

(deftest a-test
  (let [data (gen/sample (s/gen ::data) 200)
        out-chan (a/chan 10)
        in-chan (a/chan 10)
        f (fn [{::keys [a]}]
            (when (odd? a)
              a))
        data-filtered (into [] (keep f) data)]
    (a/pipeline 4
      out-chan
      (keep f)
      in-chan)
    (a/go (doseq [d data]
            (a/>! in-chan d)))
    (a/go-loop [i 0]
      (when-let [v (a/<! out-chan)]
        (println v)
        (is (= (nth data-filtered i) v))
        (recur (inc i))))))
this "passes" (in the sense that all the equalities seem to check out), but like hired said, the is calls happen asynchronously to the deftest, so the test technically passes regardless of what happens in the go-loop body

🤔 1
Bob B 2024-09-18T18:23:43.813909Z

if we make the loop synchronous, it "works" from a clojure.test standpoint... whether it does what's desired or not is less clear. I also made in-chan a chan from the data at the beginning, which isn't necessarily relevant, but just a bit shorter:

(deftest a-test
  (let [data (gen/sample (s/gen ::data) 200)
        out-chan (a/chan 10)
        in-chan (a/to-chan! data)
        f (fn [{::keys [a]}]
            (when (odd? a)
              a))
        data-filtered (into [] (keep f) data)]
    (a/pipeline 4
      out-chan
      (keep f)
      in-chan)
    (loop [i 0]
      (when-let [v (a/<!! out-chan)]
        (println v)
        (is (= (nth data-filtered i) v))
        (recur (inc i))))))

2024-09-18T18:24:23.876169Z

there is nothing that guarantees an ordering between the execution of code in the go blocks and the completion of the test, so technically it is a race, the go blocks just run fast enough in this case that usually they complete before the test does

Bob B 2024-09-18T18:39:21.718679Z

I guess another option would also be that since the go-loop returns a channel, do a blocking take from that channel, and as long as the go-loop eventually returns (which can be returning nil from the when-let), that should bridge the synch of the deftest.

👍 1
2024-09-18T17:29:59.482049Z

it fails, but it prints and run

2024-09-18T17:35:04.628749Z

is there any way to make this test pass ?