1#include <muleunit/test.h> 2#include <CFile.h> 3#include <MemFile.h> 4#include <MD4Hash.h> 5#include <limits> 6#include <kademlia/utils/UInt128.h> 7 8using Kademlia::CUInt128; 9using namespace muleunit; 10 11namespace muleunit 12{ 13 template <> 14 wxString StringFrom<CPath>(const CPath& path) 15 { 16 return path.GetPrintable(); 17 } 18} 19 20 21//! The max file-size of auto-generated files to test. 22const size_t TEST_LENGTH = 512; 23 24namespace muleunit { 25 //! Needed for ASSERT_EQUALS with CMD4Hash values 26 template <> 27 wxString StringFrom<CMD4Hash>(const CMD4Hash& hash) { 28 return hash.Encode(); 29 } 30 31 //! Needed for ASSERT_EQUALS with CUInt128 values 32 template <> 33 wxString StringFrom<CUInt128>(const CUInt128& value) { 34 return value.ToHexString(); 35 } 36} 37 38 39void writePredefData(CFileDataIO* file) 40{ 41 char data[TEST_LENGTH]; 42 43 for (size_t j = 0; j < TEST_LENGTH; ++j) { 44 data[j] = j & 0xff; 45 } 46 47 file->Write(data, TEST_LENGTH); 48 file->Seek(0, wxFromStart); 49} 50 51 52 53///////////////////////////////////////////////////////////////////// 54// Specialize this template for each implemention 55// of the CFileDataIO interface you wish to test. 56// 57// This struct must be a subclass of Test. 58// 59// Two pointers are to be defined: 60// m_emptyFile, which must be an empty, zero-length file 61// m_predefFile, which must be TEST_LENGTH in size and 62// and contain the sequence 0..255 repeated 63// as needed. 64// 65// The following functions should be overridden: 66// - setUp() 67// - tearDown() 68// 69template <typename TYPE> 70struct FileDataIOFixture; 71 72 73template <> 74class FileDataIOFixture<CFile> : public Test 75{ 76public: 77 FileDataIOFixture(const wxString& testName) 78 : Test(wxT("FileDataIO"), wxT("CFile - ") + testName) {} 79 80 81 CFile* m_emptyFile; 82 CFile* m_predefFile; 83 84 void setUp() { 85 m_emptyFile = m_predefFile = NULL; 86 const CPath emptyPath = CPath(wxT("FileDataIOTest.empty")); 87 const CPath datPath = CPath(wxT("FileDataIOTest.dat")); 88 89 m_emptyFile = new CFile(); 90 m_emptyFile->Create(emptyPath, true); 91 ASSERT_TRUE(m_emptyFile->IsOpened()); 92 m_emptyFile->Close(); 93 m_emptyFile->Open(emptyPath, CFile::read_write); 94 ASSERT_TRUE(m_emptyFile->IsOpened()); 95 96 m_predefFile = new CFile(); 97 m_predefFile->Create(datPath, true); 98 ASSERT_TRUE(m_predefFile->IsOpened()); 99 m_predefFile->Close(); 100 m_predefFile->Open(datPath, CFile::read_write); 101 ASSERT_TRUE(m_predefFile->IsOpened()); 102 103 writePredefData(m_predefFile); 104 ASSERT_EQUALS(0u, m_predefFile->GetPosition()); 105 ASSERT_EQUALS(TEST_LENGTH, m_predefFile->GetLength()); 106 } 107 108 void tearDown() { 109 delete m_emptyFile; 110 delete m_predefFile; 111 112 wxRemoveFile(wxT("FileDataIOTest.dat")); 113 wxRemoveFile(wxT("FileDataIOTest.empty")); 114 } 115}; 116 117 118template <> 119class FileDataIOFixture<CMemFile> : public Test 120{ 121public: 122 FileDataIOFixture(const wxString& testName) 123 : Test(wxT("FileDataIO"), wxT("CMemFile - ") + testName) {} 124 125 126 CMemFile* m_emptyFile; 127 CMemFile* m_predefFile; 128 129 void setUp() { 130 m_emptyFile = m_predefFile = NULL; 131 132 m_emptyFile = new CMemFile(); 133 m_predefFile = new CMemFile(); 134 135 writePredefData(m_predefFile); 136 ASSERT_EQUALS(0u, m_predefFile->GetPosition()); 137 ASSERT_EQUALS(TEST_LENGTH, m_predefFile->GetLength()); 138 } 139 140 void tearDown() { 141 delete m_emptyFile; 142 delete m_predefFile; 143 } 144}; 145 146 147///////////////////////////////////////////////////////////////////// 148// A writeWrite interface should be implemented for each set of 149// read/write functions that is to be tested. The following 3 150// static functions must be implemented in each specialization of the 151// template: 152// 153// - TYPE genValue(size_t j), which returns the expected value at 154// position j in the files with predefined data. 155// - TYPE readValue(CFileDataIO*), which returns and returns the 156// value at the current position in the file. 157// - void writeValue(CFileDataIO*, TYPE), which writes the given 158// value at the current position in the file. 159// - wxString name(), which returns the human-readble name of the type 160// 161template <typename TYPE> 162struct RWInterface; 163 164template <> 165struct RWInterface<uint8> 166{ 167 static uint8 genValue(size_t j) { 168 return j & 0xff; 169 } 170 171 static uint8 readValue(CFileDataIO* file) { 172 return file->ReadUInt8(); 173 } 174 175 static void writeValue(CFileDataIO* file, uint8 value) { 176 file->WriteUInt8(value); 177 } 178 179 static wxString name() { return wxT("UInt8"); } 180}; 181 182 183template <> 184struct RWInterface<uint16> 185{ 186 static uint16 genValue(size_t j) { 187 return (((j + 1) & 0xff) << 8) | (j & 0xff); 188 } 189 190 static uint16 readValue(CFileDataIO* file) { 191 return file->ReadUInt16(); 192 } 193 194 static void writeValue(CFileDataIO* file, uint16 value) { 195 file->WriteUInt16(value); 196 } 197 198 static wxString name() { return wxT("UInt16"); } 199}; 200 201 202template <> 203struct RWInterface<uint32> 204{ 205 static uint32 genValue(size_t j) { 206 return (((j + 3) & 0xff) << 24) | (((j + 2) & 0xff) << 16) | (((j + 1) & 0xff) << 8) | (j & 0xff); 207 } 208 209 static uint32 readValue(CFileDataIO* file) { 210 return file->ReadUInt32(); 211 } 212 213 static void writeValue(CFileDataIO* file, uint32 value) { 214 file->WriteUInt32(value); 215 } 216 217 static wxString name() { return wxT("UInt32"); } 218}; 219 220 221template <> 222struct RWInterface<CMD4Hash> 223{ 224 static CMD4Hash genValue(size_t j) { 225 CMD4Hash value; 226 for (size_t y = j; y < j + 16; y++) { 227 value[y - j] = y & 0xff; 228 } 229 230 return value; 231 } 232 233 static CMD4Hash readValue(CFileDataIO* file) { 234 return file->ReadHash(); 235 } 236 237 static void writeValue(CFileDataIO* file, CMD4Hash value) { 238 file->WriteHash(value); 239 } 240 241 static wxString name() { return wxT("CMD4Hash"); } 242}; 243 244 245template <> 246struct RWInterface<CUInt128> 247{ 248 static CUInt128 genValue(size_t j) { 249 CUInt128 value; 250 for (size_t y = 0; y < 16; y += 4) { 251 value.Set32BitChunk(y >> 2, 252 ((j + y ) & 0xff) | 253 ((j + y + 1) & 0xff) << 8 | 254 ((j + y + 2) & 0xff) << 16 | 255 ((j + y + 3) & 0xff) << 24); 256 } 257 258 return value; 259 } 260 261 static CUInt128 readValue(CFileDataIO* file) { 262 return file->ReadUInt128(); 263 } 264 265 static void writeValue(CFileDataIO* file, CUInt128 value) { 266 file->WriteUInt128(value); 267 } 268 269 static wxString name() { return wxT("CUInt128"); } 270}; 271 272 273///////////////////////////////////////////////////////////////////// 274// The following tests ensure that the given implementations 275// of the CFileDataIO interface properly does so. 276 277template <typename IMPL, typename TYPE, size_t SIZE> 278class ReadTest : public FileDataIOFixture<IMPL> 279{ 280 typedef RWInterface<TYPE> RW; 281 282public: 283 ReadTest() 284 : FileDataIOFixture<IMPL>(wxT("Read ") + RW::name()) {} 285 286 void run() { 287 CFileDataIO* file = this->m_predefFile; 288 289 for (size_t j = 0; j < TEST_LENGTH + 1 - SIZE; ++j) { 290 ASSERT_EQUALS(j, file->Seek(j, wxFromStart)); 291 ASSERT_EQUALS(j, file->GetPosition()); 292 ASSERT_EQUALS(RW::genValue(j), RW::readValue(file)); 293 ASSERT_EQUALS(j + SIZE, file->GetPosition()); 294 } 295 296 ASSERT_EQUALS(TEST_LENGTH, file->GetLength()); 297 298 // Check reads past EOF 299 for (size_t i = 0; i < SIZE; ++i) { 300 ASSERT_EQUALS(TEST_LENGTH - i, file->Seek(-(signed)i, wxFromEnd)); 301 ASSERT_RAISES(CEOFException, RW::readValue(file)); 302 } 303 304 // Check that only the given length is written to the target buffer 305 char testBuffer[32]; 306 memset(testBuffer, 127, 32); 307 char* buf = testBuffer + 8; 308 309 for (int i = 0; i < 16; ++i) { 310 ASSERT_EQUALS(0u, file->Seek(0, wxFromStart)); 311 ASSERT_EQUALS(0u, file->GetPosition()); 312 313 file->Read(buf, i + 1); 314 315 for (int j = 0; j < 32; ++j) { 316 if (j < 8 || j > i + 8) { 317 ASSERT_EQUALS(127, (int)testBuffer[j]); 318 } else { 319 ASSERT_EQUALS(j - 8, (int)testBuffer[j]); 320 } 321 } 322 } 323 } 324}; 325 326 327template <typename IMPL, typename TYPE, size_t SIZE> 328class WriteTest : public FileDataIOFixture<IMPL> 329{ 330 typedef RWInterface<TYPE> RW; 331 332public: 333 WriteTest() 334 : FileDataIOFixture<IMPL>(wxT("Write ") + RW::name()) {} 335 336 void run() { 337 const unsigned char CanaryData = 170; 338 const char canaryBlock[] = { CanaryData }; 339 340 CFileDataIO* file = this->m_predefFile; 341 342 for (size_t j = 0; j < TEST_LENGTH + 1 - SIZE; ++j) { 343 // Clear before, after and at the target byte(s) 344 for (int t = -SIZE; t < (int)(2*SIZE); ++t) { 345 if ((j + t) < TEST_LENGTH && ((int)j + t) >= 0) { 346 file->Seek(j + t, wxFromStart); 347 ASSERT_EQUALS(j + t, file->GetPosition()); 348 file->Write(canaryBlock, 1); 349 ASSERT_EQUALS(j + t + 1, file->GetPosition()); 350 351 // Check that canary was written 352 file->Seek(j + t, wxFromStart); 353 ASSERT_EQUALS(CanaryData, file->ReadUInt8()); 354 ASSERT_EQUALS(j + t + 1, file->GetPosition()); 355 } 356 } 357 358 file->Seek(j, wxFromStart); 359 360 ASSERT_EQUALS(j, file->GetPosition()); 361 RW::writeValue(file, RW::genValue(j)); 362 ASSERT_EQUALS(j + SIZE, file->GetPosition()); 363 364 // Check before, after and at the target byte 365 for (int t = -SIZE; t < (int)(2*SIZE); ++t) { 366 if ((j + t) < TEST_LENGTH && ((int)j + t) >= 0) { 367 if (t) { 368 if (t < 0 || t >= (int)SIZE) { 369 file->Seek(j + t, wxFromStart); 370 ASSERT_EQUALS(CanaryData, file->ReadUInt8()); 371 ASSERT_EQUALS(j + t + 1, file->GetPosition()); 372 } 373 } else { 374 file->Seek(j + t, wxFromStart); 375 ASSERT_EQUALS(RW::genValue(j), RW::readValue(file)); 376 ASSERT_EQUALS(j + t + SIZE, file->GetPosition()); 377 } 378 } 379 } 380 } 381 382 ASSERT_EQUALS(TEST_LENGTH, file->GetLength()); 383 } 384}; 385 386 387template <typename IMPL> 388class SeekTest : public FileDataIOFixture<IMPL> 389{ 390public: 391 SeekTest() 392 : FileDataIOFixture<IMPL>(wxT("Seek")) {} 393 394 void run() { 395 CFileDataIO* file = this->m_predefFile; 396 397 ASSERT_EQUALS(0u, file->GetPosition()); 398 for (size_t pos = 0; pos < TEST_LENGTH * 2; pos += pos + 1) { 399 ASSERT_EQUALS(pos, file->Seek(pos, wxFromStart)); 400 ASSERT_EQUALS(pos, file->GetPosition()); 401 } 402 403 ASSERT_EQUALS(0u, file->Seek(0, wxFromStart)); 404 ASSERT_EQUALS(0u, file->GetPosition()); 405 406 for (size_t pos = 0, cur = 0; pos < TEST_LENGTH * 2; pos += ++cur) { 407 ASSERT_EQUALS(pos, file->Seek(cur, wxFromCurrent)); 408 ASSERT_EQUALS(pos, file->GetPosition()); 409 } 410 411 ASSERT_EQUALS(0u, file->Seek(0, wxFromStart)); 412 ASSERT_EQUALS(0u, file->GetPosition()); 413 414 for (size_t pos = 0; pos < TEST_LENGTH; pos += pos + 1) { 415 ASSERT_EQUALS(TEST_LENGTH - pos, file->Seek(-(signed)pos, wxFromEnd)); 416 ASSERT_EQUALS(TEST_LENGTH - pos, file->GetPosition()); 417 } 418 419 ASSERT_EQUALS(0u, file->Seek(0, wxFromStart)); 420 ASSERT_EQUALS(0u, file->GetPosition()); 421 422 // Seek to negative is invalid 423 for (off_t pos = 1; pos < 10; ++pos) { 424 ASSERT_RAISES(CInvalidParamsEx, file->Seek(-1 * pos)); 425 ASSERT_EQUALS(0u, file->GetPosition()); 426 } 427 428 // Corner-case 429 ASSERT_RAISES(CInvalidParamsEx, file->Seek(std::numeric_limits<off_t>::min())); 430 ASSERT_EQUALS(0u, file->GetPosition()); 431 } 432}; 433 434 435template <typename IMPL> 436class WritePastEndTest : public FileDataIOFixture<IMPL> 437{ 438public: 439 WritePastEndTest() 440 : FileDataIOFixture<IMPL>(wxT("Write Past End")) {} 441 442 void run() { 443 CFileDataIO* file = this->m_emptyFile; 444 445 ASSERT_EQUALS(0u, file->GetLength()); 446 ASSERT_EQUALS(0u, file->GetPosition()); 447 448 file->WriteUInt8(0); 449 450 ASSERT_EQUALS(1u, file->GetLength()); 451 ASSERT_EQUALS(1u, file->GetPosition()); 452 453 file->WriteUInt16(0); 454 455 ASSERT_EQUALS(3u, file->GetLength()); 456 ASSERT_EQUALS(3u, file->GetPosition()); 457 458 file->WriteUInt32(0); 459 460 ASSERT_EQUALS(7u, file->GetLength()); 461 ASSERT_EQUALS(7u, file->GetPosition()); 462 463 file->WriteHash(CMD4Hash()); 464 465 ASSERT_EQUALS(23u, file->GetLength()); 466 ASSERT_EQUALS(23u, file->GetPosition()); 467 468 // TODO: ReadUInt128 469 470 471 char tmp[42]; 472 memset(tmp, 0, 42); 473 file->Write(tmp, 42); 474 475 ASSERT_EQUALS(65u, file->GetLength()); 476 ASSERT_EQUALS(65u, file->GetPosition()); 477 478 // Check that the length is always increased, regardless of starting pos 479 size_t length = file->GetLength(); 480 for (size_t j = 0; j < 16; ++j) { 481 ASSERT_EQUALS(length + j - 15u, file->Seek(-15, wxFromEnd)); 482 ASSERT_EQUALS(length + j - 15u, file->GetPosition()); 483 file->WriteHash(CMD4Hash()); 484 ASSERT_EQUALS(length + j + 1u, file->GetLength()); 485 ASSERT_EQUALS(length + j + 1u, file->GetPosition()); 486 } 487 } 488}; 489 490 491template <typename IMPL> 492class StringTest : public FileDataIOFixture<IMPL> 493{ 494public: 495 StringTest() 496 : FileDataIOFixture<IMPL>(wxT("String")) {} 497 498 struct Encoding 499 { 500 const EUtf8Str id; 501 const char* header; 502 const size_t headLen; 503 }; 504 505 struct TestString 506 { 507 const wxChar* str; 508 // Raw and UTF8 expected lengths ... 509 const size_t lengths[2]; 510 }; 511 512 void run() { 513 CFileDataIO* file = this->m_emptyFile; 514 515 // TODO: Need to test non-ascii values when using unicode/etc, zero-length lengthfields 516 Encoding encodings[] = 517 { 518 {utf8strNone, NULL, 0}, 519 {utf8strOptBOM, "\xEF\xBB\xBF", 3}, 520 {utf8strRaw, NULL, 0} 521 }; 522 523 TestString testData[] = 524 { 525 { wxT("0123456789abcdef"), { 16, 16 } }, 526 { wxT(""), { 0, 0 } }, 527 { wxT("abc �� def �� ghi ��"), { 17, 20 } }, 528 { wxT("a��e��u����"), { 7, 11 } }, 529 { wxT("u��o����e��a��y��"), { 11, 17 } }, 530 }; 531 532 533 for (size_t str = 0; str < ArraySize(testData); ++str) { 534 CONTEXT(wxString(wxT("Testing string: '")) << testData[str].str << wxT("'")); 535 536 for (size_t enc = 0; enc < ArraySize(encodings); ++enc) { 537 CONTEXT(wxString::Format(wxT("Testing encoding: %i"), encodings[enc])); 538 539 const wxChar* curStr = testData[str].str; 540 size_t strLen = testData[str].lengths[(encodings[enc].id == utf8strNone) ? 0 : 1]; 541 size_t headLen = encodings[enc].headLen; 542 543 file->WriteString(curStr, encodings[enc].id, 2); 544 ASSERT_EQUALS(strLen + 2 + headLen, file->GetPosition()); 545 ASSERT_EQUALS(0u, file->Seek(0, wxFromStart)); 546 ASSERT_EQUALS(strLen + headLen, file->ReadUInt16()); 547 548 // Check header (if any) 549 if (encodings[enc].header) { 550 wxCharBuffer head(headLen); 551 file->Read(head.data(), headLen); 552 ASSERT_EQUALS(0, memcmp(head, encodings[enc].header, headLen)); 553 } 554 555 ASSERT_EQUALS(0u, file->Seek(0, wxFromStart)); 556 ASSERT_EQUALS(curStr, file->ReadString(encodings[enc].id, 2)); 557 ASSERT_EQUALS(0u, file->Seek(0, wxFromStart)); 558 559 560 file->WriteString(curStr, encodings[enc].id, 4); 561 ASSERT_EQUALS(strLen + 4 + headLen, file->GetPosition()); 562 ASSERT_EQUALS(0u, file->Seek(0, wxFromStart)); 563 ASSERT_EQUALS(strLen + headLen, file->ReadUInt32()); 564 565 // Check header (if any) 566 if (encodings[enc].header) { 567 wxCharBuffer head(headLen); 568 file->Read(head.data(), headLen); 569 ASSERT_EQUALS(0, memcmp(head, encodings[enc].header, headLen)); 570 } 571 572 ASSERT_EQUALS(0u, file->Seek(0, wxFromStart)); 573 ASSERT_EQUALS(curStr, file->ReadString(encodings[enc].id, 4)); 574 ASSERT_EQUALS(0u, file->Seek(0, wxFromStart)); 575 } 576 } 577 578 CAssertOff silence; 579 for (size_t enc = 0; enc < ArraySize(encodings); ++enc) { 580 CONTEXT(wxString::Format(wxT("Testing encoding against poisoning: %i"), encodings[enc])); 581 582 ////////////////////////////////////////////// 583 // Check if we guard against "poisoning". 584 ASSERT_EQUALS(0u, file->Seek(0, wxFromStart)); 585 586 const size_t rawLen = (((uint16)-1) * 3) / 4; 587 wxString badStr(wxT('\xfe'), rawLen); 588 589 // This will cause the string to be UTF-8 encoded, 590 // thereby exceeding the max length-field size (16b). 591 file->WriteString(badStr, encodings[enc].id, 2); 592 file->WriteUInt16(0x7913); 593 594 ASSERT_EQUALS(0u, file->Seek(0, wxFromStart)); 595 file->ReadString(true, 2); 596 ASSERT_EQUALS(0x7913, file->ReadUInt16()); 597 } 598 } 599}; 600 601 602// Do not attempt to use this test with CMemFile. 603template <typename IMPL> 604class LargeFileTest : public FileDataIOFixture<IMPL> 605{ 606public: 607 LargeFileTest() 608 : FileDataIOFixture<IMPL>(wxT("LargeFile")) {} 609 610 void run() { 611 CFile* file = dynamic_cast<CFile*>(this->m_emptyFile); 612 613 ASSERT_TRUE(file != NULL); 614 ASSERT_EQUALS(2147483647UL, file->Seek(2147483647L, wxFromStart)); 615 ASSERT_EQUALS(2147483648UL, file->Seek(1, wxFromCurrent)); 616 ASSERT_EQUALS(2147483648UL, file->GetPosition()); 617 ASSERT_EQUALS(4294967296ULL, file->Seek(4294967296ULL, wxFromStart)); 618 ASSERT_EQUALS(4294967296ULL, file->GetPosition()); 619 } 620}; 621 622 623 624///////////////////////////////////////////////////////////////////// 625// Registration of all tests 626 627ReadTest<CFile, uint8, 1> CFileReadUInt8Test; 628ReadTest<CFile, uint16, 2> CFileReadUInt16Test; 629ReadTest<CFile, uint32, 4> CFileReadUInt32Test; 630ReadTest<CFile, CMD4Hash, 16> CFileReadCMD4HashTest; 631ReadTest<CFile, CUInt128, 16> CFileReadCUInt128Test; 632 633WriteTest<CFile, uint8, 1> CFileWriteUInt8Test; 634WriteTest<CFile, uint16, 2> CFileWriteUInt16Test; 635WriteTest<CFile, uint32, 4> CFileWriteUInt32Test; 636WriteTest<CFile, CMD4Hash, 16> CFileWriteCMD4HashTest; 637WriteTest<CFile, CUInt128, 16> CFileWriteCUInt128Test; 638 639SeekTest<CFile> CFileSeekTest; 640WritePastEndTest<CFile> CFileWritePastEnd; 641StringTest<CFile> CFileStringTest; 642 643LargeFileTest<CFile> CFileLargeFileTest; 644 645 646ReadTest<CMemFile, uint8, 1> CMemFileReadUInt8Test; 647ReadTest<CMemFile, uint16, 2> CMemFileReadUInt16Test; 648ReadTest<CMemFile, uint32, 4> CMemFileReadUInt32Test; 649ReadTest<CMemFile, CMD4Hash, 16> CMemFileReadCMD4HashTest; 650ReadTest<CMemFile, CUInt128, 16> CMemFileReadCUInt128Test; 651 652WriteTest<CMemFile, uint8, 1> CMemFileWriteUInt8Test; 653WriteTest<CMemFile, uint16, 2> CMemFileWriteUInt16Test; 654WriteTest<CMemFile, uint32, 4> CMemFileWriteUInt32Test; 655WriteTest<CMemFile, CMD4Hash, 16> CMemFileWriteCMD4HashTest; 656WriteTest<CMemFile, CUInt128, 16> CMemFileWriteCUInt128Test; 657 658SeekTest<CMemFile> CMemFileSeekTest; 659WritePastEndTest<CMemFile> CMemFileWritePastEnd; 660StringTest<CMemFile> CMemFileStringTest; 661 662 663///////////////////////////////////////////////////////////////////// 664// CMemFile specific tests 665 666DECLARE_SIMPLE(CMemFile); 667 668TEST(CMemFile, AttachedBuffer) 669{ 670 const size_t BufferLength = 1024; 671 byte buffer[BufferLength]; 672 673 for (size_t i = 0; i < BufferLength; ++i) { 674 buffer[i] = i & 0xFF; 675 } 676 677 CMemFile file(buffer, BufferLength); 678 for (size_t i = 0; i < BufferLength; ++i) { 679 ASSERT_EQUALS(file.ReadUInt8(), i & 0xFF); 680 } 681 682 // Resizing upwards should fail 683 ASSERT_RAISES(CRunTimeException, file.SetLength(BufferLength * 2)); 684 ASSERT_EQUALS(BufferLength, file.GetLength()); 685 686 // Resizing downwards should be ok, as should resizes up (but within bufferlen) 687 file.SetLength(BufferLength / 2); 688 ASSERT_EQUALS(BufferLength / 2, file.GetLength()); 689 file.SetLength(BufferLength); 690 ASSERT_EQUALS(BufferLength, file.GetLength()); 691 692 // Write past end should fail 693 ASSERT_EQUALS(BufferLength, file.Seek(0, wxFromEnd)); 694 ASSERT_RAISES(CRunTimeException, file.WriteUInt8(0)); 695 696 // Init with invalid buffer should fail 697 ASSERT_RAISES(CRunTimeException, new CMemFile(static_cast<const byte*>(NULL), 1024)); 698 ASSERT_RAISES(CRunTimeException, new CMemFile(static_cast<byte*>(NULL), 1024)); 699} 700 701 702TEST(CMemFile, ConstBuffer) 703{ 704 byte arr[10]; 705 CMemFile file(const_cast<const byte*>(arr), sizeof(arr)); 706 707 ASSERT_RAISES(CRunTimeException, file.WriteUInt8(0)); 708 ASSERT_RAISES(CRunTimeException, file.WriteUInt16(0)); 709 ASSERT_RAISES(CRunTimeException, file.WriteUInt32(0)); 710 ASSERT_RAISES(CRunTimeException, file.WriteUInt64(0)); 711 712 char buffer[sizeof(arr)]; 713 ASSERT_RAISES(CRunTimeException, file.Write(buffer, sizeof(arr))); 714} 715 716 717TEST(CMemFile, SetLength) 718{ 719 CMemFile file; 720 721 ASSERT_EQUALS(0u, file.GetLength()); 722 file.SetLength(1024); 723 ASSERT_EQUALS(1024u, file.GetLength()); 724 ASSERT_EQUALS(1024u, file.Seek(0, wxFromEnd)); 725 file.SetLength(512u); 726 ASSERT_EQUALS(512u, file.GetLength()); 727 ASSERT_EQUALS(512u, file.Seek(0, wxFromEnd)); 728} 729 730 731///////////////////////////////////////////////////////////////////// 732// CFile specific tests 733 734const CPath testFile = CPath(wxT("TestFile.dat")); 735const unsigned testMode = 0600; 736 737DECLARE(CFile); 738 void setUp() { 739 // Ensure that the testfile doesn't exist 740 if (testFile.FileExists()) { 741 if (!CPath::RemoveFile(testFile)) { 742 MULE_VALIDATE_STATE(false, wxT("Failed to remove temporary file.")); 743 } 744 } 745 } 746 747 void tearDown() { 748 if (testFile.FileExists()) { 749 CPath::RemoveFile(testFile); 750 } 751 } 752END_DECLARE; 753 754 755TEST(CFile, Constructor) 756{ 757 // Test initial conditions 758 { 759 CFile file; 760 761 ASSERT_TRUE(!file.IsOpened()); 762 ASSERT_TRUE(file.fd() == CFile::fd_invalid); 763 ASSERT_RAISES(CRunTimeException, file.WriteUInt8(0)); 764 ASSERT_RAISES(CRunTimeException, file.ReadUInt8()); 765 ASSERT_RAISES(CRunTimeException, file.Seek(0, wxFromStart)); 766 ASSERT_RAISES(CRunTimeException, file.GetLength()); 767 ASSERT_RAISES(CRunTimeException, file.GetPosition()); 768 ASSERT_RAISES(CRunTimeException, file.SetLength(13)); 769 ASSERT_RAISES(CRunTimeException, file.Flush()); 770 ASSERT_RAISES(CRunTimeException, file.Close()); 771 ASSERT_TRUE(!file.IsOpened()); 772 ASSERT_TRUE(file.fd() == CFile::fd_invalid); 773 } 774 775 // Create test file 776 { 777 CFile file; 778 ASSERT_TRUE(file.Create(testFile, false, testMode)); 779 ASSERT_EQUALS(testFile, file.GetFilePath()); 780 file.WriteUInt32(1); 781 } 782 783 { 784 CFile file(testFile, CFile::read); 785 786 ASSERT_TRUE(file.IsOpened()); 787 ASSERT_TRUE(file.fd() != CFile::fd_invalid); 788 ASSERT_EQUALS(testFile, file.GetFilePath()); 789 ASSERT_EQUALS(4u, file.GetLength()); 790 ASSERT_EQUALS(1u, file.ReadUInt32()); 791 792 ASSERT_RAISES(CIOFailureException, file.WriteUInt8(0)); 793 } 794 795 { 796 CFile file(testFile, CFile::write); 797 798 ASSERT_TRUE(file.IsOpened()); 799 ASSERT_TRUE(file.fd() != CFile::fd_invalid); 800 ASSERT_EQUALS(testFile, file.GetFilePath()); 801 ASSERT_EQUALS(0u, file.GetPosition()); 802 ASSERT_EQUALS(0u, file.GetLength()); 803 file.WriteUInt32(1); 804 ASSERT_EQUALS(0u, file.Seek(0, wxFromStart)); 805 806 ASSERT_RAISES(CIOFailureException, file.ReadUInt8()); 807 } 808 809 { 810 CFile file(testFile, CFile::read_write); 811 812 ASSERT_TRUE(file.IsOpened()); 813 ASSERT_TRUE(file.fd() != CFile::fd_invalid); 814 ASSERT_EQUALS(testFile, file.GetFilePath()); 815 ASSERT_EQUALS(4u, file.GetLength()); 816 ASSERT_EQUALS(0u, file.GetPosition()); 817 ASSERT_EQUALS(1u, file.ReadUInt32()); 818 ASSERT_EQUALS(0u, file.Seek(0, wxFromStart)); 819 file.WriteUInt32(2); 820 ASSERT_EQUALS(0u, file.Seek(0, wxFromStart)); 821 ASSERT_EQUALS(2u, file.ReadUInt32()); 822 } 823 824 { 825 CFile file(testFile, CFile::write_append); 826 827 ASSERT_TRUE(file.IsOpened()); 828 ASSERT_TRUE(file.fd() != CFile::fd_invalid); 829 ASSERT_EQUALS(4u, file.GetLength()); 830 file.WriteUInt32(1); 831 ASSERT_EQUALS(0u, file.Seek(0, wxFromStart)); 832 833 ASSERT_RAISES(CIOFailureException, file.ReadUInt8()); 834 835 ASSERT_TRUE(file.Close()); 836 ASSERT_TRUE(file.Open(testFile, CFile::read)); 837 838 ASSERT_EQUALS(2u, file.ReadUInt32()); 839 ASSERT_EQUALS(1u, file.ReadUInt32()); 840 } 841} 842 843 844TEST(CFile, Create) 845{ 846 ASSERT_FALSE(testFile.FileExists()); 847 848 // Check creation of new file, when none exists, with/without overwrite 849 for (size_t i = 0; i < 2; ++i) { 850 bool overwrite = (i == 1); 851 852 CFile file; 853 ASSERT_TRUE(!file.IsOpened()); 854 ASSERT_TRUE(file.fd() == CFile::fd_invalid); 855 ASSERT_TRUE(file.Create(testFile, overwrite, testMode)); 856 ASSERT_TRUE(file.IsOpened()); 857 ASSERT_TRUE(file.fd() != CFile::fd_invalid); 858 ASSERT_EQUALS(testFile, file.GetFilePath()); 859 ASSERT_TRUE(file.Close()); 860 ASSERT_TRUE(file.fd() == CFile::fd_invalid); 861 ASSERT_TRUE(!file.IsOpened()); 862 863 ASSERT_TRUE(wxFile::Access(testFile.GetRaw(), wxFile::read)); 864 ASSERT_TRUE(wxFile::Access(testFile.GetRaw(), wxFile::write)); 865 866 ASSERT_TRUE(wxRemoveFile(testFile.GetRaw())); 867 } 868 869 // Create testfile, with a bit of contents 870 { 871 CFile file; 872 ASSERT_TRUE(file.Create(testFile, false, testMode)); 873 ASSERT_EQUALS(testFile, file.GetFilePath()); 874 file.WriteUInt32(1); 875 } 876 877 // Check that owerwrite = false works as expected 878 { 879 CFile file; 880 ASSERT_FALSE(file.Create(testFile, false, testMode)); 881 ASSERT_TRUE(file.fd() == CFile::fd_invalid); 882 ASSERT_TRUE(!file.IsOpened()); 883 884 // Open and check contents 885 ASSERT_TRUE(file.Open(testFile, CFile::read)); 886 ASSERT_TRUE(file.IsOpened()); 887 ASSERT_TRUE(file.fd() != CFile::fd_invalid); 888 ASSERT_EQUALS(testFile, file.GetFilePath()); 889 ASSERT_EQUALS(4u, file.GetLength()); 890 ASSERT_EQUALS(1u, file.ReadUInt32()); 891 ASSERT_TRUE(file.Close()); 892 ASSERT_TRUE(file.fd() == CFile::fd_invalid); 893 ASSERT_TRUE(!file.IsOpened()); 894 } 895 896 // Check that owerwrite = true works as expected 897 { 898 CFile file; 899 ASSERT_TRUE(file.Create(testFile, true, testMode)); 900 ASSERT_TRUE(file.IsOpened()); 901 ASSERT_TRUE(file.fd() != CFile::fd_invalid); 902 ASSERT_EQUALS(testFile, file.GetFilePath()); 903 ASSERT_EQUALS(0u, file.GetLength()); 904 ASSERT_TRUE(file.Close()); 905 ASSERT_TRUE(file.fd() == CFile::fd_invalid); 906 ASSERT_TRUE(!file.IsOpened()); 907 } 908 909 ASSERT_TRUE(wxFile::Access(testFile.GetRaw(), wxFile::read)); 910 ASSERT_TRUE(wxFile::Access(testFile.GetRaw(), wxFile::write)); 911} 912 913 914TEST(CFile, SetLength) 915{ 916 CFile file(testFile, CFile::write); 917 918 ASSERT_EQUALS(0u, file.GetLength()); 919 file.SetLength(1024); 920 ASSERT_EQUALS(1024u, file.GetLength()); 921 ASSERT_EQUALS(1024u, file.Seek(0, wxFromEnd)); 922 file.SetLength(512u); 923 ASSERT_EQUALS(512u, file.GetLength()); 924 ASSERT_EQUALS(512u, file.Seek(0, wxFromEnd)); 925} 926 927 928TEST(CFile, GetAvailable) 929{ 930 { 931 CFile file(testFile, CFile::write); 932 933 writePredefData(&file); 934 } 935 936 CFile file(testFile, CFile::read); 937 938 const uint64 length = file.GetLength(); 939 while (!file.Eof()) { 940 ASSERT_EQUALS(length - file.GetPosition(), file.GetAvailable()); 941 file.ReadUInt32(); 942 ASSERT_EQUALS(length - file.GetPosition(), file.GetAvailable()); 943 } 944 945 ASSERT_EQUALS(0u, file.GetAvailable()); 946 947 file.Seek(1024, wxFromCurrent); 948 949 ASSERT_EQUALS(0u, file.GetAvailable()); 950} 951 952