Fix a bug in WriteStream and ReadStream

Added a unit test to confirm reliability of I/O
functions used by SaveState and RestoreState.
fix-squashed-planets
melex750 2021-06-25 18:50:15 -04:00
parent ce450d1c00
commit 55d6c431f2
2 changed files with 201 additions and 2 deletions

View File

@ -342,7 +342,11 @@ bool WriteStream(std::ostream &ostr, std::istream& istr)
if (!WriteLong(ostr, size)) return false;
if (!istr.seekg(0, istr.beg)) return false;
if (!(ostr << istr.rdbuf())) return false;
while (size > 0)
{
size -= 1;
if (!ostr.put(istr.get())) return false;
}
return true;
}
@ -355,7 +359,7 @@ bool ReadStream(std::istream& istr, std::ostream &ostr)
while (length-- > 0)
{
if (!(ostr << istr.get())) return false;
if (!ostr.put(istr.get())) return false;
}
return true;
}

View File

@ -20,6 +20,7 @@
#include "CBot/CBot.h"
#include <gtest/gtest.h>
#include <limits>
#include <stdexcept>
extern bool g_cbotTestSaveState;
@ -319,6 +320,200 @@ protected:
}
};
TEST_F(CBotUT, TestSaveStateIOFunctions)
{
std::stringstream sstr("");
std::string teststring;
for (char c = std::numeric_limits<char>::min() ;; ++c)
{
teststring.push_back(c);
if ( c == std::numeric_limits<char>::max() ) break;
}
auto CallWriteFunctions = [&sstr, &teststring]() -> bool
{
if (!WriteWord(sstr, static_cast<unsigned short>(0))) return false;
if (!WriteWord(sstr, std::numeric_limits<unsigned short>::max() / 2)) return false;
if (!WriteWord(sstr, std::numeric_limits<unsigned short>::max())) return false;
if (!WriteByte(sstr, std::numeric_limits<char>::min())) return false;
if (!WriteByte(sstr, std::numeric_limits<char>::max())) return false;
if (!WriteShort(sstr, std::numeric_limits<short>::min())) return false;
if (!WriteShort(sstr, std::numeric_limits<short>::min() / 2)) return false;
if (!WriteShort(sstr, -1)) return false;
if (!WriteShort(sstr, 0)) return false;
if (!WriteShort(sstr, std::numeric_limits<short>::max() / 2)) return false;
if (!WriteShort(sstr, std::numeric_limits<short>::max())) return false;
if (!WriteUInt32(sstr, static_cast<uint32_t>(0))) return false;
if (!WriteUInt32(sstr, std::numeric_limits<uint32_t>::max() / 2)) return false;
if (!WriteUInt32(sstr, std::numeric_limits<uint32_t>::max())) return false;
if (!WriteInt(sstr, std::numeric_limits<int>::min())) return false;
if (!WriteInt(sstr, std::numeric_limits<int>::min() / 2)) return false;
if (!WriteInt(sstr, -1)) return false;
if (!WriteInt(sstr, 0)) return false;
if (!WriteInt(sstr, std::numeric_limits<int>::max() / 2)) return false;
if (!WriteInt(sstr, std::numeric_limits<int>::max())) return false;
if (!WriteLong(sstr, std::numeric_limits<long>::min())) return false;
if (!WriteLong(sstr, std::numeric_limits<long>::min() / 2L)) return false;
if (!WriteLong(sstr, -1L)) return false;
if (!WriteLong(sstr, 0L)) return false;
if (!WriteLong(sstr, std::numeric_limits<long>::max() / 2L)) return false;
if (!WriteLong(sstr, std::numeric_limits<long>::max())) return false;
// test with padding bytes (not currently used anywhere)
if (!WriteLong(sstr, 1234567890L, 10)) return false;
if (!WriteFloat(sstr, std::numeric_limits<float>::min())) return false;
if (!WriteFloat(sstr, 0.0f)) return false;
if (!WriteFloat(sstr, std::numeric_limits<float>::max())) return false;
if (!WriteDouble(sstr, std::numeric_limits<double>::min())) return false;
if (!WriteDouble(sstr, 0.0)) return false;
if (!WriteDouble(sstr, std::numeric_limits<double>::max())) return false;
if (!WriteString(sstr, "")) return false;
if (!WriteString(sstr, teststring)) return false;
return true;
};
if ( !CallWriteFunctions() )
{
ADD_FAILURE() << "failed in CallWriteFunctions()" << std::endl;
return;
}
std::stringstream savestream("");
if ( !WriteStream(savestream, sstr) )
{
ADD_FAILURE() << "CBot::WriteStream() failed" << std::endl;
return;
}
std::stringstream newstream("");
if ( !ReadStream(savestream, newstream) )
{
ADD_FAILURE() << "CBot:ReadStream() failed" << std::endl;
return;
}
auto CallReadFunctions = [&teststring, &newstream]() -> bool
{
unsigned short w = 1;
if (!ReadWord(newstream, w)) return false;
if (w != static_cast<unsigned short>(0)) return false;
if (!ReadWord(newstream, w)) return false;
if (w != std::numeric_limits<unsigned short>::max() / 2) return false;
if (!ReadWord(newstream, w)) return false;
if (w != std::numeric_limits<unsigned short>::max()) return false;
char c = 1;
if (!ReadByte(newstream, c)) return false;
if (c != std::numeric_limits<char>::min()) return false;
if (!ReadByte(newstream, c)) return false;
if (c != std::numeric_limits<char>::max()) return false;
short s = 1;
if (!ReadShort(newstream, s)) return false;
if (s != std::numeric_limits<short>::min()) return false;
if (!ReadShort(newstream, s)) return false;
if (s != std::numeric_limits<short>::min() / 2) return false;
if (!ReadShort(newstream, s)) return false;
if (s != -1) return false;
if (!ReadShort(newstream, s)) return false;
if (s != 0) return false;
if (!ReadShort(newstream, s)) return false;
if (s != std::numeric_limits<short>::max() / 2) return false;
if (!ReadShort(newstream, s)) return false;
if (s != std::numeric_limits<short>::max()) return false;
uint32_t u = 1;
if (!ReadUInt32(newstream, u)) return false;
if (u != static_cast<uint32_t>(0)) return false;
if (!ReadUInt32(newstream, u)) return false;
if (u != std::numeric_limits<uint32_t>::max() / 2) return false;
if (!ReadUInt32(newstream, u)) return false;
if (u != std::numeric_limits<uint32_t>::max()) return false;
int i = 1;
if (!ReadInt(newstream, i)) return false;
if (i != std::numeric_limits<int>::min()) return false;
if (!ReadInt(newstream, i)) return false;
if (i != std::numeric_limits<int>::min() / 2) return false;
if (!ReadInt(newstream, i)) return false;
if (i != -1) return false;
if (!ReadInt(newstream, i)) return false;
if (i != 0) return false;
if (!ReadInt(newstream, i)) return false;
if (i != std::numeric_limits<int>::max() / 2) return false;
if (!ReadInt(newstream, i)) return false;
if (i != std::numeric_limits<int>::max()) return false;
long l = 1L;
if (!ReadLong(newstream, l)) return false;
if (l != std::numeric_limits<long>::min()) return false;
if (!ReadLong(newstream, l)) return false;
if (l != std::numeric_limits<long>::min() / 2L) return false;
if (!ReadLong(newstream, l)) return false;
if (l != -1L) return false;
if (!ReadLong(newstream, l)) return false;
if (l != 0L) return false;
if (!ReadLong(newstream, l)) return false;
if (l != std::numeric_limits<long>::max() / 2L) return false;
if (!ReadLong(newstream, l)) return false;
if (l != std::numeric_limits<long>::max()) return false;
if (!ReadLong(newstream, l)) return false;
if (l != 1234567890L) return false;
float f = 1.0f;
if (!ReadFloat(newstream, f)) return false;
if (f != std::numeric_limits<float>::min()) return false;
if (!ReadFloat(newstream, f)) return false;
if (f != 0.0f) return false;
if (!ReadFloat(newstream, f)) return false;
if (f != std::numeric_limits<float>::max()) return false;
double d = 1.0;
if (!ReadDouble(newstream, d)) return false;
if (d != std::numeric_limits<double>::min()) return false;
if (!ReadDouble(newstream, d)) return false;
if (d != 0.0) return false;
if (!ReadDouble(newstream, d)) return false;
if (d != std::numeric_limits<double>::max()) return false;
std::string newstring = "should be empty string after next read";
if (!ReadString(newstream, newstring)) return false;
if (newstring != "") return false;
if (!ReadString(newstream, newstring)) return false;
if (newstring != teststring) return false;
return true;
};
if ( !CallReadFunctions() )
{
ADD_FAILURE() << "failed in CallReadFunctions()" << std::endl;
return;
}
}
TEST_F(CBotUT, EmptyTest)
{
ExecuteTest(