mirror of
https://github.com/lunarmodules/luasocket.git
synced 2024-12-26 04:28:20 +01:00
New LTN12 test procedures (still short, but growing)
LTN12 avoids coroutines.
This commit is contained in:
parent
50da56dbee
commit
05e8f24385
1
FIX
1
FIX
@ -1,3 +1,4 @@
|
|||||||
|
ltn12 avoids coroutines (so you can go wild on the C side)
|
||||||
automated tests for ftp now in use
|
automated tests for ftp now in use
|
||||||
new compat-5.1 distribution
|
new compat-5.1 distribution
|
||||||
instalation should use new directory structure
|
instalation should use new directory structure
|
||||||
|
135
src/ltn12.lua
135
src/ltn12.lua
@ -35,64 +35,40 @@ function filter.cycle(low, ctx, extra)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--[[
|
-- chains a bunch of filters together
|
||||||
local function chain2(f1, f2)
|
-- (thanks to Wim Couwenberg)
|
||||||
local ff1, ff2 = "", ""
|
function filter.chain(...)
|
||||||
|
local n = table.getn(arg)
|
||||||
|
local top, index = 1, 1
|
||||||
return function(chunk)
|
return function(chunk)
|
||||||
local rf1 = chunk and ""
|
while true do
|
||||||
local rf2 = ff1 and ""
|
if index == top then
|
||||||
-- if f2 still has pending data, get it and return it
|
chunk = arg[index](chunk)
|
||||||
if ff2 ~= rf2 then
|
if chunk == "" or top == n then
|
||||||
ff2 = f2(rf2)
|
return chunk
|
||||||
if ff2 ~= "" then return ff2 end
|
elseif chunk then
|
||||||
end
|
index = index + 1
|
||||||
-- here we know f2 needs more data
|
else
|
||||||
-- we try to get it from f1
|
top = top+1
|
||||||
ff1 = f1(chunk)
|
index = top
|
||||||
while 1 do
|
end
|
||||||
-- if f1 can't produce data, we need more data from the user
|
else
|
||||||
if ff1 == "" then return "" end
|
local original = chunk
|
||||||
-- otherwise we pass new data to f2 until it produces something
|
chunk = arg[index](original or "")
|
||||||
-- or f1 runs out of data too
|
if chunk == "" then
|
||||||
ff2 = f2(ff1)
|
index = index - 1
|
||||||
if ff2 ~= "" then return ff2 end
|
chunk = original and chunk
|
||||||
ff1 = f1(rf1)
|
elseif chunk then
|
||||||
end
|
if index == n then return chunk
|
||||||
end
|
else index = index + 1 end
|
||||||
end
|
else
|
||||||
]]
|
base.error("filter returned inappropriate nil")
|
||||||
|
end
|
||||||
local function chain2(f1, f2)
|
|
||||||
local co = coroutine.create(function(chunk)
|
|
||||||
while true do
|
|
||||||
local filtered1 = f1(chunk)
|
|
||||||
local filtered2 = f2(filtered1)
|
|
||||||
local done2 = filtered1 and ""
|
|
||||||
while true do
|
|
||||||
if filtered2 == "" or filtered2 == nil then break end
|
|
||||||
coroutine.yield(filtered2)
|
|
||||||
filtered2 = f2(done2)
|
|
||||||
end
|
end
|
||||||
if filtered1 == "" then chunk = coroutine.yield(filtered1)
|
|
||||||
elseif filtered1 == nil then return nil
|
|
||||||
else chunk = chunk and "" end
|
|
||||||
end
|
end
|
||||||
end)
|
|
||||||
return function(chunk)
|
|
||||||
local _, res = coroutine.resume(co, chunk)
|
|
||||||
return res
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- chains a bunch of filters together
|
|
||||||
function filter.chain(...)
|
|
||||||
local f = arg[1]
|
|
||||||
for i = 2, table.getn(arg) do
|
|
||||||
f = chain2(f, arg[i])
|
|
||||||
end
|
|
||||||
return f
|
|
||||||
end
|
|
||||||
|
|
||||||
-----------------------------------------------------------------------------
|
-----------------------------------------------------------------------------
|
||||||
-- Source stuff
|
-- Source stuff
|
||||||
-----------------------------------------------------------------------------
|
-----------------------------------------------------------------------------
|
||||||
@ -165,23 +141,28 @@ end
|
|||||||
-- chains a source with a filter
|
-- chains a source with a filter
|
||||||
function source.chain(src, f)
|
function source.chain(src, f)
|
||||||
base.assert(src and f)
|
base.assert(src and f)
|
||||||
local co = coroutine.create(function()
|
local last_in, last_out = "", ""
|
||||||
while true do
|
|
||||||
local chunk, err = src()
|
|
||||||
if err then return nil, err end
|
|
||||||
local filtered = f(chunk)
|
|
||||||
local done = chunk and ""
|
|
||||||
while true do
|
|
||||||
coroutine.yield(filtered)
|
|
||||||
if filtered == done then break end
|
|
||||||
filtered = f(done)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
return function()
|
return function()
|
||||||
local ret, a, b = coroutine.resume(co)
|
if last_out == "" then
|
||||||
if ret then return a, b
|
while true do
|
||||||
else return nil, a end
|
local err
|
||||||
|
last_in, err = src()
|
||||||
|
if err then return nil, err end
|
||||||
|
last_out = f(last_in)
|
||||||
|
if last_out ~= "" then return last_out end
|
||||||
|
if not last_in then
|
||||||
|
error('filter returned inappropriate ""')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif last_out then
|
||||||
|
last_out = f(last_in and "")
|
||||||
|
if last_in and not last_out then
|
||||||
|
error('filter returned inappropriate nil')
|
||||||
|
end
|
||||||
|
return last_out
|
||||||
|
else
|
||||||
|
base.error("source is empty", 2)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -260,14 +241,16 @@ end
|
|||||||
function sink.chain(f, snk)
|
function sink.chain(f, snk)
|
||||||
base.assert(f and snk)
|
base.assert(f and snk)
|
||||||
return function(chunk, err)
|
return function(chunk, err)
|
||||||
local filtered = f(chunk)
|
if chunk ~= "" then
|
||||||
local done = chunk and ""
|
local filtered = f(chunk)
|
||||||
while true do
|
local done = chunk and ""
|
||||||
local ret, snkerr = snk(filtered, err)
|
while true do
|
||||||
if not ret then return nil, snkerr end
|
local ret, snkerr = snk(filtered, err)
|
||||||
if filtered == done then return 1 end
|
if not ret then return nil, snkerr end
|
||||||
filtered = f(done)
|
if filtered == done then return 1 end
|
||||||
end
|
filtered = f(done)
|
||||||
|
end
|
||||||
|
else return 1 end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -193,7 +193,7 @@ int sock_send(p_sock ps, const char *data, size_t count, size_t *sent, p_tm tm)
|
|||||||
*sent = 0;
|
*sent = 0;
|
||||||
for ( ;; ) {
|
for ( ;; ) {
|
||||||
/* try to send something */
|
/* try to send something */
|
||||||
int put = send(*ps, data, count, 0);
|
int put = send(*ps, data, (int) count, 0);
|
||||||
/* if we sent something, we are done */
|
/* if we sent something, we are done */
|
||||||
if (put > 0) {
|
if (put > 0) {
|
||||||
*sent = put;
|
*sent = put;
|
||||||
|
275
test/ltn12test.lua
Normal file
275
test/ltn12test.lua
Normal file
@ -0,0 +1,275 @@
|
|||||||
|
local ltn12 = require("ltn12")
|
||||||
|
|
||||||
|
dofile("testsupport.lua")
|
||||||
|
|
||||||
|
local function format(chunk)
|
||||||
|
if chunk then
|
||||||
|
if chunk == "" then return "''"
|
||||||
|
else return string.len(chunk) end
|
||||||
|
else return "nil" end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function show(name, input, output)
|
||||||
|
local sin = format(input)
|
||||||
|
local sout = format(output)
|
||||||
|
io.write(name, ": ", sin, " -> ", sout, "\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
local function chunked(length)
|
||||||
|
local tmp
|
||||||
|
return function(chunk)
|
||||||
|
local ret
|
||||||
|
if chunk and chunk ~= "" then
|
||||||
|
tmp = chunk
|
||||||
|
end
|
||||||
|
ret = string.sub(tmp, 1, length)
|
||||||
|
tmp = string.sub(tmp, length+1)
|
||||||
|
if not chunk and ret == "" then ret = nil end
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function named(f, name)
|
||||||
|
return function(chunk)
|
||||||
|
local ret = f(chunk)
|
||||||
|
show(name, chunk, ret)
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--------------------------------
|
||||||
|
local function split(size)
|
||||||
|
local buffer = ""
|
||||||
|
local last_out = ""
|
||||||
|
local last_in = ""
|
||||||
|
local function output(chunk)
|
||||||
|
local part = string.sub(buffer, 1, size)
|
||||||
|
buffer = string.sub(buffer, size+1)
|
||||||
|
last_out = (part ~= "" or chunk) and part
|
||||||
|
last_in = chunk
|
||||||
|
return last_out
|
||||||
|
end
|
||||||
|
return function(chunk, done)
|
||||||
|
if done then
|
||||||
|
return not last_in and not last_out
|
||||||
|
end
|
||||||
|
-- check if argument is consistent with state
|
||||||
|
if not chunk then
|
||||||
|
if last_in and last_in ~= "" and last_out ~= "" then
|
||||||
|
error("nil chunk following data chunk", 2)
|
||||||
|
end
|
||||||
|
if not last_out then error("extra nil chunk", 2) end
|
||||||
|
return output(chunk)
|
||||||
|
elseif chunk == "" then
|
||||||
|
if last_out == "" then error('extra "" chunk', 2) end
|
||||||
|
if not last_out then error('"" chunk following nil return', 2) end
|
||||||
|
if not last_in then error('"" chunk following nil chunk', 2) end
|
||||||
|
return output(chunk)
|
||||||
|
else
|
||||||
|
if not last_in then error("data chunk following nil chunk", 2) end
|
||||||
|
if last_in ~= "" and last_out ~= "" then
|
||||||
|
error("data chunk following data chunk", 2)
|
||||||
|
end
|
||||||
|
buffer = chunk
|
||||||
|
return output(chunk)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--------------------------------
|
||||||
|
local function format(chunk)
|
||||||
|
if chunk then
|
||||||
|
if chunk == "" then return "''"
|
||||||
|
else return string.len(chunk) end
|
||||||
|
else return "nil" end
|
||||||
|
end
|
||||||
|
|
||||||
|
--------------------------------
|
||||||
|
local function merge(size)
|
||||||
|
local buffer = ""
|
||||||
|
local last_out = ""
|
||||||
|
local last_in = ""
|
||||||
|
local function output(chunk)
|
||||||
|
local part
|
||||||
|
if string.len(buffer) >= size or not chunk then
|
||||||
|
part = buffer
|
||||||
|
buffer = ""
|
||||||
|
else
|
||||||
|
part = ""
|
||||||
|
end
|
||||||
|
last_out = (part ~= "" or chunk) and part
|
||||||
|
last_in = chunk
|
||||||
|
return last_out
|
||||||
|
end
|
||||||
|
return function(chunk, done)
|
||||||
|
if done then
|
||||||
|
return not last_in and not last_out
|
||||||
|
end
|
||||||
|
-- check if argument is consistent with state
|
||||||
|
if not chunk then
|
||||||
|
if last_in and last_in ~= "" and last_out ~= "" then
|
||||||
|
error("nil chunk following data chunk", 2)
|
||||||
|
end
|
||||||
|
if not last_out then error("extra nil chunk", 2) end
|
||||||
|
return output(chunk)
|
||||||
|
elseif chunk == "" then
|
||||||
|
if last_out == "" then error('extra "" chunk', 2) end
|
||||||
|
if not last_out then error('"" chunk following nil return', 2) end
|
||||||
|
if not last_in then error('"" chunk following nil chunk', 2) end
|
||||||
|
return output(chunk)
|
||||||
|
else
|
||||||
|
if not last_in then error("data chunk following nil chunk", 2) end
|
||||||
|
if last_in ~= "" and last_out ~= "" then
|
||||||
|
error("data chunk following data chunk", 2)
|
||||||
|
end
|
||||||
|
buffer = buffer .. chunk
|
||||||
|
return output(chunk)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--------------------------------
|
||||||
|
io.write("testing sink.table: ")
|
||||||
|
local sink, t = ltn12.sink.table()
|
||||||
|
local s, c = "", ""
|
||||||
|
for i = 0, 10 do
|
||||||
|
c = string.rep(string.char(i), i)
|
||||||
|
s = s .. c
|
||||||
|
assert(sink(c), "returned error")
|
||||||
|
end
|
||||||
|
assert(sink(nil), "returned error")
|
||||||
|
assert(table.concat(t) == s, "mismatch")
|
||||||
|
print("ok")
|
||||||
|
|
||||||
|
--------------------------------
|
||||||
|
io.write("testing sink.chain (with split): ")
|
||||||
|
sink, t = ltn12.sink.table()
|
||||||
|
local filter = split(3)
|
||||||
|
sink = ltn12.sink.chain(filter, sink)
|
||||||
|
s = "123456789012345678901234567890"
|
||||||
|
assert(sink(s), "returned error")
|
||||||
|
assert(sink(s), "returned error")
|
||||||
|
assert(sink(nil), "returned error")
|
||||||
|
assert(table.concat(t) == s .. s, "mismatch")
|
||||||
|
assert(filter(nil, 1), "filter not empty")
|
||||||
|
print("ok")
|
||||||
|
|
||||||
|
--------------------------------
|
||||||
|
io.write("testing sink.chain (with merge): ")
|
||||||
|
sink, t = ltn12.sink.table()
|
||||||
|
filter = merge(10)
|
||||||
|
sink = ltn12.sink.chain(filter, sink)
|
||||||
|
s = string.rep("123", 30)
|
||||||
|
s = s .. string.rep("4321", 30)
|
||||||
|
for i = 1, 30 do
|
||||||
|
assert(sink("123"), "returned error")
|
||||||
|
end
|
||||||
|
for i = 1, 30 do
|
||||||
|
assert(sink("4321"), "returned error")
|
||||||
|
end
|
||||||
|
assert(sink(nil), "returned error")
|
||||||
|
assert(filter(nil, 1), "filter not empty")
|
||||||
|
assert(table.concat(t) == s, "mismatch")
|
||||||
|
print("ok")
|
||||||
|
|
||||||
|
--------------------------------
|
||||||
|
io.write("testing source.string and pump.all: ")
|
||||||
|
local source = ltn12.source.string(s)
|
||||||
|
sink, t = ltn12.sink.table()
|
||||||
|
assert(ltn12.pump.all(source, sink), "returned error")
|
||||||
|
assert(table.concat(t) == s, "mismatch")
|
||||||
|
print("ok")
|
||||||
|
|
||||||
|
--------------------------------
|
||||||
|
io.write("testing source.chain (with split): ")
|
||||||
|
source = ltn12.source.string(s)
|
||||||
|
filter = split(5)
|
||||||
|
source = ltn12.source.chain(source, filter)
|
||||||
|
sink, t = ltn12.sink.table()
|
||||||
|
assert(ltn12.pump.all(source, sink), "returned error")
|
||||||
|
assert(table.concat(t) == s, "mismatch")
|
||||||
|
assert(filter(nil, 1), "filter not empty")
|
||||||
|
print("ok")
|
||||||
|
|
||||||
|
--------------------------------
|
||||||
|
io.write("testing source.chain (with split) and sink.chain (with merge): ")
|
||||||
|
source = ltn12.source.string(s)
|
||||||
|
filter = split(5)
|
||||||
|
source = ltn12.source.chain(source, filter)
|
||||||
|
local filter2 = merge(13)
|
||||||
|
sink, t = ltn12.sink.table()
|
||||||
|
sink = ltn12.sink.chain(filter2, sink)
|
||||||
|
assert(ltn12.pump.all(source, sink), "returned error")
|
||||||
|
assert(table.concat(t) == s, "mismatch")
|
||||||
|
assert(filter(nil, 1), "filter not empty")
|
||||||
|
assert(filter2(nil, 1), "filter2 not empty")
|
||||||
|
print("ok")
|
||||||
|
|
||||||
|
--------------------------------
|
||||||
|
io.write("testing filter.chain (and sink.chain, with split, merge): ")
|
||||||
|
source = ltn12.source.string(s)
|
||||||
|
filter = split(5)
|
||||||
|
filter2 = merge(13)
|
||||||
|
local chain = ltn12.filter.chain(filter, filter2)
|
||||||
|
sink, t = ltn12.sink.table()
|
||||||
|
sink = ltn12.sink.chain(chain, sink)
|
||||||
|
assert(ltn12.pump.all(source, sink), "returned error")
|
||||||
|
assert(table.concat(t) == s, "mismatch")
|
||||||
|
assert(filter(nil, 1), "filter not empty")
|
||||||
|
assert(filter2(nil, 1), "filter2 not empty")
|
||||||
|
print("ok")
|
||||||
|
|
||||||
|
--------------------------------
|
||||||
|
io.write("testing filter.chain (and sink.chain, a bunch): ")
|
||||||
|
source = ltn12.source.string(s)
|
||||||
|
filter = split(5)
|
||||||
|
filter2 = merge(13)
|
||||||
|
local filter3 = split(7)
|
||||||
|
local filter4 = merge(11)
|
||||||
|
local filter5 = split(10)
|
||||||
|
chain = ltn12.filter.chain(filter, filter2, filter3, filter4, filter5)
|
||||||
|
sink, t = ltn12.sink.table()
|
||||||
|
sink = ltn12.sink.chain(chain, sink)
|
||||||
|
assert(ltn12.pump.all(source, sink))
|
||||||
|
assert(table.concat(t) == s, "mismatch")
|
||||||
|
assert(filter(nil, 1), "filter not empty")
|
||||||
|
assert(filter2(nil, 1), "filter2 not empty")
|
||||||
|
assert(filter3(nil, 1), "filter3 not empty")
|
||||||
|
assert(filter4(nil, 1), "filter4 not empty")
|
||||||
|
assert(filter5(nil, 1), "filter5 not empty")
|
||||||
|
print("ok")
|
||||||
|
|
||||||
|
--------------------------------
|
||||||
|
io.write("testing filter.chain (and source.chain, with split, merge): ")
|
||||||
|
source = ltn12.source.string(s)
|
||||||
|
filter = split(5)
|
||||||
|
filter2 = merge(13)
|
||||||
|
local chain = ltn12.filter.chain(filter, filter2)
|
||||||
|
sink, t = ltn12.sink.table()
|
||||||
|
source = ltn12.source.chain(source, chain)
|
||||||
|
assert(ltn12.pump.all(source, sink), "returned error")
|
||||||
|
assert(table.concat(t) == s, "mismatch")
|
||||||
|
assert(filter(nil, 1), "filter not empty")
|
||||||
|
assert(filter2(nil, 1), "filter2 not empty")
|
||||||
|
print("ok")
|
||||||
|
|
||||||
|
--------------------------------
|
||||||
|
io.write("testing filter.chain (and source.chain, a bunch): ")
|
||||||
|
source = ltn12.source.string(s)
|
||||||
|
filter = split(5)
|
||||||
|
filter2 = merge(13)
|
||||||
|
local filter3 = split(7)
|
||||||
|
local filter4 = merge(11)
|
||||||
|
local filter5 = split(10)
|
||||||
|
chain = ltn12.filter.chain(filter, filter2, filter3, filter4, filter5)
|
||||||
|
sink, t = ltn12.sink.table()
|
||||||
|
source = ltn12.source.chain(source, chain)
|
||||||
|
assert(ltn12.pump.all(source, sink))
|
||||||
|
assert(table.concat(t) == s, "mismatch")
|
||||||
|
assert(filter(nil, 1), "filter not empty")
|
||||||
|
assert(filter2(nil, 1), "filter2 not empty")
|
||||||
|
assert(filter3(nil, 1), "filter3 not empty")
|
||||||
|
assert(filter4(nil, 1), "filter4 not empty")
|
||||||
|
assert(filter5(nil, 1), "filter5 not empty")
|
||||||
|
print("ok")
|
||||||
|
|
@ -47,42 +47,6 @@ local function random(handle, io_err)
|
|||||||
else return ltn12.source.empty(io_err or "unable to open file") end
|
else return ltn12.source.empty(io_err or "unable to open file") end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function format(chunk)
|
|
||||||
if chunk then
|
|
||||||
if chunk == "" then return "''"
|
|
||||||
else return string.len(chunk) end
|
|
||||||
else return "nil" end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function show(name, input, output)
|
|
||||||
local sin = format(input)
|
|
||||||
local sout = format(output)
|
|
||||||
io.write(name, ": ", sin, " -> ", sout, "\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
local function chunked(length)
|
|
||||||
local tmp
|
|
||||||
return function(chunk)
|
|
||||||
local ret
|
|
||||||
if chunk and chunk ~= "" then
|
|
||||||
tmp = chunk
|
|
||||||
end
|
|
||||||
ret = string.sub(tmp, 1, length)
|
|
||||||
tmp = string.sub(tmp, length+1)
|
|
||||||
if not chunk and ret == "" then ret = nil end
|
|
||||||
return ret
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--[[
|
|
||||||
local function named(f, name)
|
|
||||||
return function(chunk)
|
|
||||||
local ret = f(chunk)
|
|
||||||
show(name, chunk, ret)
|
|
||||||
return ret
|
|
||||||
end
|
|
||||||
end
|
|
||||||
]]
|
|
||||||
|
|
||||||
local function named(f)
|
local function named(f)
|
||||||
return f
|
return f
|
||||||
|
Loading…
Reference in New Issue
Block a user