1// ResourcesTest.cpp
2
3#include <stdio.h>
4#include <string>
5#include <unistd.h>
6#include <vector>
7
8#include <ByteOrder.h>
9#include <File.h>
10#include <Mime.h>
11#include <Resources.h>
12#include <String.h>
13#include <TypeConstants.h>
14#include <TestShell.h>
15
16#include "ResourcesTest.h"
17
18static const char *testDir		= "/tmp/testDir";
19static const char *x86ResFile	= "/tmp/testDir/x86.rsrc";
20static const char *ppcResFile	= "/tmp/testDir/ppc.rsrc";
21static const char *elfFile		= "/tmp/testDir/elf";
22static const char *elfNoResFile	= "/tmp/testDir/elf-no-res";
23static const char *pefFile		= "/tmp/testDir/pef";
24static const char *pefNoResFile	= "/tmp/testDir/pef-no-res";
25static const char *emptyFile	= "/tmp/testDir/empty-file";
26static const char *noResFile	= "/tmp/testDir/no-res-file";
27static const char *testFile1	= "/tmp/testDir/testFile1";
28static const char *testFile2	= "/tmp/testDir/testFile2";
29static const char *noSuchFile	= "/tmp/testDir/no-such-file";
30static const char *x86ResName	= "x86.rsrc";
31static const char *ppcResName	= "ppc.rsrc";
32static const char *elfName		= "elf";
33static const char *elfNoResName	= "elf-no-res";
34static const char *pefName		= "pef";
35static const char *pefNoResName	= "pef-no-res";
36
37static const int32 kEmptyResourceFileSize	= 1984;
38
39struct ResourceInfo {
40	ResourceInfo(type_code type, int32 id, const void *data, size_t size,
41				 const char *name = NULL)
42		: type(type),
43		  id(id),
44		  name(NULL),
45		  data(NULL),
46		  size(size)
47	{
48		if (data) {
49			this->data = new char[size];
50			memcpy(this->data, data, size);
51		}
52		if (name) {
53			int32 len = strlen(name);
54			this->name = new char[len + 1];
55			strcpy(this->name, name);
56		}
57	}
58
59	~ResourceInfo()
60	{
61		delete[] name;
62		delete[] data;
63	}
64
65	type_code	type;
66	int32		id;
67	char		*name;
68	char		*data;
69	size_t		size;
70};
71
72static const char *testResData1 = "I like strings, especially cellos.";
73static const int32 testResSize1 = strlen(testResData1) + 1;
74static const int32 testResData2 = 42;
75static const int32 testResSize2 = sizeof(int32);
76static const char *testResData3 = "application/bread-roll-counter";
77static const int32 testResSize3 = strlen(testResData3) + 1;
78static const char *testResData4 = "This is a long string. At least longer "
79								  "than the first one";
80static const int32 testResSize4 = strlen(testResData1) + 1;
81static const char *testResData6 = "Short, but true.";
82static const int32 testResSize6 = strlen(testResData6) + 1;
83
84static const ResourceInfo testResource1(B_STRING_TYPE, 74, testResData1,
85										testResSize1, "a string resource");
86static const ResourceInfo testResource2(B_INT32_TYPE, 17, &testResData2,
87										testResSize2, "just a resource");
88static const ResourceInfo testResource3(B_MIME_STRING_TYPE, 29, testResData3,
89										testResSize3, "another resource");
90static const ResourceInfo testResource4(B_STRING_TYPE, 75, &testResData4,
91										testResSize4,
92										"a second string resource");
93static const ResourceInfo testResource5(B_MIME_STRING_TYPE, 74, &testResData1,
94										testResSize1, "a string resource");
95static const ResourceInfo testResource6(B_STRING_TYPE, 74, &testResData6,
96										testResSize6,
97										"a third string resource");
98
99// helper class: maintains a ResourceInfo set
100struct ResourceSet {
101	typedef vector<const ResourceInfo*> ResInfoSet;
102
103	ResourceSet() : fResources() { }
104
105	ResourceSet(const ResourceSet &resourceSet)
106		: fResources()
107	{
108		fResources = resourceSet.fResources;
109	}
110
111	void add(const ResourceInfo *info)
112	{
113		if (info) {
114			remove(info->type, info->id);
115			fResources.insert(fResources.end(), info);
116		}
117	}
118
119	void remove(const ResourceInfo *info)
120	{
121		if (info)
122			remove(info->type, info->id);
123	}
124
125	void remove(type_code type, int32 id)
126	{
127		for (ResInfoSet::iterator it = fResources.begin();
128			 it != fResources.end();
129			 it++) {
130			const ResourceInfo *info = *it;
131			if (info->type == type && info->id == id) {
132				fResources.erase(it);
133				break;
134			}
135		}
136	}
137
138	int32 size() const
139	{
140		return fResources.size();
141	}
142
143	const ResourceInfo *infoAt(int32 index) const
144	{
145		const ResourceInfo *info = NULL;
146		if (index >= 0 && index < size())
147			info = fResources[index];
148		return info;
149	}
150
151	const ResourceInfo *find(type_code type, int32 id) const
152	{
153		const ResourceInfo *result = NULL;
154		for (ResInfoSet::const_iterator it = fResources.begin();
155			 result == NULL && it != fResources.end();
156			 it++) {
157			const ResourceInfo *info = *it;
158			if (info->type == type && info->id == id)
159				result = info;
160		}
161		return result;
162	}
163
164	ResInfoSet	fResources;
165};
166
167
168// Suite
169CppUnit::Test*
170ResourcesTest::Suite() {
171	CppUnit::TestSuite *suite = new CppUnit::TestSuite();
172	typedef CppUnit::TestCaller<ResourcesTest> TC;
173
174	suite->addTest( new TC("BResources::Init Test",
175						   &ResourcesTest::InitTest) );
176	suite->addTest( new TC("BResources::Read Test",
177						   &ResourcesTest::ReadTest) );
178	suite->addTest( new TC("BResources::Sync Test",
179						   &ResourcesTest::SyncTest) );
180	suite->addTest( new TC("BResources::Merge Test",
181						   &ResourcesTest::MergeTest) );
182	suite->addTest( new TC("BResources::WriteTo Test",
183						   &ResourcesTest::WriteToTest) );
184	suite->addTest( new TC("BResources::AddRemove Test",
185						   &ResourcesTest::AddRemoveTest) );
186	suite->addTest( new TC("BResources::ReadWrite Test",
187						   &ResourcesTest::ReadWriteTest) );
188
189	return suite;
190}
191
192// setUp
193void
194ResourcesTest::setUp()
195{
196	BasicTest::setUp();
197	BString unescapedTestDir(BTestShell::GlobalTestDir());
198	unescapedTestDir.CharacterEscape(" \t\n!\"'`$&()?*+{}[]<>|", '\\');
199	string resourcesTestDir(unescapedTestDir.String());
200	resourcesTestDir += "/resources";
201	execCommand(string("mkdir ") + testDir
202				+ " ; cp " + resourcesTestDir + "/" + x86ResName + " "
203						   + resourcesTestDir + "/" + ppcResName + " "
204						   + resourcesTestDir + "/" + elfName + " "
205						   + resourcesTestDir + "/" + elfNoResName + " "
206						   + resourcesTestDir + "/" + pefName + " "
207						   + resourcesTestDir + "/" + pefNoResName + " "
208						   + testDir
209				+ " ; touch " + emptyFile
210				+ " ; echo \"That's not a resource file.\" > " + noResFile
211				);
212}
213
214// tearDown
215void
216ResourcesTest::tearDown()
217{
218	execCommand(string("rm -rf ") + testDir);
219	BasicTest::tearDown();
220}
221
222// InitTest
223void
224ResourcesTest::InitTest()
225{
226	// 1. existing files, read only
227	// x86 resource file
228	NextSubTest();
229	{
230		BResources resources;
231		BFile file(x86ResFile, B_READ_ONLY);
232		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
233		CPPUNIT_ASSERT( resources.SetTo(&file, false) == B_OK );
234	}
235	// ppc resource file
236	NextSubTest();
237	{
238		BResources resources;
239		BFile file(ppcResFile, B_READ_ONLY);
240		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
241		CPPUNIT_ASSERT( resources.SetTo(&file, false) == B_OK );
242	}
243	// ELF binary containing resources
244	NextSubTest();
245	{
246		BResources resources;
247		BFile file(elfFile, B_READ_ONLY);
248		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
249		CPPUNIT_ASSERT( resources.SetTo(&file, false) == B_OK );
250	}
251	// ELF binary not containing resources
252	NextSubTest();
253	{
254		BResources resources;
255		BFile file(elfNoResFile, B_READ_ONLY);
256		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
257		CPPUNIT_ASSERT( resources.SetTo(&file, false) == B_OK );
258	}
259	// PEF binary containing resources
260	NextSubTest();
261	{
262		BResources resources;
263		BFile file(pefFile, B_READ_ONLY);
264		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
265		CPPUNIT_ASSERT( resources.SetTo(&file, false) == B_OK );
266	}
267	// PEF binary not containing resources
268	NextSubTest();
269	{
270		BResources resources;
271		BFile file(pefNoResFile, B_READ_ONLY);
272		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
273		CPPUNIT_ASSERT( resources.SetTo(&file, false) == B_OK );
274	}
275	// empty file
276	NextSubTest();
277	{
278		BResources resources;
279		BFile file(emptyFile, B_READ_ONLY);
280		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
281		CPPUNIT_ASSERT( resources.SetTo(&file, false) == B_OK );
282	}
283	// non-resource file
284	NextSubTest();
285	{
286		BResources resources;
287		BFile file(noResFile, B_READ_ONLY);
288		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
289		CPPUNIT_ASSERT( equals(resources.SetTo(&file, false), B_ERROR,
290							   B_IO_ERROR) );
291	}
292
293	// 2. existing files, read only, clobber
294	// x86 resource file
295	NextSubTest();
296	{
297		BResources resources;
298		BFile file(x86ResFile, B_READ_ONLY);
299		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
300		CPPUNIT_ASSERT( resources.SetTo(&file, true) == B_OK );
301	}
302	// ppc resource file
303	NextSubTest();
304	{
305		BResources resources;
306		BFile file(ppcResFile, B_READ_ONLY);
307		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
308		CPPUNIT_ASSERT( resources.SetTo(&file, true) == B_OK );
309	}
310	// ELF binary containing resources
311	NextSubTest();
312	{
313		BResources resources;
314		BFile file(elfFile, B_READ_ONLY);
315		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
316		CPPUNIT_ASSERT( resources.SetTo(&file, true) == B_OK );
317	}
318	// ELF binary not containing resources
319	NextSubTest();
320	{
321		BResources resources;
322		BFile file(elfNoResFile, B_READ_ONLY);
323		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
324		CPPUNIT_ASSERT( resources.SetTo(&file, true) == B_OK );
325	}
326	// PEF binary containing resources
327	NextSubTest();
328	{
329		BResources resources;
330		BFile file(pefFile, B_READ_ONLY);
331		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
332		CPPUNIT_ASSERT( resources.SetTo(&file, true) == B_OK );
333	}
334	// PEF binary not containing resources
335	NextSubTest();
336	{
337		BResources resources;
338		BFile file(pefNoResFile, B_READ_ONLY);
339		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
340		CPPUNIT_ASSERT( resources.SetTo(&file, true) == B_OK );
341	}
342	// empty file
343	NextSubTest();
344	{
345		BResources resources;
346		BFile file(emptyFile, B_READ_ONLY);
347		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
348		CPPUNIT_ASSERT( resources.SetTo(&file, true) == B_OK );
349	}
350	// non-resource file
351	NextSubTest();
352	{
353		BResources resources;
354		BFile file(noResFile, B_READ_ONLY);
355		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
356		CPPUNIT_ASSERT( resources.SetTo(&file, true) == B_OK );
357	}
358
359	// 3. existing files, read/write
360	// x86 resource file
361	NextSubTest();
362	{
363		BResources resources;
364		BFile file(x86ResFile, B_READ_WRITE);
365		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
366		CPPUNIT_ASSERT( resources.SetTo(&file, false) == B_OK );
367	}
368	// ppc resource file
369	NextSubTest();
370	{
371		BResources resources;
372		BFile file(ppcResFile, B_READ_WRITE);
373		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
374		CPPUNIT_ASSERT( resources.SetTo(&file, false) == B_OK );
375	}
376	// ELF binary containing resources
377	NextSubTest();
378	{
379		BResources resources;
380		BFile file(elfFile, B_READ_WRITE);
381		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
382		CPPUNIT_ASSERT( resources.SetTo(&file, false) == B_OK );
383	}
384	// ELF binary not containing resources
385	NextSubTest();
386	{
387		BResources resources;
388		BFile file(elfNoResFile, B_READ_WRITE);
389		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
390		CPPUNIT_ASSERT( resources.SetTo(&file, false) == B_OK );
391	}
392	// PEF binary containing resources
393	NextSubTest();
394	{
395		BResources resources;
396		BFile file(pefFile, B_READ_WRITE);
397		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
398		CPPUNIT_ASSERT( resources.SetTo(&file, false) == B_OK );
399	}
400	// PEF binary not containing resources
401	NextSubTest();
402	{
403		BResources resources;
404		BFile file(pefNoResFile, B_READ_WRITE);
405		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
406		CPPUNIT_ASSERT( resources.SetTo(&file, false) == B_OK );
407	}
408	// empty file
409	NextSubTest();
410	{
411		BResources resources;
412		BFile file(emptyFile, B_READ_WRITE);
413		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
414		CPPUNIT_ASSERT( resources.SetTo(&file, false) == B_OK );
415	}
416	// non-resource file
417	NextSubTest();
418	{
419		BResources resources;
420		BFile file(noResFile, B_READ_WRITE);
421		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
422		CPPUNIT_ASSERT( equals(resources.SetTo(&file, false), B_ERROR,
423							   B_IO_ERROR) );
424	}
425
426	// 4. existing files, read/write, clobber
427	// x86 resource file
428	NextSubTest();
429	{
430		BResources resources;
431		BFile file(x86ResFile, B_READ_WRITE);
432		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
433		CPPUNIT_ASSERT( resources.SetTo(&file, true) == B_OK );
434		CPPUNIT_ASSERT( resources.Sync() == B_OK );
435	}
436	// ppc resource file
437	NextSubTest();
438	{
439		BResources resources;
440		BFile file(ppcResFile, B_READ_WRITE);
441		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
442		CPPUNIT_ASSERT( resources.SetTo(&file, true) == B_OK );
443		CPPUNIT_ASSERT( resources.Sync() == B_OK );
444	}
445	// ELF binary containing resources
446	NextSubTest();
447	{
448		BResources resources;
449		BFile file(elfFile, B_READ_WRITE);
450		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
451		CPPUNIT_ASSERT( resources.SetTo(&file, true) == B_OK );
452		CPPUNIT_ASSERT( resources.Sync() == B_OK );
453	}
454	// ELF binary not containing resources
455	NextSubTest();
456	{
457		BResources resources;
458		BFile file(elfNoResFile, B_READ_WRITE);
459		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
460		CPPUNIT_ASSERT( resources.SetTo(&file, true) == B_OK );
461		CPPUNIT_ASSERT( resources.Sync() == B_OK );
462	}
463	// PEF binary containing resources
464	NextSubTest();
465	{
466		BResources resources;
467		BFile file(pefFile, B_READ_WRITE);
468		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
469		CPPUNIT_ASSERT( resources.SetTo(&file, true) == B_OK );
470		CPPUNIT_ASSERT( resources.Sync() == B_OK );
471	}
472	// PEF binary not containing resources
473	NextSubTest();
474	{
475		BResources resources;
476		BFile file(pefNoResFile, B_READ_WRITE);
477		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
478		CPPUNIT_ASSERT( resources.SetTo(&file, true) == B_OK );
479		CPPUNIT_ASSERT( resources.Sync() == B_OK );
480	}
481	// empty file
482	NextSubTest();
483	{
484		BResources resources;
485		BFile file(emptyFile, B_READ_WRITE);
486		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
487		CPPUNIT_ASSERT( resources.SetTo(&file, true) == B_OK );
488		CPPUNIT_ASSERT( resources.Sync() == B_OK );
489	}
490	// non-resource file
491	NextSubTest();
492	{
493		BResources resources;
494		BFile file(noResFile, B_READ_WRITE);
495		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
496		CPPUNIT_ASSERT( resources.SetTo(&file, true) == B_OK );
497	}
498
499	// 5. bad args
500	// uninitialized file
501	NextSubTest();
502	{
503		BResources resources;
504		BFile file;
505		CPPUNIT_ASSERT( file.InitCheck() == B_NO_INIT );
506		CPPUNIT_ASSERT( resources.SetTo(&file, false) == B_NO_INIT );
507	}
508	// badly initialized file
509	NextSubTest();
510	{
511		BResources resources;
512		BFile file(noSuchFile, B_READ_ONLY);
513		CPPUNIT_ASSERT( file.InitCheck() == B_ENTRY_NOT_FOUND );
514		CPPUNIT_ASSERT( resources.SetTo(&file, false) == B_ENTRY_NOT_FOUND );
515	}
516	// NULL file
517	NextSubTest();
518	{
519		BResources resources;
520		// R5: A NULL file is B_OK!
521//		CPPUNIT_ASSERT( resources.SetTo(NULL, false) == B_BAD_VALUE );
522		CPPUNIT_ASSERT( resources.SetTo(NULL, false) == B_OK );
523	}
524}
525
526// ReadResTest
527static
528void
529ReadResTest(BResources& resources, const ResourceInfo& info, bool exists)
530{
531	if (exists) {
532		// test an existing resource
533		// HasResource()
534		CPPUNIT_ASSERT( resources.HasResource(info.type, info.id) == true );
535		CPPUNIT_ASSERT( resources.HasResource(info.type, info.name)
536						== true );
537		// GetResourceInfo()
538		const char *name;
539		size_t length;
540		int32 id;
541		CPPUNIT_ASSERT( resources.GetResourceInfo(info.type, info.id,
542												  &name, &length) == true );
543		CPPUNIT_ASSERT( name == NULL && info.name == NULL
544						|| name != NULL && info.name != NULL
545						   && !strcmp(name, info.name) );
546		CPPUNIT_ASSERT( length == info.size );
547		CPPUNIT_ASSERT( resources.GetResourceInfo(info.type, info.name,
548												  &id, &length) == true );
549		CPPUNIT_ASSERT( id == info.id );
550		CPPUNIT_ASSERT( length == info.size );
551		// LoadResource
552		size_t length1;
553		size_t length2;
554		const void *data1
555			= resources.LoadResource(info.type, info.id, &length1);
556		const void *data2
557			= resources.LoadResource(info.type, info.name, &length2);
558		CPPUNIT_ASSERT( length1 == info.size && length2 == info.size );
559		CPPUNIT_ASSERT( data1 != NULL && data1 == data2 );
560		CPPUNIT_ASSERT( !memcmp(data1, info.data, info.size) );
561		// GetResourceInfo()
562		type_code type = 0;
563		id = 0;
564		length = 0;
565		name = NULL;
566		CPPUNIT_ASSERT( resources.GetResourceInfo(data1, &type, &id, &length,
567												  &name) == true );
568		CPPUNIT_ASSERT( type == info.type );
569		CPPUNIT_ASSERT( id == info.id );
570		CPPUNIT_ASSERT( length == info.size );
571		CPPUNIT_ASSERT( name == NULL && info.name == NULL
572						|| name != NULL && info.name != NULL
573						   && !strcmp(name, info.name) );
574		// ReadResource()
575		const int32 bufferSize = 1024;
576		char buffer[bufferSize];
577		memset(buffer, 0x0f, bufferSize);
578		// read past the end
579		status_t error = resources.ReadResource(info.type, info.id, buffer,
580												info.size + 2, bufferSize);
581		CPPUNIT_ASSERT( error == B_OK );
582		for (int32 i = 0; i < bufferSize; i++)
583			CPPUNIT_ASSERT( buffer[i] == 0x0f );
584		// read 2 bytes from the middle
585		int32 offset = (info.size - 2) / 2;
586		error = resources.ReadResource(info.type, info.id, buffer, offset, 2);
587		CPPUNIT_ASSERT( error == B_OK );
588		CPPUNIT_ASSERT( !memcmp(buffer, (const char*)info.data + offset, 2) );
589		for (int32 i = 2; i < bufferSize; i++)
590			CPPUNIT_ASSERT( buffer[i] == 0x0f );
591		// read the whole chunk
592		error = resources.ReadResource(info.type, info.id, buffer, 0,
593									   bufferSize);
594		CPPUNIT_ASSERT( error == B_OK );
595		CPPUNIT_ASSERT( !memcmp(buffer, info.data, info.size) );
596		// FindResource()
597		size_t lengthFound1;
598		size_t lengthFound2;
599		void *dataFound1
600			= resources.FindResource(info.type, info.id, &lengthFound1);
601		void *dataFound2
602			= resources.FindResource(info.type, info.name, &lengthFound2);
603		CPPUNIT_ASSERT( dataFound1 != NULL && dataFound2 != NULL );
604		CPPUNIT_ASSERT( lengthFound1 == info.size
605						&& lengthFound2 == info.size );
606		CPPUNIT_ASSERT( !memcmp(dataFound1, info.data, info.size) );
607		CPPUNIT_ASSERT( !memcmp(dataFound2, info.data, info.size) );
608		free(dataFound1);
609		free(dataFound2);
610	} else {
611		// test a non-existing resource
612		// HasResource()
613		CPPUNIT_ASSERT( resources.HasResource(info.type, info.id) == false );
614		CPPUNIT_ASSERT( resources.HasResource(info.type, info.name)
615						== false );
616		// GetResourceInfo()
617		const char *name;
618		size_t length;
619		int32 id;
620		CPPUNIT_ASSERT( resources.GetResourceInfo(info.type, info.id,
621												  &name, &length) == false );
622		CPPUNIT_ASSERT( resources.GetResourceInfo(info.type, info.name,
623												  &id, &length) == false );
624		// LoadResource
625		size_t length1;
626		size_t length2;
627		const void *data1
628			= resources.LoadResource(info.type, info.id, &length1);
629		const void *data2
630			= resources.LoadResource(info.type, info.name, &length2);
631		CPPUNIT_ASSERT( data1 == NULL && data2 == NULL );
632		// ReadResource()
633		const int32 bufferSize = 1024;
634		char buffer[bufferSize];
635		status_t error = resources.ReadResource(info.type, info.id, buffer,
636												0, bufferSize);
637		CPPUNIT_ASSERT( error == B_BAD_VALUE );
638		// FindResource()
639		size_t lengthFound1;
640		size_t lengthFound2;
641		void *dataFound1
642			= resources.FindResource(info.type, info.id, &lengthFound1);
643		void *dataFound2
644			= resources.FindResource(info.type, info.name, &lengthFound2);
645		CPPUNIT_ASSERT( dataFound1 == NULL && dataFound2 == NULL );
646	}
647}
648
649// ReadBadResTest
650static
651void
652ReadBadResTest(BResources& resources, const ResourceInfo& info)
653{
654	// HasResource()
655	CPPUNIT_ASSERT( resources.HasResource(info.type, info.id) == false );
656	CPPUNIT_ASSERT( resources.HasResource(info.type, info.name)
657					== false );
658	// GetResourceInfo()
659	type_code type;
660	const char *name;
661	size_t length;
662	int32 id;
663	CPPUNIT_ASSERT( resources.GetResourceInfo(info.type, info.id,
664											  &name, &length) == false );
665	CPPUNIT_ASSERT( resources.GetResourceInfo(info.type, info.name,
666											  &id, &length) == false );
667	CPPUNIT_ASSERT( resources.GetResourceInfo(0, &type, &id,
668											  &name, &length) == false );
669	CPPUNIT_ASSERT( resources.GetResourceInfo(info.type, 0, &id,
670											  &name, &length) == false );
671	// LoadResource
672	size_t length1;
673	size_t length2;
674	const void *data1
675		= resources.LoadResource(info.type, info.id, &length1);
676	const void *data2
677		= resources.LoadResource(info.type, info.name, &length2);
678	CPPUNIT_ASSERT( data1 == NULL && data2 == NULL );
679	// ReadResource()
680	const int32 bufferSize = 1024;
681	char buffer[bufferSize];
682	status_t error = resources.ReadResource(info.type, info.id, buffer,
683											0, bufferSize);
684	CPPUNIT_ASSERT( error == B_BAD_VALUE );
685	// FindResource()
686	size_t lengthFound1;
687	size_t lengthFound2;
688	void *dataFound1
689		= resources.FindResource(info.type, info.id, &lengthFound1);
690	void *dataFound2
691		= resources.FindResource(info.type, info.name, &lengthFound2);
692	CPPUNIT_ASSERT( dataFound1 == NULL && dataFound2 == NULL );
693}
694
695// ReadTest
696void
697ResourcesTest::ReadTest()
698{
699	// tests:
700	// * HasResource()
701	// * GetResourceInfo()
702	// * LoadResource()
703	// * ReadResource()
704	// * FindResource()
705	// * PreloadResource()
706
707	// 1. basic tests
708	// x86 resource file
709	NextSubTest();
710	{
711		BFile file(x86ResFile, B_READ_ONLY);
712		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
713		BResources resources;
714		CPPUNIT_ASSERT( resources.SetTo(&file, false) == B_OK );
715		ReadResTest(resources, testResource1, true);
716		ReadResTest(resources, testResource2, true);
717		ReadResTest(resources, testResource3, true);
718		ReadResTest(resources, testResource4, false);
719		ReadResTest(resources, testResource5, false);
720	}
721	// ppc resource file
722	NextSubTest();
723	{
724		BFile file(ppcResFile, B_READ_ONLY);
725		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
726		BResources resources;
727		CPPUNIT_ASSERT( resources.SetTo(&file, false) == B_OK );
728		ReadResTest(resources, testResource1, true);
729		ReadResTest(resources, testResource2, true);
730		ReadResTest(resources, testResource3, true);
731		ReadResTest(resources, testResource4, false);
732		ReadResTest(resources, testResource5, false);
733	}
734	// ELF binary containing resources
735	NextSubTest();
736	{
737		BFile file(elfFile, B_READ_ONLY);
738		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
739		BResources resources;
740		CPPUNIT_ASSERT( resources.SetTo(&file, false) == B_OK );
741		ReadResTest(resources, testResource1, true);
742		ReadResTest(resources, testResource2, true);
743		ReadResTest(resources, testResource3, true);
744		ReadResTest(resources, testResource4, false);
745		ReadResTest(resources, testResource5, false);
746	}
747	// ELF binary not containing resources
748	NextSubTest();
749	{
750		BFile file(elfNoResFile, B_READ_ONLY);
751		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
752		BResources resources;
753		CPPUNIT_ASSERT( resources.SetTo(&file, false) == B_OK );
754		ReadResTest(resources, testResource1, false);
755		ReadResTest(resources, testResource2, false);
756		ReadResTest(resources, testResource3, false);
757		ReadResTest(resources, testResource4, false);
758		ReadResTest(resources, testResource5, false);
759	}
760	// PEF binary containing resources
761	NextSubTest();
762	{
763		BFile file(pefFile, B_READ_ONLY);
764		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
765		BResources resources;
766		CPPUNIT_ASSERT( resources.SetTo(&file, false) == B_OK );
767		ReadResTest(resources, testResource1, true);
768		ReadResTest(resources, testResource2, true);
769		ReadResTest(resources, testResource3, true);
770		ReadResTest(resources, testResource4, false);
771		ReadResTest(resources, testResource5, false);
772	}
773	// PEF binary not containing resources
774	NextSubTest();
775	{
776		BFile file(pefNoResFile, B_READ_ONLY);
777		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
778		BResources resources;
779		CPPUNIT_ASSERT( resources.SetTo(&file, false) == B_OK );
780		ReadResTest(resources, testResource1, false);
781		ReadResTest(resources, testResource2, false);
782		ReadResTest(resources, testResource3, false);
783		ReadResTest(resources, testResource4, false);
784		ReadResTest(resources, testResource5, false);
785	}
786
787	// 2. PreloadResource()
788	// Hard to test: just preload all and check, if it still works.
789	NextSubTest();
790	{
791		BFile file(x86ResFile, B_READ_ONLY);
792		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
793		BResources resources;
794		CPPUNIT_ASSERT( resources.SetTo(&file, false) == B_OK );
795		// non existing type
796		CPPUNIT_ASSERT( resources.PreloadResourceType(B_MESSENGER_TYPE)
797						== B_OK );
798		// int32 type
799		CPPUNIT_ASSERT( resources.PreloadResourceType(B_INT32_TYPE) == B_OK );
800		// all types
801		CPPUNIT_ASSERT( resources.PreloadResourceType() == B_OK );
802		ReadResTest(resources, testResource1, true);
803		ReadResTest(resources, testResource2, true);
804		ReadResTest(resources, testResource3, true);
805		ReadResTest(resources, testResource4, false);
806		ReadResTest(resources, testResource5, false);
807	}
808	// uninitialized BResources
809	NextSubTest();
810	{
811		BResources resources;
812		// int32 type
813		CPPUNIT_ASSERT( resources.PreloadResourceType(B_INT32_TYPE) == B_OK );
814		// all types
815		CPPUNIT_ASSERT( resources.PreloadResourceType() == B_OK );
816	}
817
818	// 3. the index versions of GetResourceInfo()
819	// index only
820	NextSubTest();
821	{
822		BFile file(x86ResFile, B_READ_ONLY);
823		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
824		BResources resources;
825		CPPUNIT_ASSERT( resources.SetTo(&file, false) == B_OK );
826		const ResourceInfo *resourceInfos[] = {
827			&testResource1, &testResource2, &testResource3
828		};
829		int32 resourceCount = sizeof(resourceInfos) / sizeof(ResourceInfo*);
830		for (int32 i = 0; i < resourceCount; i++) {
831			const ResourceInfo &info = *resourceInfos[i];
832			type_code type;
833			int32 id;
834			const char *name;
835			size_t length;
836			CPPUNIT_ASSERT( resources.GetResourceInfo(i, &type, &id, &name,
837													  &length) == true );
838			CPPUNIT_ASSERT( id == info.id );
839			CPPUNIT_ASSERT( type == info.type );
840			CPPUNIT_ASSERT( name == NULL && info.name == NULL
841							|| name != NULL && info.name != NULL
842							   && !strcmp(name, info.name) );
843			CPPUNIT_ASSERT( length == info.size );
844		}
845		type_code type;
846		int32 id;
847		const char *name;
848		size_t length;
849		CPPUNIT_ASSERT( resources.GetResourceInfo(resourceCount, &type, &id,
850												  &name, &length) == false );
851	}
852	// type and index
853	NextSubTest();
854	{
855		BFile file(x86ResFile, B_READ_WRITE);
856		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
857		BResources resources;
858		CPPUNIT_ASSERT( resources.SetTo(&file, false) == B_OK );
859		CPPUNIT_ASSERT( resources.AddResource(testResource4.type,
860											  testResource4.id,
861											  testResource4.data,
862											  testResource4.size,
863											  testResource4.name) == B_OK );
864
865		const ResourceInfo *resourceInfos[] = {
866			&testResource1, &testResource4
867		};
868		int32 resourceCount = sizeof(resourceInfos) / sizeof(ResourceInfo*);
869		type_code type = resourceInfos[0]->type;
870		for (int32 i = 0; i < resourceCount; i++) {
871			const ResourceInfo &info = *resourceInfos[i];
872			int32 id;
873			const char *name;
874			size_t length;
875			CPPUNIT_ASSERT( resources.GetResourceInfo(type, i, &id, &name,
876													  &length) == true );
877			CPPUNIT_ASSERT( id == info.id );
878			CPPUNIT_ASSERT( type == info.type );
879			CPPUNIT_ASSERT( name == NULL && info.name == NULL
880							|| name != NULL && info.name != NULL
881							   && !strcmp(name, info.name) );
882			CPPUNIT_ASSERT( length == info.size );
883		}
884		int32 id;
885		const char *name;
886		size_t length;
887		CPPUNIT_ASSERT( resources.GetResourceInfo(type, resourceCount, &id,
888												  &name, &length) == false );
889	}
890
891	// 4. error cases
892	// non-resource file
893	NextSubTest();
894	{
895		BResources resources;
896		BFile file(noResFile, B_READ_ONLY);
897		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
898		CPPUNIT_ASSERT( equals(resources.SetTo(&file, false), B_ERROR,
899							   B_IO_ERROR) );
900		ReadBadResTest(resources, testResource1);
901	}
902	// uninitialized file
903	NextSubTest();
904	{
905		BResources resources;
906		BFile file;
907		CPPUNIT_ASSERT( file.InitCheck() == B_NO_INIT );
908		CPPUNIT_ASSERT( resources.SetTo(&file, false) == B_NO_INIT );
909		ReadBadResTest(resources, testResource1);
910	}
911	// badly initialized file
912	NextSubTest();
913	{
914		BResources resources;
915		BFile file(noSuchFile, B_READ_ONLY);
916		CPPUNIT_ASSERT( file.InitCheck() == B_ENTRY_NOT_FOUND );
917		CPPUNIT_ASSERT( resources.SetTo(&file, false) == B_ENTRY_NOT_FOUND );
918		ReadBadResTest(resources, testResource1);
919	}
920	// NULL file
921	NextSubTest();
922	{
923		BResources resources;
924		// R5: A NULL file is B_OK!
925//		CPPUNIT_ASSERT( resources.SetTo(NULL, false) == B_BAD_VALUE );
926		CPPUNIT_ASSERT( resources.SetTo(NULL, false) == B_OK );
927		ReadBadResTest(resources, testResource1);
928	}
929	// bad args
930	NextSubTest();
931	{
932		BFile file(x86ResFile, B_READ_ONLY);
933		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
934		BResources resources;
935		CPPUNIT_ASSERT( resources.SetTo(&file, false) == B_OK );
936		const ResourceInfo &info = testResource1;
937		// GetResourceInfo()
938		CPPUNIT_ASSERT( resources.GetResourceInfo(info.type, info.id,
939												  NULL, NULL) == true );
940		CPPUNIT_ASSERT( resources.GetResourceInfo(info.type, info.name,
941												  NULL, NULL) == true );
942		CPPUNIT_ASSERT( resources.GetResourceInfo(0L, (type_code*)NULL, NULL,
943												  NULL, NULL) == true );
944		CPPUNIT_ASSERT( resources.GetResourceInfo(info.type, 0, NULL,
945												  NULL, NULL) == true );
946		// LoadResource
947		const void *data1
948			= resources.LoadResource(info.type, info.id, NULL);
949		const void *data2
950			= resources.LoadResource(info.type, info.name, NULL);
951		CPPUNIT_ASSERT( data1 != NULL && data2 != NULL );
952		// ReadResource()
953		const int32 bufferSize = 1024;
954		status_t error = resources.ReadResource(info.type, info.id, NULL,
955												0, bufferSize);
956		CPPUNIT_ASSERT( error == B_BAD_VALUE );
957		// FindResource()
958		void *dataFound1
959			= resources.FindResource(info.type, info.id, NULL);
960		void *dataFound2
961			= resources.FindResource(info.type, info.name, NULL);
962		CPPUNIT_ASSERT( dataFound1 != NULL && dataFound2 != NULL );
963		free(dataFound1);
964		free(dataFound2);
965	}
966}
967
968// AddResources
969static
970void
971AddResources(BResources &resources, const ResourceInfo *resourceInfos[],
972			 int32 count)
973{
974	for (int32 i = 0; i < count; i++) {
975		const ResourceInfo &info = *resourceInfos[i];
976		CPPUNIT_ASSERT( resources.AddResource(info.type, info.id, info.data,
977											  info.size, info.name) == B_OK );
978	}
979}
980
981// CompareFiles
982static
983void
984CompareFiles(BFile &file1, BFile &file2)
985{
986	off_t size1 = 0;
987	off_t size2 = 0;
988	CPPUNIT_ASSERT( file1.GetSize(&size1) == B_OK );
989	CPPUNIT_ASSERT( file2.GetSize(&size2) == B_OK );
990	CPPUNIT_ASSERT( size1 == size2 );
991	char *buffer1 = new char[size1];
992	char *buffer2 = new char[size2];
993	CPPUNIT_ASSERT( file1.ReadAt(0, buffer1, size1) == size1 );
994	CPPUNIT_ASSERT( file2.ReadAt(0, buffer2, size2) == size2 );
995	for (int32 i = 0; i < size1; i++)
996		CPPUNIT_ASSERT( buffer1[i] == buffer2[i] );
997	delete[] buffer1;
998	delete[] buffer2;
999}
1000
1001// CompareResources
1002static
1003void
1004CompareResources(BResources &resources, const ResourceSet &resourceSet)
1005{
1006	// compare the count
1007	int32 count = resourceSet.size();
1008	{
1009		type_code type;
1010		int32 id;
1011		const char *name;
1012		size_t length;
1013		CPPUNIT_ASSERT( resources.GetResourceInfo(count, &type, &id,
1014												  &name, &length) == false );
1015	}
1016	// => resources contains at most count resources. If it contains all the
1017	// resources that are in resourceSet, then the sets are equal.
1018	for (int32 i = 0; i < count; i++) {
1019		const ResourceInfo &info = *resourceSet.infoAt(i);
1020		const char *name;
1021		size_t length;
1022		CPPUNIT_ASSERT( resources.GetResourceInfo(info.type, info.id, &name,
1023												  &length) == true );
1024		CPPUNIT_ASSERT( name == NULL && info.name == NULL
1025						|| name != NULL && info.name != NULL
1026						   && !strcmp(name, info.name) );
1027		CPPUNIT_ASSERT( length == info.size );
1028		const void *data = resources.LoadResource(info.type, info.id, &length);
1029		CPPUNIT_ASSERT( data != NULL && length == info.size );
1030		CPPUNIT_ASSERT( !memcmp(data, info.data, info.size) );
1031	}
1032}
1033
1034// AddResource
1035static
1036void
1037AddResource(BResources &resources, ResourceSet &resourceSet,
1038			const ResourceInfo &info)
1039{
1040	resourceSet.add(&info);
1041	CPPUNIT_ASSERT( resources.AddResource(info.type, info.id, info.data,
1042										  info.size, info.name) == B_OK );
1043	CompareResources(resources, resourceSet);
1044}
1045
1046// RemoveResource
1047static
1048void
1049RemoveResource(BResources &resources, ResourceSet &resourceSet,
1050			   const ResourceInfo &info, bool firstVersion)
1051{
1052	resourceSet.remove(&info);
1053	if (firstVersion) {
1054		CPPUNIT_ASSERT( resources.RemoveResource(info.type, info.id) == B_OK );
1055	} else {
1056		size_t size;
1057		const void *data = resources.LoadResource(info.type, info.id, &size);
1058		CPPUNIT_ASSERT( data != NULL );
1059		CPPUNIT_ASSERT( resources.RemoveResource(data) == B_OK );
1060	}
1061	CompareResources(resources, resourceSet);
1062}
1063
1064// ListResources
1065/*static
1066void
1067ListResources(BResources &resources)
1068{
1069	printf("Resources:\n");
1070	type_code type;
1071	int32 id;
1072	const char *name;
1073	size_t size;
1074	for (int32 i = 0;
1075		 resources.GetResourceInfo(i, &type, &id, &name, &size);
1076		 i++) {
1077		type_code bigType = htonl(type);
1078		printf("resource %2ld: type: `%.4s', id: %3ld, size: %5lu\n", i,
1079			   (char*)&bigType, id, size);
1080	}
1081}*/
1082
1083// SyncTest
1084void
1085ResourcesTest::SyncTest()
1086{
1087	// Sync() is not easy to test. We just check its return value for now.
1088	NextSubTest();
1089	execCommand(string("cp ") + x86ResFile + " " + testFile1);
1090	{
1091		BFile file(testFile1, B_READ_WRITE);
1092		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
1093		BResources resources;
1094		CPPUNIT_ASSERT( resources.SetTo(&file, false) == B_OK );
1095		// add a resource and Sync()
1096		CPPUNIT_ASSERT( resources.AddResource(testResource4.type,
1097											  testResource4.id,
1098											  testResource4.data,
1099											  testResource4.size,
1100											  testResource4.name) == B_OK );
1101		CPPUNIT_ASSERT( resources.Sync() == B_OK );
1102		// remove a resource and Sync()
1103		CPPUNIT_ASSERT( resources.RemoveResource(testResource1.type,
1104												 testResource1.id) == B_OK );
1105		CPPUNIT_ASSERT( resources.Sync() == B_OK );
1106	}
1107	// error cases
1108	// read only file
1109	NextSubTest();
1110	{
1111		BFile file(testFile1, B_READ_ONLY);
1112		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
1113		BResources resources;
1114		CPPUNIT_ASSERT( resources.SetTo(&file, false) == B_OK );
1115		CPPUNIT_ASSERT( resources.Sync() == B_NOT_ALLOWED );
1116	}
1117	// uninitialized BResources
1118	NextSubTest();
1119	{
1120		BResources resources;
1121		CPPUNIT_ASSERT( resources.Sync() == B_NO_INIT );
1122	}
1123	// badly initialized BResources
1124	NextSubTest();
1125	{
1126		BFile file(noSuchFile, B_READ_ONLY);
1127		CPPUNIT_ASSERT( file.InitCheck() == B_ENTRY_NOT_FOUND );
1128		BResources resources;
1129		CPPUNIT_ASSERT( resources.SetTo(&file, false) == B_ENTRY_NOT_FOUND );
1130		CPPUNIT_ASSERT( resources.Sync() == B_NO_INIT );
1131	}
1132}
1133
1134// MergeTest
1135void
1136ResourcesTest::MergeTest()
1137{
1138	// empty file, merge with resources from another file
1139	NextSubTest();
1140	{
1141		BFile file(testFile1, B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
1142		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
1143		BResources resources;
1144		CPPUNIT_ASSERT( resources.SetTo(&file, true) == B_OK );
1145		BFile file2(x86ResFile, B_READ_ONLY);
1146		CPPUNIT_ASSERT( resources.MergeFrom(&file2) == B_OK );
1147		ResourceSet resourceSet;
1148		resourceSet.add(&testResource1);
1149		resourceSet.add(&testResource2);
1150		resourceSet.add(&testResource3);
1151		CompareResources(resources, resourceSet);
1152	}
1153	// empty file, add some resources first and then merge with resources
1154	// from another file
1155	NextSubTest();
1156	{
1157		BFile file(testFile1, B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
1158		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
1159		BResources resources;
1160		CPPUNIT_ASSERT( resources.SetTo(&file, true) == B_OK );
1161		// add some resources
1162		ResourceSet resourceSet;
1163		AddResource(resources, resourceSet, testResource4);
1164		AddResource(resources, resourceSet, testResource5);
1165		AddResource(resources, resourceSet, testResource6);
1166		// merge
1167		BFile file2(x86ResFile, B_READ_ONLY);
1168		CPPUNIT_ASSERT( resources.MergeFrom(&file2) == B_OK );
1169		resourceSet.add(&testResource1);	// replaces testResource6
1170		resourceSet.add(&testResource2);
1171		resourceSet.add(&testResource3);
1172		CompareResources(resources, resourceSet);
1173	}
1174	// error cases
1175	// bad args
1176	NextSubTest();
1177	{
1178		BFile file(testFile1, B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
1179		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
1180		BResources resources;
1181		CPPUNIT_ASSERT( resources.SetTo(&file, true) == B_OK );
1182		BFile file2(noResFile, B_READ_ONLY);
1183		// non-resource file
1184		CPPUNIT_ASSERT( resources.MergeFrom(&file2) == B_IO_ERROR );
1185		// NULL file
1186// R5: crashs
1187#if !TEST_R5
1188		CPPUNIT_ASSERT( resources.MergeFrom(NULL) == B_BAD_VALUE );
1189#endif
1190	}
1191	// uninitialized BResources
1192	// R5: returns B_OK!
1193	NextSubTest();
1194	{
1195		BResources resources;
1196		BFile file2(x86ResFile, B_READ_ONLY);
1197		CPPUNIT_ASSERT( resources.MergeFrom(&file2) == B_OK );
1198	}
1199	// badly initialized BResources
1200	// R5: returns B_OK!
1201	NextSubTest();
1202	{
1203		BFile file(noSuchFile, B_READ_ONLY);
1204		CPPUNIT_ASSERT( file.InitCheck() == B_ENTRY_NOT_FOUND );
1205		BResources resources;
1206		CPPUNIT_ASSERT( resources.SetTo(&file, true) == B_ENTRY_NOT_FOUND );
1207		BFile file2(x86ResFile, B_READ_ONLY);
1208		CPPUNIT_ASSERT( resources.MergeFrom(&file2) == B_OK );
1209	}
1210}
1211
1212// WriteToTest
1213void
1214ResourcesTest::WriteToTest()
1215{
1216	// take a file with resources and write them to an empty file
1217	NextSubTest();
1218	{
1219		BFile file(x86ResFile, B_READ_ONLY);
1220		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
1221		BResources resources;
1222		CPPUNIT_ASSERT( resources.SetTo(&file, false) == B_OK );
1223		BFile file2(testFile1, B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
1224		CPPUNIT_ASSERT( file2.InitCheck() == B_OK );
1225		CPPUNIT_ASSERT( resources.WriteTo(&file2) == B_OK);
1226		CPPUNIT_ASSERT( resources.File() == file2 );
1227	}
1228	// take a file with resources and write them to an non-empty non-resource
1229	// file
1230	NextSubTest();
1231	{
1232		BFile file(x86ResFile, B_READ_ONLY);
1233		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
1234		BResources resources;
1235		CPPUNIT_ASSERT( resources.SetTo(&file, false) == B_OK );
1236		BFile file2(noResFile, B_READ_WRITE);
1237		CPPUNIT_ASSERT( file2.InitCheck() == B_OK );
1238		CPPUNIT_ASSERT( resources.WriteTo(&file2) == B_IO_ERROR);
1239		CPPUNIT_ASSERT( resources.File() == file2 );
1240	}
1241	// take a file with resources and write them to an ELF file
1242	NextSubTest();
1243	execCommand(string("cp ") + elfNoResFile + " " + testFile1);
1244	{
1245		BFile file(x86ResFile, B_READ_ONLY);
1246		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
1247		BResources resources;
1248		CPPUNIT_ASSERT( resources.SetTo(&file, false) == B_OK );
1249		BFile file2(testFile1, B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
1250		CPPUNIT_ASSERT( file2.InitCheck() == B_OK );
1251		CPPUNIT_ASSERT( resources.WriteTo(&file2) == B_OK);
1252		CPPUNIT_ASSERT( resources.File() == file2 );
1253	}
1254	// empty file, add a resource, write it to another file
1255	NextSubTest();
1256	{
1257		BFile file(testFile2, B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
1258		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
1259		BResources resources;
1260		CPPUNIT_ASSERT( resources.SetTo(&file, false) == B_OK );
1261		// add a resource
1262		ResourceSet resourceSet;
1263		AddResource(resources, resourceSet, testResource1);
1264		// write to another file
1265		BFile file2(testFile1, B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
1266		CPPUNIT_ASSERT( file2.InitCheck() == B_OK );
1267		CPPUNIT_ASSERT( resources.WriteTo(&file2) == B_OK);
1268		CPPUNIT_ASSERT( resources.File() == file2 );
1269		// check, whether the first file has been modified -- it shouldn't be
1270		off_t fileSize = 0;
1271		CPPUNIT_ASSERT( file.GetSize(&fileSize) == B_OK );
1272		CPPUNIT_ASSERT( fileSize == 0 );
1273	}
1274	// uninitialized BResources
1275	NextSubTest();
1276	execCommand(string("cp ") + elfNoResFile + " " + testFile1);
1277	{
1278		BResources resources;
1279		BFile file2(testFile1, B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
1280		CPPUNIT_ASSERT( file2.InitCheck() == B_OK );
1281		CPPUNIT_ASSERT( resources.WriteTo(&file2) == B_OK);
1282		CPPUNIT_ASSERT( resources.File() == file2 );
1283	}
1284	// bad args: NULL file
1285	// R5: crashs
1286	NextSubTest();
1287	{
1288		BFile file(testFile2, B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
1289		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
1290		BResources resources;
1291		CPPUNIT_ASSERT( resources.SetTo(&file, false) == B_OK );
1292#if !TEST_R5
1293		CPPUNIT_ASSERT( resources.WriteTo(NULL) == B_BAD_VALUE);
1294#endif
1295	}
1296}
1297
1298// AddRemoveTest
1299void
1300ResourcesTest::AddRemoveTest()
1301{
1302	// tests:
1303	// * AddResource()
1304	// * RemoveResource()
1305	// * WriteResource()
1306
1307	// Start with an empty file, add all the resources of our x86 resource
1308	// file and compare the two bytewise.
1309	// This does of course only work on x86 machines.
1310	NextSubTest();
1311	{
1312		BFile file(testFile1, B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
1313		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
1314		BResources resources;
1315		CPPUNIT_ASSERT( resources.SetTo(&file, true) == B_OK );
1316		const ResourceInfo *resourceInfos[] = {
1317			&testResource1, &testResource2, &testResource3
1318		};
1319		int32 resourceCount = sizeof(resourceInfos) / sizeof(ResourceInfo*);
1320		AddResources(resources, resourceInfos, resourceCount);
1321	}
1322	{
1323		BFile file1(testFile1, B_READ_ONLY);
1324		BFile file2(x86ResFile, B_READ_ONLY);
1325		CompareFiles(file1, file2);
1326	}
1327	// Now remove all resources and compare the file with an empty resource
1328	// file.
1329	NextSubTest();
1330	{
1331		BFile file(testFile1, B_READ_WRITE);
1332		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
1333		BResources resources;
1334		CPPUNIT_ASSERT( resources.SetTo(&file, false) == B_OK );
1335		const ResourceInfo *resourceInfos[] = {
1336			&testResource1, &testResource2, &testResource3
1337		};
1338		int32 resourceCount = sizeof(resourceInfos) / sizeof(ResourceInfo*);
1339		for (int32 i = 0; i < resourceCount; i++) {
1340			const ResourceInfo &info = *resourceInfos[i];
1341			CPPUNIT_ASSERT( resources.RemoveResource(info.type, info.id)
1342							== B_OK );
1343		}
1344		BFile file2(testFile2, B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
1345		BResources resources2;
1346		CPPUNIT_ASSERT( resources2.SetTo(&file2, true) == B_OK );
1347	}
1348	{
1349		BFile file1(testFile1, B_READ_ONLY);
1350		BFile file2(testFile2, B_READ_ONLY);
1351		CompareFiles(file1, file2);
1352	}
1353	// Same file, use the other remove version.
1354	NextSubTest();
1355	execCommand(string("cp ") + x86ResFile + " " + testFile1);
1356	{
1357		BFile file(testFile1, B_READ_WRITE);
1358		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
1359		BResources resources;
1360		CPPUNIT_ASSERT( resources.SetTo(&file, false) == B_OK );
1361		const ResourceInfo *resourceInfos[] = {
1362			&testResource1, &testResource2, &testResource3
1363		};
1364		int32 resourceCount = sizeof(resourceInfos) / sizeof(ResourceInfo*);
1365		for (int32 i = 0; i < resourceCount; i++) {
1366			const ResourceInfo &info = *resourceInfos[i];
1367			size_t size;
1368			const void *data = resources.LoadResource(info.type, info.id,
1369													  &size);
1370			CPPUNIT_ASSERT( data != NULL );
1371			CPPUNIT_ASSERT( resources.RemoveResource(data) == B_OK );
1372		}
1373	}
1374	{
1375		BFile file1(testFile1, B_READ_ONLY);
1376		BFile file2(testFile2, B_READ_ONLY);
1377		CompareFiles(file1, file2);
1378	}
1379	// some arbitrary adding and removing...
1380	NextSubTest();
1381	{
1382		BFile file(testFile1, B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
1383		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
1384		BResources resources;
1385		CPPUNIT_ASSERT( resources.SetTo(&file, true) == B_OK );
1386		ResourceSet resourceSet;
1387		AddResource(resources, resourceSet, testResource1);
1388		AddResource(resources, resourceSet, testResource2);
1389		RemoveResource(resources, resourceSet, testResource1, true);
1390		AddResource(resources, resourceSet, testResource3);
1391		AddResource(resources, resourceSet, testResource4);
1392		RemoveResource(resources, resourceSet, testResource3, false);
1393		AddResource(resources, resourceSet, testResource5);
1394		AddResource(resources, resourceSet, testResource1);
1395		AddResource(resources, resourceSet, testResource6);		// replaces 1
1396		RemoveResource(resources, resourceSet, testResource2, true);
1397		RemoveResource(resources, resourceSet, testResource5, false);
1398		RemoveResource(resources, resourceSet, testResource6, true);
1399		RemoveResource(resources, resourceSet, testResource4, false);
1400	}
1401	// bad args
1402	NextSubTest();
1403	{
1404		BFile file(testFile1, B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
1405		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
1406		BResources resources;
1407		CPPUNIT_ASSERT( resources.SetTo(&file, true) == B_OK );
1408		CPPUNIT_ASSERT( resources.RemoveResource(NULL) == B_BAD_VALUE );
1409		CPPUNIT_ASSERT( resources.RemoveResource(B_INT32_TYPE, 0)
1410						== B_BAD_VALUE );
1411		CPPUNIT_ASSERT( resources.AddResource(B_INT32_TYPE, 0, NULL, 7,
1412											  "Hey!") == B_BAD_VALUE );
1413	}
1414	// uninitialized BResources
1415	NextSubTest();
1416	{
1417		BResources resources;
1418		const ResourceInfo &info = testResource1;
1419		CPPUNIT_ASSERT( resources.AddResource(info.type, info.id, info.data,
1420											  info.size, info.name)
1421						== B_OK );
1422	}
1423}
1424
1425// ReadWriteTest
1426void
1427ResourcesTest::ReadWriteTest()
1428{
1429	// ReadResource() has already been tested in ReadTest(). Thus we focus on
1430	// WriteResource().
1431	// Open a file and write a little bit into one of its resources.
1432	NextSubTest();
1433	execCommand(string("cp ") + x86ResFile + " " + testFile1);
1434	{
1435		BFile file(testFile1, B_READ_WRITE);
1436		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
1437		BResources resources;
1438		CPPUNIT_ASSERT( resources.SetTo(&file, false) == B_OK );
1439		const ResourceInfo &info = testResource1;
1440		type_code type = info.type;
1441		int32 id = info.id;
1442		// write at the beginning of the resource
1443		char writeBuffer[1024];
1444		for (int32 i = 0; i < 1024; i++)
1445			writeBuffer[i] = i & 0xff;
1446		char readBuffer[1024];
1447		memset(readBuffer, 0, 1024);
1448		CPPUNIT_ASSERT( resources.WriteResource(type, id, writeBuffer, 0, 10)
1449						== B_OK );
1450		size_t length;
1451		const void *data = resources.LoadResource(type, id, &length);
1452		CPPUNIT_ASSERT( data != NULL && length == info.size );
1453		CPPUNIT_ASSERT( !memcmp(data, writeBuffer, 10) );
1454		CPPUNIT_ASSERT( !memcmp((const char*)data + 10, info.data + 10,
1455								info.size - 10) );
1456		// completely overwrite the resource with the former data
1457		CPPUNIT_ASSERT( resources.WriteResource(type, id, info.data, 0,
1458												info.size) == B_OK );
1459		data = resources.LoadResource(type, id, &length);
1460		CPPUNIT_ASSERT( data != NULL && length == info.size );
1461		CPPUNIT_ASSERT( !memcmp(data, info.data, info.size) );
1462		// write something in the middle
1463		size_t offset = (info.size - 2) / 2;
1464		CPPUNIT_ASSERT( resources.WriteResource(type, id, writeBuffer, offset,
1465												2) == B_OK );
1466		data = resources.LoadResource(type, id, &length);
1467		CPPUNIT_ASSERT( data != NULL && length == info.size );
1468		CPPUNIT_ASSERT( !memcmp(data, info.data, offset) );
1469		CPPUNIT_ASSERT( !memcmp((const char*)data + offset, writeBuffer, 2) );
1470		CPPUNIT_ASSERT( !memcmp((const char*)data + offset + 2,
1471								info.data + offset + 2,
1472								info.size - offset - 2) );
1473		// write starting inside the resource, but extending it
1474		size_t writeSize = info.size + 3;
1475		CPPUNIT_ASSERT( resources.WriteResource(type, id, writeBuffer, offset,
1476												writeSize) == B_OK );
1477		data = resources.LoadResource(type, id, &length);
1478		CPPUNIT_ASSERT( data != NULL && length == (size_t)offset + writeSize );
1479		CPPUNIT_ASSERT( !memcmp(data, info.data, offset) );
1480		CPPUNIT_ASSERT( !memcmp((const char*)data + offset, writeBuffer,
1481								writeSize) );
1482		// write past the end of the resource
1483		size_t newOffset = length + 30;
1484		size_t newWriteSize = 17;
1485		CPPUNIT_ASSERT( resources.WriteResource(type, id, writeBuffer,
1486												newOffset, newWriteSize)
1487						== B_OK );
1488		data = resources.LoadResource(type, id, &length);
1489		CPPUNIT_ASSERT( data != NULL && length == newOffset + newWriteSize );
1490		CPPUNIT_ASSERT( !memcmp(data, info.data, offset) );
1491		CPPUNIT_ASSERT( !memcmp((const char*)data + offset, writeBuffer,
1492								writeSize) );
1493		// [offset + writeSize, newOffset) unspecified
1494		CPPUNIT_ASSERT( !memcmp((const char*)data + newOffset, writeBuffer,
1495								newWriteSize) );
1496	}
1497	// error cases
1498	// bad args
1499	NextSubTest();
1500	{
1501		BFile file(testFile1, B_READ_WRITE);
1502		CPPUNIT_ASSERT( file.InitCheck() == B_OK );
1503		BResources resources;
1504		CPPUNIT_ASSERT( resources.SetTo(&file, false) == B_OK );
1505		const ResourceInfo &info = testResource1;
1506		type_code type = info.type;
1507		int32 id = info.id;
1508		// NULL buffer
1509		CPPUNIT_ASSERT( resources.WriteResource(type, id, NULL, 0, 10)
1510						== B_BAD_VALUE );
1511		// non existing resource
1512		char writeBuffer[1024];
1513		CPPUNIT_ASSERT( resources.WriteResource(B_MESSENGER_TYPE, 0,
1514												writeBuffer, 0, 10)
1515						== B_BAD_VALUE );
1516	}
1517	// uninitialized BResources
1518	NextSubTest();
1519	{
1520		BResources resources;
1521		const ResourceInfo &info = testResource1;
1522		type_code type = info.type;
1523		int32 id = info.id;
1524		char writeBuffer[1024];
1525		CPPUNIT_ASSERT( resources.WriteResource(type, id, writeBuffer, 0, 10)
1526						== B_BAD_VALUE );
1527	}
1528}
1529
1530
1531
1532
1533
1534