From c1b0e922de5247203b89f37737c6ae0ed9d4cb55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20B=C3=B6rjesson?= Date: Fri, 15 Mar 2024 08:58:14 +0100 Subject: [PATCH 1/2] Raise more specific error when closed --- src/lavinmq/mfile.cr | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/lavinmq/mfile.cr b/src/lavinmq/mfile.cr index d5f080c237..aef14346b7 100644 --- a/src/lavinmq/mfile.cr +++ b/src/lavinmq/mfile.cr @@ -18,6 +18,12 @@ end # not `size` large, only on graceful close is the file truncated to its `size`. # The file does not expand further than initial `capacity`, unless manually expanded. class MFile < IO + class ClosedError < IO::Error + def initialize + super("MFile closed") + end + end + getter pos : Int64 = 0i64 getter? closed : Bool = false getter size : Int64 = 0i64 @@ -170,7 +176,7 @@ class MFile < IO end def write(slice : Bytes) : Nil - raise IO::Error.new("MFile closed") if @closed + raise ClosedError.new if @closed size = @size new_size = size + slice.size raise IO::EOFError.new if new_size > @capacity @@ -179,7 +185,7 @@ class MFile < IO end def read(slice : Bytes) - raise IO::Error.new("MFile closed") if @closed + raise ClosedError.new if @closed pos = @pos new_pos = pos + slice.size raise IO::EOFError.new if new_pos > @size @@ -225,12 +231,12 @@ class MFile < IO end def to_slice - raise IO::Error.new("MFile closed") if @closed + raise ClosedError.new if @closed Bytes.new(buffer, @size, read_only: true) end def to_slice(pos, size) - raise IO::Error.new("MFile closed") if @closed + raise ClosedError.new if @closed raise IO::EOFError.new if pos + size > @size Bytes.new(buffer + pos, size, read_only: true) end From 0ef08dc6b19c4388041f2dace16d3da2309c51f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20B=C3=B6rjesson?= Date: Fri, 15 Mar 2024 11:19:07 +0100 Subject: [PATCH 2/2] Add specs to verify ClosedError is raised --- spec/mfile_spec.cr | 63 ++++++++++++++++++++++++++++++++++++++++++++ src/lavinmq/mfile.cr | 1 + 2 files changed, 64 insertions(+) create mode 100644 spec/mfile_spec.cr diff --git a/spec/mfile_spec.cr b/spec/mfile_spec.cr new file mode 100644 index 0000000000..e9f5e92bb5 --- /dev/null +++ b/spec/mfile_spec.cr @@ -0,0 +1,63 @@ +require "spec" +require "../src/lavinmq/mfile" + +module MFileSpec + def self.with_file(&) + file = File.tempfile "mfile_spec" + yield file + ensure + file.delete unless file.nil? + end + + describe MFile do + describe "#write" do + it "should raise ClosedError if closed" do + with_file do |file| + mfile = MFile.new file.path + mfile.close + expect_raises(MFile::ClosedError) { mfile.write "foo".to_slice } + end + end + end + describe "#read" do + it "should raise ClosedError if closed" do + with_file do |file| + mfile = MFile.new file.path + mfile.close + data = Bytes.new(1) + expect_raises(MFile::ClosedError) { mfile.read data } + end + end + end + describe "#to_slice" do + describe "without position and size" do + it "should raise ClosedError if closed" do + with_file do |file| + mfile = MFile.new file.path + mfile.close + expect_raises(MFile::ClosedError) { mfile.to_slice } + end + end + end + describe "with position and size" do + it "should raise ClosedError if closed" do + with_file do |file| + mfile = MFile.new file.path + mfile.close + expect_raises(MFile::ClosedError) { mfile.to_slice(1, 1) } + end + end + end + end + describe "#copy_to" do + it "should raise ClosedError if closed" do + with_file do |file| + mfile = MFile.new file.path + mfile.close + data = IO::Memory.new + expect_raises(MFile::ClosedError) { mfile.copy_to data } + end + end + end + end +end diff --git a/src/lavinmq/mfile.cr b/src/lavinmq/mfile.cr index aef14346b7..fd4406b40c 100644 --- a/src/lavinmq/mfile.cr +++ b/src/lavinmq/mfile.cr @@ -142,6 +142,7 @@ class MFile < IO # Copies the file to another IO # Won't mmap the file if it's unmapped already def copy_to(output : IO, size = @size) : Int64 + raise ClosedError.new if @closed if unmapped? # don't remap unmapped files io = IO::FileDescriptor.new(@fd, blocking: true, close_on_finalize: false) io.rewind