diff --git a/test/integration/load_node_test.cpp b/test/integration/load_node_test.cpp index bc57c0a39..e5f52bea6 100644 --- a/test/integration/load_node_test.cpp +++ b/test/integration/load_node_test.cpp @@ -25,6 +25,22 @@ class FailingStreamBuf : public std::stringbuf { bool first_read_; }; +// Fails on the very first read, which the buffer above cannot do: it serves 32 +// bytes before throwing, so Stream's constructor completes and the failure is +// raised later, during scanning. Stream's constructor ends in ReadAheadTo(0), +// so a buffer that throws immediately makes the failure escape the constructor +// itself - a different path, and the one that used to leak the prefetch buffer +// because ~Stream cannot run for an object that was never constructed. +class ImmediatelyFailingStreamBuf : public std::stringbuf { + public: + ImmediatelyFailingStreamBuf() : std::stringbuf(std::string()) {} + + protected: + std::streamsize xsgetn(char*, std::streamsize) override { + throw std::ios_base::failure("simulated read failure"); + } +}; + TEST(LoadNodeTest, Reassign) { Node node = Load("foo"); node = Node(); @@ -55,6 +71,12 @@ TEST(LoadNodeTest, RejectsInputStreamFailureWhileReading) { EXPECT_THROW(Load(stream), BadStream); } +TEST(LoadNodeTest, RejectsInputStreamFailureOnFirstRead) { + ImmediatelyFailingStreamBuf buffer; + std::istream stream(&buffer); + EXPECT_THROW(Load(stream), BadStream); +} + TEST(LoadNodeTest, EmptyInputStreamRemainsNull) { std::istringstream stream; EXPECT_TRUE(Load(stream).IsNull());