rbuffer_spec.lua 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. local helpers = require("test.unit.helpers")(after_each)
  2. local itp = helpers.gen_itp(it)
  3. local eq = helpers.eq
  4. local ffi = helpers.ffi
  5. local cstr = helpers.cstr
  6. local to_cstr = helpers.to_cstr
  7. local child_call_once = helpers.child_call_once
  8. local rbuffer = helpers.cimport("./test/unit/fixtures/rbuffer.h")
  9. describe('rbuffer functions', function()
  10. local capacity = 16
  11. local rbuf
  12. local function inspect()
  13. return ffi.string(rbuf.start_ptr, capacity)
  14. end
  15. local function write(str)
  16. local buf = to_cstr(str)
  17. return rbuffer.rbuffer_write(rbuf, buf, #str)
  18. end
  19. local function read(len)
  20. local buf = cstr(len)
  21. len = rbuffer.rbuffer_read(rbuf, buf, len)
  22. return ffi.string(buf, len)
  23. end
  24. local function get(idx)
  25. return ffi.string(rbuffer.rbuffer_get(rbuf, idx), 1)
  26. end
  27. before_each(function()
  28. child_call_once(function()
  29. rbuf = ffi.gc(rbuffer.rbuffer_new(capacity), rbuffer.rbuffer_free)
  30. -- fill the internal buffer with the character '0' to simplify inspecting
  31. ffi.C.memset(rbuf.start_ptr, string.byte('0'), capacity)
  32. end)
  33. end)
  34. describe('RBUFFER_UNTIL_FULL', function()
  35. local chunks
  36. local function collect_write_chunks()
  37. rbuffer.ut_rbuffer_each_write_chunk(rbuf, function(wptr, wcnt)
  38. table.insert(chunks, ffi.string(wptr, wcnt))
  39. end)
  40. end
  41. before_each(function()
  42. chunks = {}
  43. end)
  44. describe('with empty buffer in one contiguous chunk', function()
  45. itp('is called once with the empty chunk', function()
  46. collect_write_chunks()
  47. eq({'0000000000000000'}, chunks)
  48. end)
  49. end)
  50. describe('with partially empty buffer in one contiguous chunk', function()
  51. itp('is called once with the empty chunk', function()
  52. write('string')
  53. collect_write_chunks()
  54. eq({'0000000000'}, chunks)
  55. end)
  56. end)
  57. describe('with filled buffer in one contiguous chunk', function()
  58. itp('is not called', function()
  59. write('abcdefghijklmnopq')
  60. collect_write_chunks()
  61. eq({}, chunks)
  62. end)
  63. end)
  64. describe('with buffer partially empty in two contiguous chunks', function()
  65. itp('is called twice with each filled chunk', function()
  66. write('1234567890')
  67. read(8)
  68. collect_write_chunks()
  69. eq({'000000', '12345678'}, chunks)
  70. end)
  71. end)
  72. describe('with buffer empty in two contiguous chunks', function()
  73. itp('is called twice with each filled chunk', function()
  74. write('12345678')
  75. read(8)
  76. collect_write_chunks()
  77. eq({'00000000', '12345678'}, chunks)
  78. end)
  79. end)
  80. describe('with buffer filled in two contiguous chunks', function()
  81. itp('is not called', function()
  82. write('12345678')
  83. read(8)
  84. write('abcdefghijklmnopq')
  85. collect_write_chunks()
  86. eq({}, chunks)
  87. end)
  88. end)
  89. end)
  90. describe('RBUFFER_UNTIL_EMPTY', function()
  91. local chunks
  92. local function collect_read_chunks()
  93. rbuffer.ut_rbuffer_each_read_chunk(rbuf, function(rptr, rcnt)
  94. table.insert(chunks, ffi.string(rptr, rcnt))
  95. end)
  96. end
  97. before_each(function()
  98. chunks = {}
  99. end)
  100. describe('with empty buffer', function()
  101. itp('is not called', function()
  102. collect_read_chunks()
  103. eq({}, chunks)
  104. end)
  105. end)
  106. describe('with partially filled buffer in one contiguous chunk', function()
  107. itp('is called once with the filled chunk', function()
  108. write('string')
  109. collect_read_chunks()
  110. eq({'string'}, chunks)
  111. end)
  112. end)
  113. describe('with filled buffer in one contiguous chunk', function()
  114. itp('is called once with the filled chunk', function()
  115. write('abcdefghijklmnopq')
  116. collect_read_chunks()
  117. eq({'abcdefghijklmnop'}, chunks)
  118. end)
  119. end)
  120. describe('with buffer partially filled in two contiguous chunks', function()
  121. itp('is called twice with each filled chunk', function()
  122. write('1234567890')
  123. read(10)
  124. write('long string')
  125. collect_read_chunks()
  126. eq({'long s', 'tring'}, chunks)
  127. end)
  128. end)
  129. describe('with buffer filled in two contiguous chunks', function()
  130. itp('is called twice with each filled chunk', function()
  131. write('12345678')
  132. read(8)
  133. write('abcdefghijklmnopq')
  134. collect_read_chunks()
  135. eq({'abcdefgh', 'ijklmnop'}, chunks)
  136. end)
  137. end)
  138. end)
  139. describe('RBUFFER_EACH', function()
  140. local chars
  141. local function collect_chars()
  142. rbuffer.ut_rbuffer_each(rbuf, function(c, i)
  143. table.insert(chars, {string.char(c), tonumber(i)})
  144. end)
  145. end
  146. before_each(function()
  147. chars = {}
  148. end)
  149. describe('with empty buffer', function()
  150. itp('is not called', function()
  151. collect_chars()
  152. eq({}, chars)
  153. end)
  154. end)
  155. describe('with buffer filled in two contiguous chunks', function()
  156. itp('collects each character and index', function()
  157. write('1234567890')
  158. read(10)
  159. write('long string')
  160. collect_chars()
  161. eq({{'l', 0}, {'o', 1}, {'n', 2}, {'g', 3}, {' ', 4}, {'s', 5},
  162. {'t', 6}, {'r', 7}, {'i', 8}, {'n', 9}, {'g', 10}}, chars)
  163. end)
  164. end)
  165. end)
  166. describe('RBUFFER_EACH_REVERSE', function()
  167. local chars
  168. local function collect_chars()
  169. rbuffer.ut_rbuffer_each_reverse(rbuf, function(c, i)
  170. table.insert(chars, {string.char(c), tonumber(i)})
  171. end)
  172. end
  173. before_each(function()
  174. chars = {}
  175. end)
  176. describe('with empty buffer', function()
  177. itp('is not called', function()
  178. collect_chars()
  179. eq({}, chars)
  180. end)
  181. end)
  182. describe('with buffer filled in two contiguous chunks', function()
  183. itp('collects each character and index', function()
  184. write('1234567890')
  185. read(10)
  186. write('long string')
  187. collect_chars()
  188. eq({{'g', 10}, {'n', 9}, {'i', 8}, {'r', 7}, {'t', 6}, {'s', 5},
  189. {' ', 4}, {'g', 3}, {'n', 2}, {'o', 1}, {'l', 0}}, chars)
  190. end)
  191. end)
  192. end)
  193. describe('rbuffer_cmp', function()
  194. local function cmp(str)
  195. local rv = rbuffer.rbuffer_cmp(rbuf, to_cstr(str), #str)
  196. if rv == 0 then
  197. return 0
  198. else
  199. return rv / math.abs(rv)
  200. end
  201. end
  202. describe('with buffer filled in two contiguous chunks', function()
  203. itp('compares the common longest sequence', function()
  204. write('1234567890')
  205. read(10)
  206. write('long string')
  207. eq(0, cmp('long string'))
  208. eq(0, cmp('long strin'))
  209. eq(-1, cmp('long striM'))
  210. eq(1, cmp('long strio'))
  211. eq(0, cmp('long'))
  212. eq(-1, cmp('lonG'))
  213. eq(1, cmp('lonh'))
  214. end)
  215. end)
  216. describe('with empty buffer', function()
  217. itp('returns 0 since no characters are compared', function()
  218. eq(0, cmp(''))
  219. end)
  220. end)
  221. end)
  222. describe('rbuffer_write', function()
  223. itp('fills the internal buffer and returns the write count', function()
  224. eq(12, write('short string'))
  225. eq('short string0000', inspect())
  226. end)
  227. itp('wont write beyond capacity', function()
  228. eq(16, write('very very long string'))
  229. eq('very very long s', inspect())
  230. end)
  231. end)
  232. describe('rbuffer_read', function()
  233. itp('reads what was previously written', function()
  234. write('to read')
  235. eq('to read', read(20))
  236. end)
  237. itp('reads nothing if the buffer is empty', function()
  238. eq('', read(20))
  239. write('empty')
  240. eq('empty', read(20))
  241. eq('', read(20))
  242. end)
  243. end)
  244. describe('rbuffer_get', function()
  245. itp('fetch the pointer at offset, wrapping if required', function()
  246. write('1234567890')
  247. read(10)
  248. write('long string')
  249. eq('l', get(0))
  250. eq('o', get(1))
  251. eq('n', get(2))
  252. eq('g', get(3))
  253. eq(' ', get(4))
  254. eq('s', get(5))
  255. eq('t', get(6))
  256. eq('r', get(7))
  257. eq('i', get(8))
  258. eq('n', get(9))
  259. eq('g', get(10))
  260. end)
  261. end)
  262. describe('wrapping behavior', function()
  263. itp('writing/reading wraps across the end of the internal buffer', function()
  264. write('1234567890')
  265. eq('1234', read(4))
  266. eq('5678', read(4))
  267. write('987654321')
  268. eq('3214567890987654', inspect())
  269. eq('90987654321', read(20))
  270. eq('', read(4))
  271. write('abcdefghijklmnopqrs')
  272. eq('nopabcdefghijklm', inspect())
  273. eq('abcdefghijklmnop', read(20))
  274. end)
  275. end)
  276. end)