Fork me on GitHub

is there a way to drain a channel? specifically for testing


As in, take until it blocks? But don't block?


Or take until it closes?


take until it blocks or closes


blocks probably would be ideal


the code being tested will publish a known number of messages to the channel but trying to avoid the looping and simplify the code in the test


You can use poll! and just poll! until it returns nil


that should do it! thanks!


I think it's possibly not perfect, because as you poll! it is technically possible that you polled the last thing pending in the channel, but by the time the next iteration calls poll again, something was delivered to the channel.


right, there is, technically, a race condition right?


So there's a small time window where poll! could have failed, but because something got concurrently delivered it'll succeed another round.


Like if I run this:

(def chan (a/chan 100))
(def ccount (atom 0))
  (dotimes [i 300]
    (a/>! chan i)))
(while (a/poll! chan)
  (swap! ccount inc))
(println @ccount)
It doesn't always print 100 perfectly. Sometimes it is a bit more. Probably because the go block added more things when poll! polled the 100ed item, and by the time poll! was call again there was now more things to poll.


The thing is, you can't lock the channel 😛 that's kind of the whole point of core.async. So I'm not sure if there's a better way.


There might be an interop into the implementation that could return the buffer count


Seems you can do this:

(count (.-buf chan))


And it'll just tell you how many things are in the channel's buffer


(.full? (.-buf chan))
This tells you if it is full. So in your test, you could check until it is full?, and then get the count to see if it is the count you expect.


It won't be thread safe, but I think for your use case it is fine.


  (def chan (a/chan 100))
    (dotimes [i 300]
      (a/>! chan i)))
  (while (not (.full? (.-buf chan))))
  (println (count (.-buf chan))))
Always gives me 100 exactly

Ben Sless02:04:36

Use async/reduce and collect everything that way. It will return a go block


it seems like the first poll on the chan is nil so it always fails 😕

Ben Sless05:04:01

@U4ZJ5UHQD don't use poll in this case, use reduce. Reduce is always the way to drain/consume a source. It respects channel semantics, blocking and time. Poll doesn't


Reduce will drain till close though, not till the first yield


> ch must close before reduce produces a result


I guess it depends what the ask is. But I understood it as, can I test that I have delivered what I expected to the channel buffer till first park/block due to the buffer being full.

Ben Sless05:04:51

If it's to first yield then it's not a drain. Just do a blocking take

Ben Sless05:04:16

Besides, producer should close channel on finish