nim-faststreams/tests/test_output_stream.nim
2020-04-11 23:31:08 +03:00

154 lines
4.1 KiB
Nim

import
os, unittest, random,
stew/ranges/ptr_arith,
../faststreams
proc bytes(s: string): seq[byte] =
result = newSeqOfCap[byte](s.len)
for c in s: result.add byte(c)
template bytes(c: char): byte = byte(c)
template bytes(b: seq[byte]): seq[byte] = b
proc repeat(b: byte, count: int): seq[byte] =
result = newSeq[byte](count)
for i in 0 ..< count: result[i] = b
proc randomBytes(n: int): seq[byte] =
result.newSeq n
for i in 0 ..< n:
result[i] = byte(rand(255))
suite "output stream":
setup:
var memStream = memoryOutput()
var altOutput: seq[byte] = @[]
var tempFilePath = getTempDir() / "faststreams_testfile"
var fileStream = fileOutput(tempFilePath)
const bufferSize = 1000000
var buffer = alloc(bufferSize)
var existingBufferStream = memoryOutput(buffer, bufferSize)
teardown:
removeFile tempFilePath
template output(val: auto) {.dirty.} =
altOutput.add bytes(val)
memStream.append val
fileStream.append val
existingBufferStream.append val
template checkOutputsMatch =
fileStream.flush
let
fileContents = readFile(tempFilePath).string.bytes
memStreamContents = memStream.getOutput
let outputsMatch =
altOutput == memStreamContents and
altOutput == makeOpenArray(cast[ptr byte](buffer),
existingBufferStream.pos)
check outputsMatch
test "no appends produce an empty output":
checkOutputsMatch()
test "append zero length slice":
output ""
checkOutputsMatch()
test "string output":
for i in 0 .. 1:
output $i
output " bottles on the wall"
output '\n'
checkOutputsMatch()
test "delayed write":
output "initial output\n"
const delayedWriteContent = bytes "delayed write\n"
var cursor = memStream.delayFixedSizeWrite(delayedWriteContent.len)
let cursorStart = memStream.pos
altOutput.add delayedWriteContent
fileStream.append delayedWriteContent
existingBufferStream.append delayedWriteContent
var totalBytesWritten = 0
for i, count in [12, 342, 2121, 23, 1, 34012, 932]:
output repeat(byte(i), count)
totalBytesWritten += count
check memStream.pos - cursorStart == totalBytesWritten
cursor.writeAndFinalize delayedWriteContent
checkOutputsMatch()
test "multi-page delayed writes":
randomize(1000)
type
DelayedWrite = object
cursor: WriteCursor
content: seq[byte]
written: int
var delayedWrites = newSeq[DelayedWrite]()
for i in 0..50:
let
size = rand(8000) + 2000
randomBytes = randomBytes(size)
decision = rand(100)
if decision < 70:
# Write at some random cursor
if delayedWrites.len == 0:
continue
let
i = rand(delayedWrites.len - 1)
written = delayedWrites[i].written
remaining = delayedWrites[i].content.len - written
toWrite = min(rand(remaining) + 10, remaining)
delayedWrites[i].cursor.append delayedWrites[i].content[written ..< written + toWrite]
delayedWrites[i].written += toWrite
if remaining - toWrite == 0:
finalize delayedWrites[i].cursor
if i != delayedWrites.len - 1:
swap(delayedWrites[i], delayedWrites[^1])
delayedWrites.setLen(delayedWrites.len - 1)
elif decision < 90:
# Normal write
memStream.append randomBytes
altOutput.add randomBytes
else:
# Create cursor
altOutput.add randomBytes
delayedWrites.add DelayedWrite(
cursor: memStream.delayFixedSizeWrite(randomBytes.len),
content: randomBytes,
written: 0)
# Check that the stream position is consistently tracked at every step
check altOutput.len == memStream.pos
# Write all unwritten data to all outstanding cursors
for dw in mitems(delayedWrites):
let remaining = dw.content.len - dw.written
dw.cursor.append dw.content[dw.written ..< dw.written + remaining]
finalize dw.cursor
# The final outputs are the same
check altOutput == memStream.getOutput