First few external tests with test/unit, some minor tidying of internal data

structures.
This commit is contained in:
Matthew Bloch
2012-05-24 01:39:35 +01:00
parent d5d6e0f55d
commit 5a5041a751
8 changed files with 300 additions and 23 deletions

63
tests/flexnbd.rb Normal file
View File

@@ -0,0 +1,63 @@
require 'socket'
# Noddy test class to exercise FlexNBD from the outside for testing.
#
class FlexNBD
attr_reader :bin, :ctrl, :pid, :ip, :port
def initialize(bin, ip, port)
@bin = bin
raise "#{bin} not executable" unless File.executable?(bin)
@ctrl = "/tmp/.flexnbd.ctrl.#{Time.now.to_i}.#{rand}"
@ip = ip
@port = port
end
def serve(ip, port, file, *acl)
@pid = fork do
exec("#{@bin} serve #{ip} #{port} #{file} #{ctrl} #{acl.join(' ')}")
end
end
def kill
Process.kill("INT", @pid)
Process.wait(@pid)
end
def read(offset, length)
IO.popen("#{@bin} read #{ip} #{port} #{offset} #{length}","r") do |fh|
return fh.read
end
raise "read failed" unless $?.success?
end
def write(offset, data)
IO.popen("#{@bin} write #{ip} #{port} #{offset} #{data.length}","w") do |fh|
fh.write(data)
end
raise "write failed" unless $?.success?
nil
end
def mirror(bandwidth=nil, action=nil)
control_command("mirror", ip, port, bandwidth, action)
end
def acl(*acl)
control_command("acl", *acl)
end
def status
end
protected
def control_command(*args)
raise "Server not running" unless @pid
args = args.compact
UNIXSocket.open(@ctrl) do |u|
u.write(args.join("\n") + "\n")
code, message = u.readline.split(": ", 2)
return [code, message]
end
end
end

71
tests/nbd_scenarios Normal file
View File

@@ -0,0 +1,71 @@
#!/usr/bin/ruby
require 'test/unit'
require 'flexnbd'
require 'test_file_writer'
class NBDScenarios < Test::Unit::TestCase
def setup
@blocksize = 1024
@filename1 = ".flexnbd.test.#{$$}.#{Time.now.to_i}.1"
@filename2 = ".flexnbd.test.#{$$}.#{Time.now.to_i}.2"
@ip = "127.0.0.1"
@available_ports = [*40000..41000] - listening_ports
@port1 = @available_ports.shift
@port2 = @available_ports.shift
@nbd1 = FlexNBD.new("../flexnbd", @ip, @port1)
end
def teardown
@nbd1.kill rescue nil
[@filename1, @filename2].each do |f|
File.unlink(f) if File.exists?(f)
end
end
def test_read1
writefile1("f"*64)
serve1
[0, 12, 63].each do |num|
assert_equal(
@nbd1.read(num*@blocksize, @blocksize),
@file1.read(num*@blocksize, @blocksize)
)
end
[124, 1200, 10028, 25488].each do |num|
assert_equal(@nbd1.read(num, 4), @file1.read(num, 4))
end
end
def test_writeread1
writefile1("0"*64)
serve1
[0, 12, 63].each do |num|
data = "X"*@blocksize
@nbd1.write(num*@blocksize, data)
assert_equal(data, @file1.read(num*@blocksize, data.size))
assert_equal(data, @nbd1.read(num*@blocksize, data.size))
end
end
protected
def serve1(*acl)
@nbd1.serve(@ip, @port1, @filename1, *acl)
end
def writefile1(data)
@file1 = TestFileWriter.new(@filename1, @blocksize).write(data)
end
def listening_ports
`netstat -ltn`.
split("\n").
map { |x| x.split(/\s+/) }[2..-1].
map { |l| l[3].split(":")[-1].to_i }
end
end

83
tests/test_file_writer.rb Normal file
View File

@@ -0,0 +1,83 @@
# Noddy test class for writing files to disc in predictable patterns
# in order to test FlexNBD.
#
class TestFileWriter
def initialize(filename, blocksize)
@fh = File.open(filename, "w+")
@blocksize = blocksize
@pattern = ""
end
# We write in fixed block sizes, given by "blocksize"
# _ means skip a block
# 0 means write a block full of zeroes
# f means write a block with the file offset packed every 4 bytes
#
def write(data)
@pattern += data
data.split("").each do |code|
if code == "_"
@fh.seek(@blocksize, IO::SEEK_CUR)
else
@fh.write(data(code))
end
end
@fh.flush
self
end
# Returns what the data ought to be at the given offset and length
#
def read_original(off, len)
r=""
current = 0
@pattern.split("").each do |block|
if off >= current && (off+len) < current + blocksize
current += data(block, current)[
current-off..(current+blocksize)-(off+len)
]
end
current += @blocksize
end
r
end
# Read what's actually in the file
#
def read(off, len)
@fh.seek(off, IO::SEEK_SET)
@fh.read(len)
end
def untouched?(offset, len)
read(off, len) == read_original(off, len)
end
def close
@fh.close
nil
end
protected
def data(code, at=@fh.tell)
case code
when "0", "_"
"\0" * @blocksize
when "X"
"X" * @blocksize
when "f"
r = ""
(@blocksize/4).times do
r += [at].pack("I")
at += 4
end
r
else
raise "Unknown character '#{block}'"
end
end
end