xref: /haiku/src/tests/kits/storage/ResourcesTest.cpp (revision 77aa0e2a5ef8e6d00570dbeb7a12aeb23e9e5cf0)
1 // ResourcesTest.cpp
2 
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <unistd.h>
6 
7 #include <string>
8 using std::string;
9 #include <vector>
10 using 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 
22 static const char *testDir		= "/tmp/testDir";
23 static const char *x86ResFile	= "/tmp/testDir/x86.rsrc";
24 static const char *ppcResFile	= "/tmp/testDir/ppc.rsrc";
25 static const char *elfFile		= "/tmp/testDir/elf";
26 static const char *elfNoResFile	= "/tmp/testDir/elf-no-res";
27 static const char *pefFile		= "/tmp/testDir/pef";
28 static const char *pefNoResFile	= "/tmp/testDir/pef-no-res";
29 static const char *emptyFile	= "/tmp/testDir/empty-file";
30 static const char *noResFile	= "/tmp/testDir/no-res-file";
31 static const char *testFile1	= "/tmp/testDir/testFile1";
32 static const char *testFile2	= "/tmp/testDir/testFile2";
33 static const char *noSuchFile	= "/tmp/testDir/no-such-file";
34 static const char *x86ResName	= "x86.rsrc";
35 static const char *ppcResName	= "ppc.rsrc";
36 static const char *elfName		= "elf";
37 static const char *elfNoResName	= "elf-no-res";
38 static const char *pefName		= "pef";
39 static const char *pefNoResName	= "pef-no-res";
40 
41 static const int32 kEmptyResourceFileSize	= 1984;
42 
43 struct 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 
76 static const char *testResData1 = "I like strings, especially cellos.";
77 static const int32 testResSize1 = strlen(testResData1) + 1;
78 static const int32 testResData2 = 42;
79 static const int32 testResSize2 = sizeof(int32);
80 static const char *testResData3 = "application/bread-roll-counter";
81 static const int32 testResSize3 = strlen(testResData3) + 1;
82 static const char *testResData4 = "This is a long string. At least longer "
83 								  "than the first one";
84 static const int32 testResSize4 = strlen(testResData1) + 1;
85 static const char *testResData6 = "Short, but true.";
86 static const int32 testResSize6 = strlen(testResData6) + 1;
87 
88 static const ResourceInfo testResource1(B_STRING_TYPE, 74, testResData1,
89 										testResSize1, "a string resource");
90 static const ResourceInfo testResource2(B_INT32_TYPE, 17, &testResData2,
91 										testResSize2, "just a resource");
92 static const ResourceInfo testResource3(B_MIME_STRING_TYPE, 29, testResData3,
93 										testResSize3, "another resource");
94 static const ResourceInfo testResource4(B_STRING_TYPE, 75, &testResData4,
95 										testResSize4,
96 										"a second string resource");
97 static const ResourceInfo testResource5(B_MIME_STRING_TYPE, 74, &testResData1,
98 										testResSize1, "a string resource");
99 static const ResourceInfo testResource6(B_STRING_TYPE, 74, &testResData6,
100 										testResSize6,
101 										"a third string resource");
102 
103 // helper class: maintains a ResourceInfo set
104 struct 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
173 CppUnit::Test*
174 ResourcesTest::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
197 void
198 ResourcesTest::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
219 void
220 ResourcesTest::tearDown()
221 {
222 	execCommand(string("rm -rf ") + testDir);
223 	BasicTest::tearDown();
224 }
225 
226 // InitTest
227 void
228 ResourcesTest::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
531 static
532 void
533 ReadResTest(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
654 static
655 void
656 ReadBadResTest(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
700 void
701 ResourcesTest::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(0L, (type_code*)NULL, NULL,
947 												  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
973 static
974 void
975 AddResources(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
986 static
987 void
988 CompareFiles(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
1006 static
1007 void
1008 CompareResources(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
1039 static
1040 void
1041 AddResource(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
1051 static
1052 void
1053 RemoveResource(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
1070 void
1071 ListResources(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
1088 void
1089 ResourcesTest::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
1139 void
1140 ResourcesTest::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
1217 void
1218 ResourcesTest::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
1303 void
1304 ResourcesTest::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
1430 void
1431 ResourcesTest::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