2020-12-21 11:45:07 +00:00
|
|
|
{.used.}
|
|
|
|
|
|
|
|
import
|
|
|
|
os, unittest, strutils, random,
|
|
|
|
stew/ranges/ptr_arith, testutils,
|
|
|
|
../faststreams, ../faststreams/textio
|
|
|
|
|
|
|
|
setCurrentDir getAppDir()
|
|
|
|
|
|
|
|
proc bytes(s: string): seq[byte] =
|
|
|
|
result = newSeqOfCap[byte](s.len)
|
|
|
|
for c in s: result.add byte(c)
|
|
|
|
|
|
|
|
proc str(bytes: openarray[byte]): string =
|
|
|
|
result = newStringOfCap(bytes.len)
|
|
|
|
for b in items(bytes):
|
|
|
|
result.add b.char
|
|
|
|
|
|
|
|
proc countLines(s: InputStream): Natural =
|
|
|
|
for s in lines(s):
|
|
|
|
inc result
|
|
|
|
|
|
|
|
proc readAll(s: InputStream): seq[byte] =
|
|
|
|
while s.readable:
|
|
|
|
result.add s.read
|
|
|
|
|
|
|
|
const
|
|
|
|
asciiTableFile = "files" / "ascii_table.txt"
|
|
|
|
asciiTableContents = slurp(asciiTableFile)
|
|
|
|
|
|
|
|
procSuite "input stream":
|
|
|
|
template emptyInputTests(suiteName, setupCode: untyped) =
|
|
|
|
suite suiteName & " empty inputs":
|
|
|
|
setup setupCode
|
|
|
|
|
|
|
|
test "input is not readable with read":
|
|
|
|
check not input.readable
|
|
|
|
when not defined(danger):
|
|
|
|
expect Defect:
|
|
|
|
echo "This read should not complete: ", input.read
|
|
|
|
|
|
|
|
test "input is not readable with read(n)":
|
|
|
|
check not input.readable(10)
|
|
|
|
when not defined(danger):
|
|
|
|
expect Defect:
|
|
|
|
echo "This read should not complete: ", input.read(10)
|
|
|
|
|
|
|
|
test "next returns none":
|
|
|
|
check input.next.isNone
|
|
|
|
|
|
|
|
emptyInputTests "memoryInput":
|
|
|
|
var input = InputStream()
|
|
|
|
|
|
|
|
emptyInputTests "unsafeMemoryInput":
|
|
|
|
var str = ""
|
|
|
|
var input = unsafeMemoryInput(str)
|
|
|
|
|
|
|
|
emptyInputTests "fileInput":
|
|
|
|
var input = fileInput("files" / "empty_file")
|
|
|
|
|
|
|
|
emptyInputTests "memFileInput":
|
|
|
|
var input = memFileInput("files" / "empty_file")
|
|
|
|
|
|
|
|
template asciiTableFileTest(name: string, body: untyped) =
|
|
|
|
test name & " of ascii table with regular pageSize":
|
|
|
|
var input {.inject.} = fileInput(asciiTableFile)
|
|
|
|
try:
|
|
|
|
body
|
|
|
|
finally:
|
|
|
|
close input
|
|
|
|
|
|
|
|
test name & " of ascii table with pageSize = 10":
|
|
|
|
var input {.inject.} = fileInput(asciiTableFile, pageSize = 10)
|
|
|
|
try:
|
|
|
|
body
|
|
|
|
finally:
|
|
|
|
close input
|
|
|
|
|
|
|
|
test name & " of ascii table with pageSize = 1":
|
|
|
|
var input {.inject.} = fileInput(asciiTableFile, pageSize = 1)
|
|
|
|
try:
|
|
|
|
body
|
|
|
|
finally:
|
|
|
|
close input
|
|
|
|
|
|
|
|
# TODO: fileInput with offset
|
|
|
|
# - in the middle of the
|
|
|
|
# - right at the end of the file
|
|
|
|
# - past the end of the file
|
|
|
|
|
|
|
|
asciiTableFileTest "count lines":
|
|
|
|
check countLines(input) == 34
|
|
|
|
|
|
|
|
asciiTableFileTest "mixed read types":
|
|
|
|
randomize(10000)
|
|
|
|
|
|
|
|
var fileContents = ""
|
|
|
|
|
|
|
|
while true:
|
|
|
|
let r = rand(100)
|
|
|
|
if r < 20:
|
|
|
|
let readSize = 1 + rand(10)
|
|
|
|
|
|
|
|
var buf = newSeq[byte](readSize)
|
|
|
|
let bytesRead = input.readIntoEx(buf)
|
|
|
|
fileContents.add buf.toOpenArray(0, bytesRead - 1).str
|
|
|
|
|
|
|
|
if bytesRead < buf.len:
|
|
|
|
break
|
|
|
|
|
|
|
|
elif r < 50:
|
|
|
|
let readSize = 6 + rand(10)
|
|
|
|
|
|
|
|
if input.readable(readSize):
|
|
|
|
fileContents.add input.read(readSize).str
|
|
|
|
else:
|
|
|
|
while input.readable:
|
|
|
|
fileContents.add input.read.char
|
|
|
|
break
|
|
|
|
|
|
|
|
else:
|
|
|
|
if input.readable:
|
|
|
|
fileContents.add input.read.char
|
|
|
|
|
|
|
|
# You can uncomment this to get earlier failure in the test:
|
|
|
|
when false:
|
|
|
|
require fileContents == asciiTableContents[0 ..< fileContents.len]
|
|
|
|
|
|
|
|
check fileContents == asciiTableContents
|
|
|
|
|
|
|
|
test "missing file input":
|
|
|
|
const fileName = "there-is-no-such-faststreams-file"
|
|
|
|
|
|
|
|
check not fileExists(fileName)
|
|
|
|
expect CatchableError: discard fileInput(fileName)
|
|
|
|
|
|
|
|
check not fileExists(fileName)
|
|
|
|
expect CatchableError: discard memFileInput(fileName)
|
|
|
|
|
|
|
|
check not fileExists(fileName)
|
|
|
|
|
|
|
|
test "non-blocking reads":
|
|
|
|
let s = fileInput(asciiTableFile, pageSize = 100)
|
|
|
|
if s.readable(20):
|
|
|
|
s.withReadableRange(20, r):
|
|
|
|
check r.len.get == 20
|
|
|
|
check r.totalUnconsumedBytes == 20
|
|
|
|
check r.readAll.len == 20
|
|
|
|
|
|
|
|
check s.readable
|
|
|
|
|
|
|
|
if s.readable(200):
|
|
|
|
s.withReadableRange(200, r):
|
|
|
|
check r.len.get == 200
|
|
|
|
check r.totalUnconsumedBytes == 200
|
|
|
|
check r.readAll.len == 200
|
|
|
|
|
|
|
|
check s.readable
|
|
|
|
|
|
|
|
test "simple":
|
|
|
|
var input = repeat("1234 5678 90AB CDEF\n", 1000)
|
|
|
|
var stream = unsafeMemoryInput(input)
|
|
|
|
|
|
|
|
check:
|
|
|
|
(stream.read(4) == "1234".toOpenArrayByte(0, 3))
|
|
|
|
|
2021-03-17 09:20:33 +00:00
|
|
|
template posTest(name: string, setup: untyped) =
|
|
|
|
test name:
|
|
|
|
setup
|
|
|
|
check input.readable
|
|
|
|
check input.pos == 0
|
|
|
|
discard input.read
|
|
|
|
check input.pos == 1
|
|
|
|
close input
|
|
|
|
|
|
|
|
posTest "unsafeMemoryInput pos":
|
|
|
|
var str = "hello"
|
|
|
|
var input = unsafeMemoryInput(str)
|
|
|
|
|
|
|
|
posTest "fileInput pos":
|
|
|
|
var input = fileInput(asciiTableFile)
|
|
|
|
|
|
|
|
posTest "memFileInput pos":
|
|
|
|
var input = memFileInput(asciiTableFile)
|