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