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