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