diff --git a/renderdoc/common/result.h b/renderdoc/common/result.h index 96c474f6f..64b9e0da5 100644 --- a/renderdoc/common/result.h +++ b/renderdoc/common/result.h @@ -49,17 +49,44 @@ struct RDResult bool operator==(ResultCode result) const { return code == result; } bool operator!=(ResultCode result) const { return code != result; } operator ResultDetails() const; + +#if ENABLED(ENABLE_UNIT_TESTS) + static bool testErrorExpected; +#endif }; DECLARE_REFLECTION_STRUCT(RDResult); +#if ENABLED(ENABLE_UNIT_TESTS) + +#define EXPECT_ERROR() RDResult::testErrorExpected = true; + +#define PRINT_ERROR(...) \ + if(RDResult::testErrorExpected) \ + { \ + RDCLOG("Expected error: " __VA_ARGS__); \ + RDResult::testErrorExpected = false; \ + } \ + else \ + RDCERR(__VA_ARGS__); + +#define DID_ERROR_HAPPEN() (RDResult::testErrorExpected == false) + +#else + +#define EXPECT_ERROR() +#define PRINT_ERROR RDCERR +#define DID_ERROR_HAPPEN() false + +#endif + // helper macros since we often want to print the error message that gets returned. // one helper returns immediately, the other sets a result and prints - to allow cleanup #define RETURN_ERROR_RESULT_INTERNAL(code, msg, ...) \ do \ { \ RDResult CONCAT(res, __LINE__)(code, StringFormat::Fmt(STRING_LITERAL(msg), __VA_ARGS__)); \ - RDCERR("%s", CONCAT(res, __LINE__).message.c_str()); \ + PRINT_ERROR("%s", CONCAT(res, __LINE__).message.c_str()); \ return CONCAT(res, __LINE__); \ } while(0) @@ -67,7 +94,7 @@ DECLARE_REFLECTION_STRUCT(RDResult); do \ { \ res = RDResult(code, StringFormat::Fmt(STRING_LITERAL(msg), __VA_ARGS__)); \ - RDCERR("%s", res.message.c_str()); \ + PRINT_ERROR("%s", res.message.c_str()); \ } while(0) #define RETURN_WARNING_RESULT_INTERNAL(code, msg, ...) \ diff --git a/renderdoc/driver/shaders/dxil/llvm_bitreader.h b/renderdoc/driver/shaders/dxil/llvm_bitreader.h index b36c3beec..0a7f33c6b 100644 --- a/renderdoc/driver/shaders/dxil/llvm_bitreader.h +++ b/renderdoc/driver/shaders/dxil/llvm_bitreader.h @@ -25,6 +25,7 @@ #pragma once #include "common/common.h" +#include "common/formatting.h" namespace LLVMBC { @@ -166,10 +167,17 @@ public: m_Bits += (alignedByteOffs - byteOffs); } + bool IsErrored() { return m_Error != ResultCode::Succeeded; } + RDResult GetError() { return m_Error; } + private: const byte *m_Bits, *m_Start, *m_End; size_t m_Offset; + // result indicating if an error has been encountered and the stream is now invalid, with details + // of what happened + RDResult m_Error; + void Advance(size_t N) { m_Offset += N; @@ -188,7 +196,7 @@ private: { if(BitOffset() + bitsToRead > BitLength()) { - RDCERR("Reading off end of bitstream"); + SET_ERROR_RESULT(m_Error, ResultCode::InternalError, "Reading off end of bitstream"); // read 0s off the end of the stream. // set any whole bytes to 0: diff --git a/renderdoc/driver/shaders/dxil/llvm_decoder.cpp b/renderdoc/driver/shaders/dxil/llvm_decoder.cpp index 6a87cd93b..ceaec485b 100644 --- a/renderdoc/driver/shaders/dxil/llvm_decoder.cpp +++ b/renderdoc/driver/shaders/dxil/llvm_decoder.cpp @@ -402,14 +402,19 @@ TEST_CASE("Check LLVM bitreader", "[llvm]") uint32_t val1 = b.fixed(17); CHECK(val1 == 0x18040); + EXPECT_ERROR(); + // second read is partially out of bounds, we should read all 0s uint32_t val2 = b.fixed(16); CHECK(val2 == 0); + CHECK(DID_ERROR_HAPPEN()); + // should be exactly at the end of the stream CHECK(b.AtEndOfStream()); CHECK(b.ByteOffset() == sizeof(bits)); CHECK(b.BitOffset() == sizeof(bits) * 8); + CHECK(b.IsErrored()); } SECTION("Check fixed encoding") diff --git a/renderdoc/replay/entry_points.cpp b/renderdoc/replay/entry_points.cpp index 6a23dc0a9..30b793ef5 100644 --- a/renderdoc/replay/entry_points.cpp +++ b/renderdoc/replay/entry_points.cpp @@ -40,6 +40,10 @@ Threading::CriticalSection detailStringLock; rdcarray detailStrings; +#if ENABLED(ENABLE_UNIT_TESTS) +bool RDResult::testErrorExpected = false; +#endif + RDResult::operator ResultDetails() const { RDCCOMPILE_ASSERT(ResultCode(0) == ResultCode::Succeeded, diff --git a/renderdoc/serialise/streamio_tests.cpp b/renderdoc/serialise/streamio_tests.cpp index 5247acdf5..744a4667c 100644 --- a/renderdoc/serialise/streamio_tests.cpp +++ b/renderdoc/serialise/streamio_tests.cpp @@ -87,9 +87,12 @@ TEST_CASE("Test basic stream I/O operations", "[streamio]") CHECK_FALSE(reader.IsErrored()); CHECK(reader.AtEnd()); + EXPECT_ERROR(); + // reading off the end should read 0s and move to error state reader.Read(test); CHECK(test == 0); + CHECK(DID_ERROR_HAPPEN()); CHECK(reader.IsErrored()); }; @@ -98,9 +101,11 @@ TEST_CASE("Test stream I/O with invalid/broken outputs", "[streamio]") { SECTION("NULL file writer") { + EXPECT_ERROR(); StreamWriter writer((FILE *)NULL, Ownership::Stream); CHECK(writer.IsErrored()); + CHECK(DID_ERROR_HAPPEN()); uint32_t test = 5; writer.Write(test); @@ -110,9 +115,11 @@ TEST_CASE("Test stream I/O with invalid/broken outputs", "[streamio]") SECTION("NULL socket writer") { + EXPECT_ERROR(); StreamWriter writer((Network::Socket *)NULL, Ownership::Stream); CHECK(writer.IsErrored()); + CHECK(DID_ERROR_HAPPEN()); uint32_t test = 5; writer.Write(test); @@ -122,9 +129,11 @@ TEST_CASE("Test stream I/O with invalid/broken outputs", "[streamio]") SECTION("NULL file reader") { + EXPECT_ERROR(); StreamReader reader((FILE *)NULL, 0, Ownership::Stream); CHECK(reader.IsErrored()); + CHECK(DID_ERROR_HAPPEN()); uint32_t test = 5; reader.Read(test); @@ -134,9 +143,11 @@ TEST_CASE("Test stream I/O with invalid/broken outputs", "[streamio]") SECTION("NULL file reader") { + EXPECT_ERROR(); StreamReader reader((FILE *)NULL); CHECK(reader.IsErrored()); + CHECK(DID_ERROR_HAPPEN()); uint32_t test = 5; reader.Read(test); @@ -146,9 +157,11 @@ TEST_CASE("Test stream I/O with invalid/broken outputs", "[streamio]") SECTION("NULL socket reader") { + EXPECT_ERROR(); StreamReader reader((Network::Socket *)NULL, Ownership::Stream); CHECK(reader.IsErrored()); + CHECK(DID_ERROR_HAPPEN()); uint32_t test = 5; reader.Read(test);