xref: /haiku/src/tests/kits/storage/MimeTypeTest.cpp (revision 425ac1b60a56f4df7a0e88bd784545c0ec4fa01f)
1 // MimeTypeTest.cpp
2 
3 #include <ctype.h>			// For tolower()
4 #include <fcntl.h>			// open()
5 #include <map>
6 #include <queue>
7 #include <stdio.h>
8 #include <string.h>			// For memcmp()
9 #include <string>
10 #include <unistd.h>
11 #include <vector>
12 
13 
14 #include <fs_attr.h>		// For struct attr_info
15 #include <fs_info.h>
16 #include <Application.h>
17 #include <Bitmap.h>
18 #include <DataIO.h>
19 #include <Drivers.h>		// B_GET_ICON, device_icon
20 #include <Message.h>
21 #include <Mime.h>
22 #if !TEST_R5
23 	#include <mime/database_support.h>
24 #endif
25 #include <Path.h>			// Only needed for entry_ref dumps
26 #include <StorageKit.h>
27 #include <String.h>
28 #include <storage_support.h>	// for split_path()
29 
30 #include "TestShell.h"
31 #include "TestApp.h"
32 #include "TestUtils.h"
33 
34 #include "MimeTypeTest.h"
35 
36 // MIME database directories
37 static const char *testDir				= "/tmp/mimeTestDir";
38 static const char *R5DatabaseDir		= "/boot/home/config/settings/beos_mime";
39 #if TEST_R5
40 static std::string mimeDatabaseDir		= R5DatabaseDir;
41 #else
42 static std::string mimeDatabaseDir		= BPrivate::Storage::Mime::kDatabaseDir;
43 #endif
44 
45 // MIME Test Types
46 // testType and testTypeApp are Delete()d after each test.
47 static const char *testType				= "text/x-vnd.obos-Storage-Kit-Test";
48 static const char *testType1			= "text/x-vnd.obos-Storage-Kit-Test1";
49 static const char *testType2			= "text/x-vnd.obos-Storage-Kit-Test2";
50 static const char *testType3			= "text/x-vnd.obos-Storage-Kit-Test3";
51 static const char *testType4			= "text/x-vnd.obos-Storage-Kit-Test4";
52 static const char *testType5			= "text/x-vnd.obos-Storage-Kit-Test5";
53 static const char *testTypeApp			= "application/StorageKit-Test";
54 static const char *testTypeApp1			=  "application/"
55 										   "x-vnd.obos.mime.test.test1";
56 static const char *testTypeApp2			=  "application/"
57 										   "x-vnd.obos.mime.test.test2";
58 static const char *testTypeApp3			=  "application/"
59 										   "x-vnd.obos.mime.test.test3";
60 static const char *testTypeInvalid		= "text/Are spaces valid?";
61 static const char *testTypeSuperValid	= "valid-but-fake-supertype";
62 static const char *testTypeSuperInvalid	= "?????";
63 
64 // Real MIME types
65 static const char *wildcardType			= "application/octet-stream";
66 static const char *applicationSupertype	= "application";
67 
68 // Application Paths
69 static const char *testApp				= "/boot/beos/apps/SoundRecorder";
70 static const char *testApp2				= "/boot/beos/apps/CDPlayer";
71 static const char *fakeTestApp			= "/__this_isn't_likely_to_exist__";
72 
73 // BMessage field names
74 static const char *applicationsField		= "applications";
75 static const char *typeField				= "type";
76 static const char *typesField				= "types";
77 static const char *fileExtField				= "extensions";
78 static const char *attrInfoField_Name		= "attr:name";
79 static const char *attrInfoField_PublicName	= "attr:public_name";
80 static const char *attrInfoField_Type		= "attr:type";
81 static const char *attrInfoField_Viewable	= "attr:viewable";
82 static const char *attrInfoField_Editable	= "attr:editable";
83 
84 // Descriptions
85 static const char *testDescr			= "Just a test, nothing more :-)";
86 static const char *testDescr2			= "Another amazing test string";
87 static const char *longDescr			=
88 "This description is longer than B_MIME_TYPE_LENGTH, which is quite useful for certain things... "
89 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
90 "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
91 "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
92 "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd";
93 // Signatures
94 static const char *testSig				= "application/x-vnd.obos.mime-type-test";
95 static const char *testSig2				= "application/x-vnd.obos.mime-type-test-2";
96 static const char *longSig				= "application/x-vnd.obos.mime-type-test-long."
97 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
98 "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
99 "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
100 "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd";
101 
102 // Declarations for handy dandy private functions
103 static bool operator==(BBitmap &bmp1, BBitmap &bmp2);
104 static bool operator!=(BBitmap &bmp1, BBitmap &bmp2);
105 static bool operator==(BMessage &msg1, BMessage &msg2);
106 static bool operator!=(BMessage &msg1, BMessage &msg2);
107 static void fill_bitmap(BBitmap &bmp, char value);
108 //static void dump_bitmap(BBitmap &bmp, char *name = "bmp");
109 #if !TEST_R5
110 	static status_t reduce_color_depth(BBitmap &src32, BBitmap &dest8);
111 #endif
112 //static void dump_ref(entry_ref *ref, char* name = "ref");
113 static void to_lower(const char *str, std::string &result);
114 static std::string to_lower(const char *str);
115 static void remove_type(const char *type, const char *databaseDir = mimeDatabaseDir.c_str());
116 static bool type_exists(const char *type, const char *databaseDir = mimeDatabaseDir.c_str());
117 class ContainerAdapter;
118 class SetAdapter;
119 class QueueAdapter;
120 void FillWithMimeTypes(ContainerAdapter &container, BMessage &typeMessage, const char* fieldName);
121 	// Used to add all the types in a BMessage from GetInstalled*Types() to some
122 	// sort of container object (via an adapter to support differing interfaces).
123 
124 // Custom TestSuite class to allow us to make a copy of the MIME database directory
125 // before running all the BMimeType tests
126 class MimeTypeTestSuite : public CppUnit::TestSuite {
127 public:
MimeTypeTestSuite()128 	MimeTypeTestSuite() : CppUnit::TestSuite(), fMimeDirExisted(false) {}
setUp()129 	virtual void setUp()
130 	{
131 		// If we're using a directory other than the R5 MIME database directory, make
132 		// sure that directory exists. If not, make a complete copy of the R5 database
133 		// directory.
134 		if (mimeDatabaseDir != R5DatabaseDir) {
135 			BEntry dir(mimeDatabaseDir.c_str());
136 			if (dir.InitCheck() != B_OK || !dir.Exists()) {
137 				if (BTestShell::GlobalBeVerbose())
138 					cout << "(Making a copy of your MIME database at '" + mimeDatabaseDir + "')" << endl;
139 				std::string cmd = std::string("copyattr -d -r -- ") + R5DatabaseDir
140 					+ " " + mimeDatabaseDir;
141 				ExecCommand(cmd.c_str());
142 			} else {
143 				fMimeDirExisted = true;
144 				if (BTestShell::GlobalBeVerbose())
145 					cout << "(Using existing copy of MIME database in '" + mimeDatabaseDir + "')" << endl;
146 			}
147 		}
148 	}
149 
tearDown()150 	virtual void tearDown()
151 	{
152 		if (mimeDatabaseDir != R5DatabaseDir && !fMimeDirExisted) {
153 			if (BTestShell::GlobalBeVerbose())
154 				cout << "(Removing copy of MIME database in '" + mimeDatabaseDir + "')" << endl;
155 			std::string cmd = std::string("rm -rf ") + mimeDatabaseDir;
156 			ExecCommand(cmd.c_str());
157 		}
158 	}
159 
run(CppUnit::TestResult * result)160 	virtual void run( CppUnit::TestResult *result )
161 	{
162 		setUp();
163 		CppUnit::TestSuite::run(result);
164 		tearDown();
165 	}
166 private:
167 	bool fMimeDirExisted;
168 };
169 
170 // Suite
171 CppUnit::Test*
Suite()172 MimeTypeTest::Suite() {
173 	MimeTypeTestSuite *suite = new MimeTypeTestSuite();
174 	typedef CppUnit::TestCaller<MimeTypeTest> TC;
175 
176 	// Tyler
177 	suite->addTest( new TC("BMimeType::Install/Delete Test",
178 						   &MimeTypeTest::InstallDeleteTest) );
179 	suite->addTest( new TC("BMimeType::App Hint Test",
180 						   &MimeTypeTest::AppHintTest) );
181 	suite->addTest( new TC("BMimeType::Attribute Info Test",
182 						   &MimeTypeTest::AttrInfoTest) );
183 	suite->addTest( new TC("BMimeType::Long Description Test",
184 						   &MimeTypeTest::LongDescriptionTest) );
185 	suite->addTest( new TC("BMimeType::Short Description Test",
186 						   &MimeTypeTest::ShortDescriptionTest) );
187 	suite->addTest( new TC("BMimeType::File Extensions Test",
188 						   &MimeTypeTest::FileExtensionsTest) );
189 	suite->addTest( new TC("BMimeType::Icon Test (Large)",
190 						   &MimeTypeTest::LargeIconTest) );
191 	suite->addTest( new TC("BMimeType::Icon Test (Mini)",
192 						   &MimeTypeTest::MiniIconTest) );
193 	suite->addTest( new TC("BMimeType::Icon For Type Test (Large)",
194 						   &MimeTypeTest::LargeIconForTypeTest) );
195 	suite->addTest( new TC("BMimeType::Icon For Type Test (Mini)",
196 						   &MimeTypeTest::MiniIconForTypeTest) );
197 	suite->addTest( new TC("BMimeType::Installed Types Test",
198 						   &MimeTypeTest::InstalledTypesTest) );
199 	suite->addTest( new TC("BMimeType::Preferred App Test",
200 						   &MimeTypeTest::PreferredAppTest) );
201 	suite->addTest( new TC("BMimeType::Supporting Apps Test",
202 						   &MimeTypeTest::SupportingAppsTest) );
203 	suite->addTest( new TC("BMimeType::Supported Types Test",
204 						   &MimeTypeTest::SupportedTypesTest) );
205 	suite->addTest( new TC("BMimeType::Wildcard Apps Test",
206 						   &MimeTypeTest::WildcardAppsTest) );
207 
208 	// Ingo
209 	suite->addTest( new TC("BMimeType::Initialization Test",
210 						   &MimeTypeTest::InitTest) );
211 	suite->addTest( new TC("BMimeType::MIME String Test",
212 						   &MimeTypeTest::StringTest) );
213 	suite->addTest( new TC("BMimeType::MIME Monitoring Test",
214 						   &MimeTypeTest::MonitoringTest) );
215 	suite->addTest( new TC("BMimeType::update_mime_info() Test",
216 						   &MimeTypeTest::UpdateMimeInfoTest) );
217 	suite->addTest( new TC("BMimeType::create_app_meta_mime() Test",
218 						   &MimeTypeTest::CreateAppMetaMimeTest) );
219 	suite->addTest( new TC("BMimeType::get_device_icon() Test",
220 						   &MimeTypeTest::GetDeviceIconTest) );
221 	suite->addTest( new TC("BMimeType::Sniffer Rule Test",
222 						   &MimeTypeTest::SnifferRuleTest) );
223 	suite->addTest( new TC("BMimeType::Sniffing Test",
224 						   &MimeTypeTest::SniffingTest) );
225 
226 
227 	return suite;
228 }
229 
230 // Handy comparison operators for BBitmaps. The size and color depth
231 // are compared first, followed by the bitmap data.
232 bool
operator ==(BBitmap & bmp1,BBitmap & bmp2)233 operator==(BBitmap &bmp1, BBitmap &bmp2) {
234 	if (bmp1.Bounds() == bmp2.Bounds()) {
235 //		printf("bmp== 1\n");
236 		if (bmp1.ColorSpace() == bmp2.ColorSpace()) {
237 //			printf("bmp== 2\n");
238 			char *data1 = (char*)bmp1.Bits();
239 			char *data2 = (char*)bmp2.Bits();
240 			// NOTE! It's possible that padding bits might not get copied verbatim,
241 			// which could lead to unexpected failures. If things are acting weird,
242 			// you might try the commented out code below (keeping in mind that it
243 			// currently only works for 8-bit color depths).
244 			for (int i = 0; i < bmp1.BitsLength(); data1++, data2++, i++) {
245 				if (*data1 != *data2) {
246 //					printf("i == %d\n", i);
247 					return false;
248 				}
249 			}
250 /*			for (int i = 0; i < bmp1.Bounds().IntegerHeight(); i++) {
251 				for (int j = 0; j < bmp1.Bounds().IntegerWidth(); j++) {
252 //					printf("(%d, %d)", data1[(i * bmp1.BytesPerRow()) + j], data2[(i * bmp2.BytesPerRow()) + j]);
253 					if (data1[(i * bmp1.BytesPerRow()) + j] != data2[(i * bmp2.BytesPerRow()) + j]) {
254 						printf("fail at (%d, %d)\n", j, i);
255 						return false;
256 					}
257 				}
258 			}*/
259 			return true;
260 		} else
261 			return false;
262 	} else
263 		return false;
264 }
265 
266 bool
operator !=(BBitmap & bmp1,BBitmap & bmp2)267 operator!=(BBitmap &bmp1, BBitmap &bmp2) {
268 	return !(bmp1 == bmp2);
269 }
270 
271 // Handy comparison operators for BMessages. The BMessages are checked field
272 // by field, each of which is verified to be identical with respect to: type,
273 // count, and data (all items).
274 bool
operator ==(BMessage & msg1,BMessage & msg2)275 operator==(BMessage &msg1, BMessage &msg2) {
276 	status_t err = B_OK;
277 
278 	// For now I'm ignoring the what fields...I shall deal with that later :-)
279 	if (msg1.what != msg2.what)
280 		return false;
281 
282 /*
283 	printf("----------------------------------------------------------------------\n");
284 	msg1.PrintToStream();
285 	msg2.PrintToStream();
286 	printf("----------------------------------------------------------------------\n");
287 */
288 
289 	// Check the counts of field names
290 	int count1, count2;
291 	count1 = msg1.CountNames(B_ANY_TYPE);
292 	count2 = msg2.CountNames(B_ANY_TYPE);
293 	if (count1 != count2 && (count1 == 0 || count2 == 0))
294 		return false;
295 
296 	// Iterate over all the names in msg1 and check that the field
297 	// with the same name exists in msg2, is of the same type, and
298 	// contains identical data.
299 	for (int i = 0; i < count1 && !err; i++) {
300 		char *name;
301 		type_code typeFound1, typeFound2;
302 		int32 countFound1, countFound2;
303 
304 		// Check type and count info
305 		err = msg1.GetInfo(B_ANY_TYPE, i, &name, &typeFound1, &countFound1);
306 		if (!err)
307 			err = msg2.GetInfo(name, &typeFound2, &countFound2);
308 		if (!err)
309 			err = (typeFound1 == typeFound2 && countFound1 == countFound2 ? B_OK : B_ERROR);
310 		if (!err) {
311 			// Check all the data items
312 			for (int j = 0; j < countFound1; j++) {
313 				void *data1, *data2;
314 				ssize_t bytes1, bytes2;
315 
316 				err = msg1.FindData(name, typeFound1, j, (const void**)&data1, &bytes1);
317 				if (!err)
318 					err = msg2.FindData(name, typeFound2, j, (const void**)&data2, &bytes2);
319 				if (!err)
320 					err = (bytes1 == bytes2 && memcmp(data1, data2, bytes1) == 0 ? B_OK : B_ERROR);
321 			}
322 		}
323 	}
324 
325 	return !err;
326 }
327 
328 bool
operator !=(BMessage & msg1,BMessage & msg2)329 operator!=(BMessage &msg1, BMessage &msg2) {
330 	return !(msg1 == msg2);
331 }
332 
333 // Fills the bitmap data with the given character
334 void
fill_bitmap(BBitmap & bmp,char value)335 fill_bitmap(BBitmap &bmp, char value) {
336 	char *data = (char*)bmp.Bits();
337 	for (int i = 0; i < bmp.BitsLength(); data++, i++) {
338 //		printf("(%d -> ", *data);
339 		*data = value;
340 //		printf("%d)", *data);
341 	}
342 //	printf("\n");
343 }
344 
345 // Fills the bitmap data with the given character
346 void
fill_bitmap32(BBitmap & bmp,char r,char g,char b,char a)347 fill_bitmap32(BBitmap &bmp, char r, char g, char b, char a) {
348 	if (bmp.ColorSpace() == B_RGB32 || bmp.ColorSpace() == B_RGBA32) {
349 		char *data = (char*)bmp.Bits();
350 		for (int i = 0; i+3 < bmp.BitsLength(); data += 4, i+= 4) {
351 			data[0] = b;
352 			data[1] = g;
353 			data[2] = r;
354 			data[3] = a;
355 //			printf("(%d,%d,%d,%d)", data[2], data[1], data[0], data[3]);
356 		}
357 //		printf("\n");
358 	}
359 }
360 
361 // Dumps the size, colorspace, and first data byte
362 // of the bitmap to stdout
363 /*void
364 dump_bitmap(BBitmap &bmp, char *name = "bmp") {
365 	printf("%s == (%ldx%ld, ", name, bmp.Bounds().IntegerWidth()+1,
366 		bmp.Bounds().IntegerHeight()+1);
367 	switch (bmp.ColorSpace()) {
368 		case B_CMAP8:
369 			printf("B_CMAP8");
370 			break;
371 
372 		case B_RGB32:
373 			printf("B_RGB32");
374 			break;
375 
376 		case B_RGBA32:
377 			printf("B_RGBA32");
378 			break;
379 
380 		default:
381 			printf("%x", bmp.ColorSpace());
382 			break;
383 	}
384 	printf(", %d, [", *(char*)bmp.Bits());
385 	char *data = (char*)bmp.Bits();
386 	for (int i = 0; i < bmp.BitsLength() && i < 20; data++, i++)
387 		printf("%d,", *data);
388 	printf("]\n");
389 }*/
390 
391 // Uses BBitmap::SetBits() to convert the B_RGB32 bitmap in src32
392 // to a B_CMAP8 bitmap in dest8
393 #if !TEST_R5
394 status_t
reduce_color_depth(BBitmap & src32,BBitmap & dest8)395 reduce_color_depth(BBitmap &src32, BBitmap &dest8)
396 {
397 	status_t err = (src32.ColorSpace() == B_RGB32
398 				      && dest8.ColorSpace() == B_CMAP8
399 				        && src32.InitCheck() == B_OK
400 				          && dest8.InitCheck() == B_OK
401 					        && src32.Bounds() == dest8.Bounds())
402 					          ? B_OK
403 					            : B_BAD_VALUE;
404 	if (!err) {
405 		// Set each pixel individually, since SetBits() for B_RGB32 takes
406 		// 24-bit rgb pixel data...
407 		char *data = (char*)src32.Bits();
408 		for (int32 i = 0; i*4+3 < src32.BitsLength(); data += 4, i++) {
409 			char rgb[3];
410 			rgb[0] = data[2];	// red
411 			rgb[1] = data[1];	// green
412 			rgb[2] = data[0];	// blue
413 			dest8.SetBits(rgb, 3, i, B_RGB32);
414 		}
415 	}
416 	return err;
417 }
418 #endif
419 
420 // IconHelper and IconForTypeHelper:
421 // Adapter(?) classes needed to reuse icon tests among {Get,Set}Icon() and {Get,Set}IconForType()
422 // What originally were meant to encapsulate the variations among calls to the various BMimeType
423 // icon functions have now exploded into beefy helper classes with a bunch of BBitmap related
424 // functionality as well. A lot of this stuff doesn't necessarily belong
425 
426 class IconHelper {
427 public:
IconHelper(icon_size which)428 	IconHelper(icon_size which)
429 		: bmp1(BitmapBounds(which), B_CMAP8),
430 		  bmp2(BitmapBounds(which), B_CMAP8),
431 		  bmpTemp(BitmapBounds(which), B_CMAP8),
432 		  size(which)
433 	{
434 		// Initialize our three bitmaps to different "colors"
435 		fill_bitmap(bmp1, 1);
436 		fill_bitmap(bmp2, 2);
437 		fill_bitmap(bmpTemp, 3);
438 	}
439 
~IconHelper()440 	virtual ~IconHelper() {}
441 
442 	// Returns the proper bitmap bounds for the given icon size
BitmapBounds(icon_size isize)443 	BRect BitmapBounds(icon_size isize) {
444 		return isize == B_LARGE_ICON ? BRect(0,0,31,31) : BRect(0,0,15,15);
445 	}
446 
447 	// Returns the proper bitmap bounds for this helper's icon size
BitmapBounds()448 	BRect BitmapBounds() {
449 		return BitmapBounds(size);
450 	}
451 
452 	// Used to call the appropriate GetIcon[ForType] function
GetIcon(BMimeType & mime,BBitmap * icon)453 	virtual status_t GetIcon(BMimeType &mime, BBitmap *icon) {
454 		return mime.GetIcon(icon, size);
455 	}
456 
457 	// Used to call the appropriate SetIcon[ForType] function
SetIcon(BMimeType & mime,BBitmap * icon)458 	virtual status_t SetIcon(BMimeType &mime, BBitmap *icon) {
459 		return mime.SetIcon(icon, size);
460 	}
461 
462 	// Used to call the appropriate DeleteIcon[ForType] function
DeleteIcon(BMimeType & mime)463 	virtual status_t DeleteIcon(BMimeType &mime) {
464 #if TEST_R5
465 		return B_BAD_VALUE;
466 #else
467 		return mime.DeleteIcon(size);
468 #endif
469 	}
470 
TempBitmap()471 	BBitmap* TempBitmap() {
472 		return &bmpTemp;
473 	}
474 
Bitmap1()475 	BBitmap* Bitmap1() {
476 		return &bmp1;
477 	}
478 
Bitmap2()479 	BBitmap* Bitmap2() {
480 		return &bmp2;
481 	}
482 
Size()483 	icon_size Size() {
484 		return size;
485 	}
486 
487 protected:
488 	BBitmap bmp1;
489 	BBitmap bmp2;
490 	BBitmap bmpTemp;
491 	icon_size size;
492 };
493 
494 class IconForTypeHelper : public IconHelper {
495 public:
IconForTypeHelper(const char * fileType,icon_size which)496 	IconForTypeHelper(const char *fileType, icon_size which)
497 		: IconHelper(which), fileType(fileType) {}
~IconForTypeHelper()498 	virtual ~IconForTypeHelper() {}
GetIcon(BMimeType & mime,BBitmap * icon)499 	virtual status_t GetIcon(BMimeType &mime, BBitmap *icon) {
500 		return mime.GetIconForType(fileType.c_str(), icon, size);
501 	}
SetIcon(BMimeType & mime,BBitmap * icon)502 	virtual status_t SetIcon(BMimeType &mime, BBitmap *icon) {
503 		return mime.SetIconForType(fileType.c_str(), icon, size);
504 	}
DeleteIcon(BMimeType & mime)505 	virtual status_t DeleteIcon(BMimeType &mime) {
506 #if TEST_R5
507 		return B_BAD_VALUE;
508 #else
509 		return mime.DeleteIconForType(fileType.c_str(), size);
510 #endif
511 	}
512 protected:
513 	std::string fileType;
514 };
515 
516 // Adapter classes used by FillWithMimeTypes() to facilitate
517 // addition of strings to containers with varying interfaces
518 class ContainerAdapter {
519 public:
520 	virtual void Add(std::string value) = 0;
521 };
522 
523 class SetAdapter : public ContainerAdapter {
524 public:
SetAdapter(std::set<std::string> & set)525 	SetAdapter(std::set<std::string> &set)
526 		: fSet(set) { }
Add(std::string value)527 	virtual void Add(std::string value) {
528 		fSet.insert(value);
529 	}
530 protected:
531 	std::set<std::string> &fSet;
532 };
533 
534 class QueueAdapter : public ContainerAdapter {
535 public:
QueueAdapter(std::queue<std::string> & queue)536 	QueueAdapter(std::queue<std::string> &queue)
537 		: fQueue(queue) { }
Add(std::string value)538 	virtual void Add(std::string value) {
539 		fQueue.push(value);
540 	}
541 protected:
542 	std::queue<std::string> &fQueue;
543 };
544 
545 
546 // setUp
547 void
setUp()548 MimeTypeTest::setUp()
549 {
550 	BasicTest::setUp();
551 	execCommand(string("mkdir ") + testDir);
552 /*	// Better not to play with fire, so we'll make a copy of the
553 	// local mime database which we'll use for certain Haiku tests
554 	execCommand(string("mkdir ") + testDir
555 				+ " ; copyattr -d -r -- " + mimeDatabaseDir + "/\* " + testDir
556 				); */
557 	// Setup our application
558 	fApplication = new BTestApp(testSig);
559 	if (fApplication->Init() != B_OK) {
560 		fprintf(stderr, "Failed to initialize application.\n");
561 		delete fApplication;
562 		fApplication = NULL;
563 	}
564 
565 }
566 
567 // tearDown
568 void
tearDown()569 MimeTypeTest::tearDown()
570 {
571 	execCommand(string("rm -rf ") + testDir);
572 
573 	// Uninistall our test type
574 	//! /todo Uncomment the following uninstall code when all is said and done.
575 /*	BMimeType mime(testType);
576 	status_t err = mime.InitCheck();
577 	if (!err && mime.IsInstalled())
578 		err = mime.Delete();
579 	if (err)
580 		fprintf(stderr, "Failed to unistall test type \"%s\"\n", testType); */
581 	// Terminate the Application
582 	if (fApplication) {
583 		fApplication->Terminate();
584 		delete fApplication;
585 		fApplication = NULL;
586 	}
587 	// remove the types we've added
588 	const char * const testTypes[] = {
589 		testType, testType1, testType2, testType3, testType4, testType5,
590 		testTypeApp, testTypeApp1, testTypeApp2, testTypeApp3,
591 	};
592 	for (uint32 i = 0; i < sizeof(testTypes) / sizeof(const char*); i++) {
593 		BMimeType type(testTypes[i]);
594 		type.Delete();
595 	}
596 	BasicTest::tearDown();
597 }
598 
599 // entry_ref dumping function ; this may be removed at any time
600 
601 /*void
602 dump_ref(entry_ref *ref, char* name = "ref") {
603 	if (ref) {
604 		BPath path(ref);
605 		status_t err = path.InitCheck();
606 		if (!err) {
607 			printf("%s == '%s'", name, path.Path());
608 		} else
609 			printf("%s == ERROR", name);
610 		printf(" == (%ld, %lld, '%s')\n", ref->device, ref->directory, ref->name);
611 
612 	} else
613 		printf("%s == (NULL)\n", name);
614 }*/
615 
616 // App Hint
617 
618 void
AppHintTest()619 MimeTypeTest::AppHintTest() {
620 	// init a couple of entry_refs to applications
621 	BEntry entry(testApp);
622 	entry_ref appRef;
623 	CHK(entry.InitCheck() == B_OK);
624 	CHK(entry.GetRef(&appRef) == B_OK);
625 	BEntry entry2(testApp2);
626 	entry_ref appRef2;
627 	CHK(entry2.InitCheck() == B_OK);
628 	CHK(entry2.GetRef(&appRef2) == B_OK);
629 	// Uninitialized
630 	NextSubTest();
631 	{
632 		BMimeType mime;
633 		entry_ref ref;
634 		CHK(mime.InitCheck() == B_NO_INIT);
635 		CHK(mime.GetAppHint(&ref) != B_OK);		// R5 == B_BAD_VALUE
636 		CHK(mime.SetAppHint(&ref) != B_OK);		// R5 == B_BAD_VALUE
637 	}
638 	// NULL params
639 	NextSubTest();
640 	{
641 		entry_ref ref;
642 		BMimeType mime(testType);
643 		CHK(mime.InitCheck() == B_OK);
644 		// Make sure the type isn't installed
645 		if (mime.IsInstalled())
646 			CHK(mime.Delete() == B_OK);
647 #if TEST_R5
648 		CHK(!mime.IsInstalled());
649 		CHK(mime.GetAppHint(NULL) != B_OK);		// R5 == B_BAD_VALUE
650 		CHK(!mime.IsInstalled());
651 		CHK(mime.SetAppHint(NULL) != B_OK);		// Installs, R5 == B_ENTRY_NOT_FOUND
652 		CHK(mime.IsInstalled());
653 		CHK(mime.GetAppHint(NULL) != B_OK);		// R5 == B_BAD_VALUE
654 		CHK(mime.SetAppHint(NULL) != B_OK);		// R5 == B_ENTRY_NOT_FOUND
655 #else
656 		CHK(!mime.IsInstalled());
657 		CHK(mime.GetAppHint(NULL) != B_OK);		// B_BAD_VALUE
658 		CHK(!mime.IsInstalled());
659 		CHK(mime.SetAppHint(NULL) != B_OK);		// B_ENTRY_NOT_FOUND
660 		CHK(!mime.IsInstalled());
661 		CHK(mime.SetAppHint(&appRef) == B_OK);
662 		CHK(mime.IsInstalled());
663 		CHK(mime.GetAppHint(&ref) == B_OK);
664 		CHK(ref == appRef);
665 		CHK(mime.SetAppHint(NULL) == B_OK);
666 		CHK(mime.IsInstalled());
667 		CHK(mime.GetAppHint(&ref) != B_OK);		// B_ENTRY_NOT_FOUND
668 #endif
669 	}
670 	// Delete test
671 	NextSubTest();
672 	{
673 #if !TEST_R5
674 		entry_ref ref;
675 		BMimeType mime(testType);
676 		CHK(mime.InitCheck() == B_OK);
677 		// Make sure the type isn't installed
678 		if (mime.IsInstalled())
679 			CHK(mime.Delete() == B_OK);
680 		CHK(!mime.IsInstalled());
681 		CHK(mime.DeleteAppHint() != B_OK);
682 		CHK(!mime.IsInstalled());
683 		CHK(mime.SetAppHint(&appRef) == B_OK);
684 		CHK(mime.IsInstalled());
685 		CHK(mime.GetAppHint(&ref) == B_OK);
686 		CHK(ref == appRef);
687 		CHK(mime.DeleteAppHint() == B_OK);
688 		CHK(mime.IsInstalled());
689 		CHK(mime.GetAppHint(&ref) != B_OK);
690 #endif
691 	}
692 	// Non-installed type
693 	NextSubTest();
694 	{
695 		entry_ref ref;
696 		BMimeType mime(testType);
697 		CHK(mime.InitCheck() == B_OK);
698 		// Make sure the type isn't installed
699 		if (mime.IsInstalled())
700 			CHK(mime.Delete() == B_OK);
701 		CHK(!mime.IsInstalled());
702 		CHK(mime.GetAppHint(&ref) != B_OK);		// R5 == B_ENTRY_NOT_FOUND
703 		CHK(!mime.IsInstalled());
704 		CHK(mime.SetAppHint(&appRef) == B_OK);
705 		CHK(mime.IsInstalled());
706 		CHK(mime.GetAppHint(&ref) == B_OK);
707 		CHK(ref == appRef);
708 	}
709 	// Installed Type
710 	NextSubTest();
711 	{
712 		entry_ref ref;
713 		BMimeType mime(testType);
714 		CHK(mime.InitCheck() == B_OK);
715 		// Uninstall then reinstall to clear attributes
716 		if (mime.IsInstalled())
717 			CHK(mime.Delete() == B_OK);
718 		if (!mime.IsInstalled())
719 			CHK(mime.Install() == B_OK);
720 		CHK(mime.IsInstalled());
721 		// Get() with no apphint installed
722 		CHK(mime.GetAppHint(&ref) == B_ENTRY_NOT_FOUND);
723 		// Initial Set()/Get()
724 		CHK(mime.SetAppHint(&appRef) == B_OK);
725 		CHK(mime.GetAppHint(&ref) == B_OK);
726 		CHK(ref == appRef);
727 		// Followup Set()/Get()
728 		CHK(mime.SetAppHint(&appRef2) == B_OK);
729 		CHK(mime.GetAppHint(&ref) == B_OK);
730 		CHK(ref == appRef2);
731 		CHK(ref != appRef);
732 	}
733 	// Installed Type, invalid entry_ref
734 	NextSubTest();
735 	{
736 		entry_ref ref(-1, -1, NULL);
737 		BMimeType mime(testType);
738 		CHK(mime.InitCheck() == B_OK);
739 		// Uninstall then reinstall to clear attributes
740 		if (mime.IsInstalled())
741 			CHK(mime.Delete() == B_OK);
742 		if (!mime.IsInstalled())
743 			CHK(mime.Install() == B_OK);
744 		CHK(mime.IsInstalled());
745 		CHK(mime.SetAppHint(&appRef) == B_OK);
746 		CHK(mime.SetAppHint(&ref) != B_OK);	// R5 == B_BAD_VALUE
747 	}
748 	// Installed Type, fake/invalid entry_ref
749 	NextSubTest();
750 	{
751 		entry_ref ref(0, 0, "__this_ought_not_exist__");
752 		BMimeType mime(testType);
753 		CHK(mime.InitCheck() == B_OK);
754 		// Uninstall then reinstall to clear attributes
755 		if (mime.IsInstalled())
756 			CHK(mime.Delete() == B_OK);
757 		if (!mime.IsInstalled())
758 			CHK(mime.Install() == B_OK);
759 		CHK(mime.IsInstalled());
760 		CHK(mime.SetAppHint(&appRef) == B_OK);
761 		CHK(mime.SetAppHint(&ref) != B_OK);	// R5 == B_ENTRY_NOT_FOUND
762 	}
763 	// Installed Type, abstract entry_ref
764 	NextSubTest();
765 	{
766 		entry_ref fakeRef;
767 		entry_ref ref;
768 		BEntry entry(fakeTestApp);
769 		CHK(entry.InitCheck() == B_OK);
770 		CHK(!entry.Exists());
771 		CHK(entry.GetRef(&fakeRef) == B_OK);
772 		BMimeType mime(testType);
773 		CHK(mime.InitCheck() == B_OK);
774 		// Uninstall then reinstall to clear attributes
775 		if (mime.IsInstalled())
776 			CHK(mime.Delete() == B_OK);
777 		if (!mime.IsInstalled())
778 			CHK(mime.Install() == B_OK);
779 		CHK(mime.IsInstalled());
780 		CHK(mime.SetAppHint(&appRef) == B_OK);
781 		CHK(mime.SetAppHint(&fakeRef) == B_OK);
782 		CHK(mime.GetAppHint(&ref) == B_OK);
783 		CHK(ref == fakeRef);
784 		CHK(ref != appRef);
785 	}
786 }
787 
788 // Attr Info
789 
790 void
AttrInfoTest()791 MimeTypeTest::AttrInfoTest() {
792 	// Create some messages to sling around
793 	const int32 WHAT = 233;	// This is the what value that GAI() returns...not sure if it has a name yet
794 	BMessage msg1(WHAT), msg2(WHAT), msg3(WHAT), msgIncomplete1(WHAT), msgIncomplete2(WHAT);
795 
796 	CHK(msg1.AddString(attrInfoField_Name, "Color") == B_OK);
797 	CHK(msg1.AddString(attrInfoField_PublicName, "The Color") == B_OK);
798 	CHK(msg1.AddInt32(attrInfoField_Type, B_STRING_TYPE) == B_OK);
799 	CHK(msg1.AddBool(attrInfoField_Viewable, true) == B_OK);
800 	CHK(msg1.AddBool(attrInfoField_Editable, true) == B_OK);
801 
802 	CHK(msg1.AddString(attrInfoField_Name, "High Score") == B_OK);
803 	CHK(msg1.AddString(attrInfoField_PublicName, "The Highest Score Ever") == B_OK);
804 	CHK(msg1.AddInt32(attrInfoField_Type, B_INT32_TYPE) == B_OK);
805 	CHK(msg1.AddBool(attrInfoField_Viewable, false) == B_OK);
806 	CHK(msg1.AddBool(attrInfoField_Editable, false) == B_OK);
807 
808 	CHK(msg2.AddString(attrInfoField_Name, "Volume") == B_OK);
809 	CHK(msg2.AddString(attrInfoField_PublicName, "Loudness") == B_OK);
810 	CHK(msg2.AddInt32(attrInfoField_Type, B_DOUBLE_TYPE) == B_OK);
811 	CHK(msg2.AddBool(attrInfoField_Viewable, true) == B_OK);
812 	CHK(msg2.AddBool(attrInfoField_Editable, true) == B_OK);
813 
814 	CHK(msg3.AddString(attrInfoField_Name, "Volume") == B_OK);
815 	CHK(msg3.AddString(attrInfoField_PublicName, "Loudness") == B_OK);
816 	CHK(msg3.AddInt32(attrInfoField_Type, B_DOUBLE_TYPE) == B_OK);
817 	CHK(msg3.AddBool(attrInfoField_Viewable, true) == B_OK);
818 	CHK(msg3.AddBool(attrInfoField_Editable, true) == B_OK);
819 
820 	CHK(msgIncomplete1.AddString(attrInfoField_Name, "Color") == B_OK);
821 	CHK(msgIncomplete1.AddString(attrInfoField_PublicName, "The Color") == B_OK);
822 	CHK(msgIncomplete1.AddInt32(attrInfoField_Type, B_STRING_TYPE) == B_OK);
823 	CHK(msgIncomplete1.AddBool(attrInfoField_Viewable, true) == B_OK);
824 	CHK(msgIncomplete1.AddBool(attrInfoField_Editable, true) == B_OK);
825 
826 	CHK(msgIncomplete1.AddString(attrInfoField_Name, "High Score") == B_OK);
827 //	CHK(msgIncomplete1.AddString(attrInfoField_PublicName, "The Highest Score Ever") == B_OK);
828 	CHK(msgIncomplete1.AddInt32(attrInfoField_Type, B_INT32_TYPE) == B_OK);
829 //	CHK(msgIncomplete1.AddBool(attrInfoField_Viewable, false) == B_OK);
830 	CHK(msgIncomplete1.AddBool(attrInfoField_Editable, false) == B_OK);
831 
832 	CHK(msgIncomplete2.AddString(attrInfoField_Name, "Color") == B_OK);
833 //	CHK(msgIncomplete2.AddString(attrInfoField_PublicName, "The Color") == B_OK);
834 //	CHK(msgIncomplete2.AddInt32(attrInfoField_Type, B_STRING_TYPE) == B_OK);
835 //	CHK(msgIncomplete2.AddBool(attrInfoField_Viewable, true) == B_OK);
836 	CHK(msgIncomplete2.AddBool(attrInfoField_Editable, true) == B_OK);
837 
838 	CHK(msg1 == msg1);
839 	CHK(msg2 == msg2);
840 	CHK(msg3 == msg3);
841 	CHK(msg1 != msg2);
842 	CHK(msg1 != msg3);
843 	CHK(msg2 == msg3);
844 
845 	// Uninitialized
846 	NextSubTest();
847 	{
848 		BMimeType mime;
849 		BMessage msg;
850 
851 		CHK(mime.InitCheck() == B_NO_INIT);
852 		CHK(mime.GetAttrInfo(&msg) != B_OK);		// R5 == B_BAD_VALUE
853 		CHK(mime.SetAttrInfo(&msg) != B_OK);		// R5 == B_BAD_VALUE
854 	}
855 
856 	// NULL params
857 	NextSubTest();
858 	{
859 #if !TEST_R5
860 		BMessage msg;
861 		BMimeType mime(testType);
862 		CHK(mime.InitCheck() == B_OK);
863 		// Make sure the type isn't installed
864 		if (mime.IsInstalled())
865 			CHK(mime.Delete() == B_OK);
866 		CHK(!mime.IsInstalled());
867 		CHK(mime.DeleteAttrInfo() != B_OK);
868 		CHK(!mime.IsInstalled());
869 		msg1.RemoveName(typeField);						// Clear "type" field, since SAI() just adds another
870 		CHK(mime.SetAttrInfo(&msg1) == B_OK);
871 		CHK(mime.IsInstalled());
872 		CHK(msg != msg1);
873 		CHK(mime.GetAttrInfo(&msg) == B_OK);
874 		CHK(msg1.AddString(typeField, testType) == B_OK);	// Add in "type" field as GAI() does
875 		CHK(msg == msg1);
876 		CHK(mime.SetAttrInfo(NULL) == B_OK);
877 		CHK(mime.IsInstalled());
878 		CHK(mime.GetAttrInfo(&msg) != B_OK);
879 #endif
880 	}
881 	// Delete test
882 	NextSubTest();
883 	{
884 #if !TEST_R5
885 		BMessage msg;
886 		BMimeType mime(testType);
887 		CHK(mime.InitCheck() == B_OK);
888 		// Make sure the type isn't installed
889 		if (mime.IsInstalled())
890 			CHK(mime.Delete() == B_OK);
891 		CHK(!mime.IsInstalled());
892 		CHK(mime.DeleteAttrInfo() != B_OK);
893 		CHK(!mime.IsInstalled());
894 		msg1.RemoveName(typeField);						// Clear "type" field, since SAI() just adds another
895 		CHK(mime.SetAttrInfo(&msg1) == B_OK);
896 		CHK(mime.IsInstalled());
897 		CHK(msg != msg1);
898 		CHK(mime.GetAttrInfo(&msg) == B_OK);
899 		CHK(msg1.AddString(typeField, testType) == B_OK);	// Add in "type" field as GAI() does
900 		CHK(msg == msg1);
901 		CHK(mime.DeleteAttrInfo() == B_OK);
902 		CHK(mime.IsInstalled());
903 		CHK(mime.GetAttrInfo(&msg) != B_OK);
904 #endif
905 	}
906 
907 	// Improperly formatted BMessages
908 	NextSubTest();
909 	{
910 		BMessage msg(WHAT);
911 		BMimeType mime(testType);
912 		CHK(mime.InitCheck() == B_OK);
913 
914 		// Uninstall then reinstall to clear attributes
915 		if (mime.IsInstalled())
916 			CHK(mime.Delete() == B_OK);
917 		if (!mime.IsInstalled())
918 			CHK(mime.Install() == B_OK);
919 		CHK(mime.IsInstalled());
920 
921 		// Initial Set()/Get()
922 		msgIncomplete1.RemoveName(typeField);		// Clear "type" fields, since SAI() just adds another
923 		msgIncomplete2.RemoveName(typeField);
924 		CHK(msg != msgIncomplete1);
925 		CHK(msg != msgIncomplete2);
926 		CHK(mime.SetAttrInfo(&msgIncomplete1) == B_OK);
927 		CHK(mime.GetAttrInfo(&msg) == B_OK);
928 		CHK(msgIncomplete1.AddString(typeField, testType) == B_OK);	// Add in "type" fields as GFE() does
929 		CHK(msgIncomplete2.AddString(typeField, testType) == B_OK);
930 		CHK(msg == msgIncomplete1);
931 		CHK(msg != msgIncomplete2);
932 	}
933 
934 	// Set() with improperly formatted message
935 	NextSubTest();
936 	{
937 		BMessage msg(WHAT);
938 		BMimeType mime(testType);
939 		CHK(mime.InitCheck() == B_OK);
940 
941 		// Uninstall then reinstall to clear attributes
942 		if (mime.IsInstalled())
943 			CHK(mime.Delete() == B_OK);
944 		if (!mime.IsInstalled())
945 			CHK(mime.Install() == B_OK);
946 		CHK(mime.IsInstalled());
947 
948 		// Initial Set()/Get()
949 		msgIncomplete1.RemoveName(typeField);		// Clear "type" fields, since SAI() just adds another
950 		msgIncomplete2.RemoveName(typeField);
951 		CHK(msg != msgIncomplete1);
952 		CHK(msg != msgIncomplete2);
953 		CHK(mime.SetAttrInfo(&msgIncomplete1) == B_OK);
954 		CHK(mime.GetAttrInfo(&msg) == B_OK);
955 		CHK(msgIncomplete1.AddString(typeField, testType) == B_OK);	// Add in "type" fields as GFE() does
956 		CHK(msgIncomplete2.AddString(typeField, testType) == B_OK);
957 		CHK(msg == msgIncomplete1);
958 		CHK(msg != msgIncomplete2);
959 	}
960 
961 	// Set() with empty message
962 	NextSubTest();
963 	{
964 		BMimeType mime(testType);
965 		BMessage msgEmpty(WHAT);
966 		BMessage msg(WHAT);
967 		CHK(msg.AddInt32("stuff", 1234) == B_OK);	// Add an extra attribute to give us something to compare with
968 
969 		// Uninstall then reinstall to clear attributes
970 		if (mime.IsInstalled())
971 			CHK(mime.Delete() == B_OK);
972 		if (!mime.IsInstalled())
973 			CHK(mime.Install() == B_OK);
974 		CHK(mime.IsInstalled());
975 
976 		// Set(empty)
977 		CHK(msg != msgEmpty);
978 		CHK(mime.SetAttrInfo(&msgEmpty) == B_OK);
979 		CHK(mime.GetAttrInfo(&msg) == B_OK);
980 		CHK(msgEmpty.AddString(typeField, testType) == B_OK);	// Add in "type" fields as GFE() does
981 		CHK(msg == msgEmpty);
982 	}
983 
984 	// Set() with extra attributes in message
985 	NextSubTest();
986 	{
987 		BMimeType mime(testType);
988 		BMessage msg(WHAT);
989 		BMessage msgExtraSet(msg1);
990 		CHK(msgExtraSet.AddString("extra", ".extra") == B_OK);
991 		CHK(msgExtraSet.AddInt32("more_extras", 123) == B_OK);
992 		CHK(msgExtraSet.AddInt32("more_extras", 456) == B_OK);
993 		CHK(msgExtraSet.AddInt32("more_extras", 789) == B_OK);
994 		BMessage msgExtraGet(msgExtraSet);
995 
996 		// Uninstall then reinstall to clear attributes
997 		if (mime.IsInstalled())
998 			CHK(mime.Delete() == B_OK);
999 		if (!mime.IsInstalled())
1000 			CHK(mime.Install() == B_OK);
1001 		CHK(mime.IsInstalled());
1002 
1003 		// Set(extra)/Get(empty)
1004 		msg1.RemoveName(typeField);		// Clear "type" fields, since SFE() just adds another
1005 		msg2.RemoveName(typeField);
1006 		CHK(msg != msg1);
1007 		CHK(msg != msgExtraSet);
1008 		CHK(mime.SetAttrInfo(&msgExtraSet) == B_OK);
1009 		CHK(mime.GetAttrInfo(&msg) == B_OK);
1010 		CHK(msg1.AddString(typeField, testType) == B_OK);	// Add in "type" fields as GFE() does
1011 		CHK(msgExtraSet.AddString(typeField, testType) == B_OK);
1012 		CHK(msg == msgExtraSet);
1013 		CHK(msg != msg1);
1014 
1015 		// Get(extra)
1016 		NextSubTest();
1017 		CHK(mime.GetAttrInfo(&msgExtraGet) == B_OK);
1018 		CHK(msgExtraGet == msgExtraSet);
1019 		CHK(msgExtraGet != msg1);
1020 
1021 		// Get(extra and then some)
1022 		NextSubTest();
1023 		CHK(msgExtraGet.AddInt32("more_extras", 101112) == B_OK);
1024 		msgExtraGet.RemoveName(typeField);		// Clear "type" fields to be fair, since SFE() just adds another
1025 		CHK(mime.GetAttrInfo(&msgExtraGet) == B_OK);	// Reinitializes result (clearing extra fields)
1026 		CHK(msgExtraGet == msgExtraSet);
1027 		CHK(msgExtraGet != msg1);
1028 
1029 	}
1030 	// Normal Function (Non-installed type)
1031 	NextSubTest();
1032 	{
1033 		BMimeType mime(testType);
1034 		BMessage msg(WHAT);
1035 		BMessage msg2(WHAT);
1036 
1037 		CHK(mime.InitCheck() == B_OK);
1038 		// Make sure the type isn't installed
1039 		if (mime.IsInstalled())
1040 			CHK(mime.Delete() == B_OK);
1041 
1042 		CHK(!mime.IsInstalled());
1043 		CHK(mime.GetAttrInfo(&msg) != B_OK);		// R5 == B_ENTRY_NOT_FOUND
1044 		CHK(!mime.IsInstalled());
1045 		CHK(mime.SetAttrInfo(&msg) == B_OK);
1046 		CHK(mime.IsInstalled());
1047 		CHK(mime.GetAttrInfo(&msg2) == B_OK);
1048 		CHK(msg.AddString(typeField, testType) == B_OK);	// Add in "type" fields as GAI() does
1049 		CHK(msg == msg2);
1050 	}
1051 
1052 	// Normal Function
1053 	NextSubTest();
1054 	{
1055 		BMessage msg(WHAT);
1056 		BMimeType mime(testType);
1057 		CHK(mime.InitCheck() == B_OK);
1058 
1059 		// Uninstall then reinstall to clear attributes
1060 		if (mime.IsInstalled())
1061 			CHK(mime.Delete() == B_OK);
1062 		if (!mime.IsInstalled())
1063 			CHK(mime.Install() == B_OK);
1064 		CHK(mime.IsInstalled());
1065 
1066 		// Initial Set()/Get()
1067 		msg1.RemoveName(typeField);		// Clear "type" fields, since SAI() just adds another
1068 		msg2.RemoveName(typeField);
1069 		CHK(msg != msg1);
1070 		CHK(msg != msg2);
1071 		CHK(mime.SetAttrInfo(&msg1) == B_OK);
1072 		CHK(mime.GetAttrInfo(&msg) == B_OK);
1073 		CHK(msg1.AddString(typeField, testType) == B_OK);	// Add in "type" fields as GFE() does
1074 		CHK(msg2.AddString(typeField, testType) == B_OK);
1075 		CHK(msg == msg1);
1076 		CHK(msg != msg2);
1077 
1078 		// Followup Set()/Get()
1079 		NextSubTest();
1080 		CHK(msg.MakeEmpty() == B_OK);
1081 		msg1.RemoveName(typeField);		// Clear "type" fields, since SFE() just adds another
1082 		msg2.RemoveName(typeField);
1083 		CHK(msg != msg1);
1084 		CHK(msg != msg2);
1085 		CHK(mime.SetAttrInfo(&msg2) == B_OK);
1086 		CHK(mime.GetAttrInfo(&msg) == B_OK);
1087 		CHK(msg1.AddString(typeField, testType) == B_OK);	// Add in "type" fields as GFE() does
1088 		CHK(msg2.AddString(typeField, testType) == B_OK);
1089 		CHK(msg != msg1);
1090 		CHK(msg == msg2);
1091 
1092 		// Clear
1093 		NextSubTest();
1094 		CHK(msg.MakeEmpty() == B_OK);
1095 		msg1.RemoveName(typeField);		// Clear "type" fields, since SFE() just adds another
1096 		msg2.RemoveName(typeField);
1097 		CHK(msg != msg1);
1098 		CHK(msg != msg2);
1099 #if !TEST_R5
1100 		CHK(mime.SetAttrInfo(NULL) == B_OK);		// R5 == CRASH! despite what one might think should happen
1101 		CHK(mime.GetAttrInfo(&msg) != B_OK);
1102 		CHK(msg1.AddString(typeField, testType) == B_OK);	// Add in "type" fields as GFE() does
1103 		CHK(msg2.AddString(typeField, testType) == B_OK);
1104 		CHK(msg != msg1);
1105 		CHK(msg != msg2);
1106 #endif
1107 	}
1108 }
1109 
1110 // File Extensions
1111 
1112 void
FileExtensionsTest()1113 MimeTypeTest::FileExtensionsTest() {
1114 	// Create some messages to sling around
1115 	const int32 WHAT = 234;	// This is the what value that GFE returns...not sure if it has a name yet
1116 	BMessage msg1(WHAT), msg2(WHAT), msg3(WHAT);
1117 
1118 	CHK(msg1.AddString(fileExtField, ".data") == B_OK);
1119 	CHK(msg1.AddString(fileExtField, ".txt") == B_OK);
1120 	CHK(msg1.AddString(fileExtField, ".png") == B_OK);
1121 	CHK(msg1.AddString(fileExtField, ".html") == B_OK);
1122 
1123 	CHK(msg2.AddString(fileExtField, ".data") == B_OK);
1124 	CHK(msg2.AddString(fileExtField, ".txt") == B_OK);
1125 
1126 	CHK(msg3.AddString(fileExtField, ".data") == B_OK);
1127 	CHK(msg3.AddString(fileExtField, ".txt") == B_OK);
1128 
1129 	CHK(msg1 == msg1);
1130 	CHK(msg2 == msg2);
1131 	CHK(msg3 == msg3);
1132 	CHK(msg1 != msg2);
1133 	CHK(msg1 != msg3);
1134 	CHK(msg2 == msg3);
1135 
1136 	// Uninitialized
1137 	NextSubTest();
1138 	{
1139 		BMessage msg(WHAT);
1140 		BMimeType mime;
1141 
1142 		CHK(mime.InitCheck() == B_NO_INIT);
1143 		CHK(mime.GetFileExtensions(&msg) != B_OK);	// R5 == B_BAD_VALUE
1144 		CHK(mime.SetFileExtensions(&msg) != B_OK);	// R5 == B_BAD_VALUE
1145 	}
1146 	// NULL params
1147 	NextSubTest();
1148 	{
1149 #if !TEST_R5
1150 		BMessage msg;
1151 		BMimeType mime(testType);
1152 		CHK(mime.InitCheck() == B_OK);
1153 		// Make sure the type isn't installed
1154 		if (mime.IsInstalled())
1155 			CHK(mime.Delete() == B_OK);
1156 		CHK(!mime.IsInstalled());
1157 		CHK(mime.DeleteFileExtensions() != B_OK);
1158 		CHK(!mime.IsInstalled());
1159 		msg1.RemoveName(typeField);						// Clear "type" field, since SAI() just adds another
1160 		CHK(mime.SetFileExtensions(&msg1) == B_OK);
1161 		CHK(mime.IsInstalled());
1162 		CHK(msg != msg1);
1163 		CHK(mime.GetFileExtensions(&msg) == B_OK);
1164 		CHK(msg1.AddString(typeField, testType) == B_OK);	// Add in "type" field as GAI() does
1165 		CHK(msg == msg1);
1166 		CHK(mime.SetFileExtensions(NULL) == B_OK);
1167 		CHK(mime.IsInstalled());
1168 		CHK(mime.GetFileExtensions(&msg) != B_OK);
1169 #endif
1170 	}
1171 	// Delete test
1172 	NextSubTest();
1173 	{
1174 #if !TEST_R5
1175 		BMessage msg;
1176 		BMimeType mime(testType);
1177 		CHK(mime.InitCheck() == B_OK);
1178 		// Make sure the type isn't installed
1179 		if (mime.IsInstalled())
1180 			CHK(mime.Delete() == B_OK);
1181 		CHK(!mime.IsInstalled());
1182 		CHK(mime.DeleteFileExtensions() != B_OK);
1183 		CHK(!mime.IsInstalled());
1184 		msg1.RemoveName(typeField);						// Clear "type" field, since SAI() just adds another
1185 		CHK(mime.SetFileExtensions(&msg1) == B_OK);
1186 		CHK(mime.IsInstalled());
1187 		CHK(msg != msg1);
1188 		CHK(mime.GetFileExtensions(&msg) == B_OK);
1189 		CHK(msg1.AddString(typeField, testType) == B_OK);	// Add in "type" field as GAI() does
1190 		CHK(msg == msg1);
1191 		CHK(mime.DeleteFileExtensions() == B_OK);
1192 		CHK(mime.IsInstalled());
1193 		CHK(mime.GetFileExtensions(&msg) != B_OK);
1194 #endif
1195 	}
1196 	// Set() with empty message
1197 	NextSubTest();
1198 	{
1199 		BMimeType mime(testType);
1200 		BMessage msgEmpty(WHAT);
1201 		BMessage msg(WHAT);
1202 		CHK(msg.AddInt32("stuff", 1234) == B_OK);	// Add an extra attribute to give us something to compare with
1203 
1204 		// Uninstall then reinstall to clear attributes
1205 		if (mime.IsInstalled())
1206 			CHK(mime.Delete() == B_OK);
1207 		if (!mime.IsInstalled())
1208 			CHK(mime.Install() == B_OK);
1209 		CHK(mime.IsInstalled());
1210 
1211 		// Set(empty)
1212 		CHK(msg != msgEmpty);
1213 		CHK(mime.SetFileExtensions(&msgEmpty) == B_OK);
1214 		CHK(mime.GetFileExtensions(&msg) == B_OK);
1215 		CHK(msgEmpty.AddString(typeField, testType) == B_OK);	// Add in "type" fields as GFE() does
1216 		CHK(msg == msgEmpty);
1217 	}
1218 	// Set() with extra attributes in message
1219 	NextSubTest();
1220 	{
1221 		BMimeType mime(testType);
1222 		BMessage msg(WHAT);
1223 		BMessage msgExtraSet(msg1);
1224 		CHK(msgExtraSet.AddString("extra", ".extra") == B_OK);
1225 		CHK(msgExtraSet.AddInt32("more_extras", 123) == B_OK);
1226 		CHK(msgExtraSet.AddInt32("more_extras", 456) == B_OK);
1227 		CHK(msgExtraSet.AddInt32("more_extras", 789) == B_OK);
1228 		BMessage msgExtraGet(msgExtraSet);
1229 
1230 		// Uninstall then reinstall to clear attributes
1231 		if (mime.IsInstalled())
1232 			CHK(mime.Delete() == B_OK);
1233 		if (!mime.IsInstalled())
1234 			CHK(mime.Install() == B_OK);
1235 		CHK(mime.IsInstalled());
1236 
1237 		// Set(extra)/Get(empty)
1238 		msg1.RemoveName(typeField);		// Clear "type" fields, since SFE() just adds another
1239 		msg2.RemoveName(typeField);
1240 		CHK(msg != msg1);
1241 		CHK(msg != msgExtraSet);
1242 		CHK(mime.SetFileExtensions(&msgExtraSet) == B_OK);
1243 		CHK(mime.GetFileExtensions(&msg) == B_OK);
1244 		CHK(msg1.AddString(typeField, testType) == B_OK);	// Add in "type" fields as GFE() does
1245 		CHK(msgExtraSet.AddString(typeField, testType) == B_OK);
1246 		CHK(msg == msgExtraSet);
1247 		CHK(msg != msg1);
1248 
1249 		// Get(extra)
1250 		NextSubTest();
1251 		CHK(mime.GetFileExtensions(&msgExtraGet) == B_OK);
1252 		CHK(msgExtraGet == msgExtraSet);
1253 		CHK(msgExtraGet != msg1);
1254 
1255 		// Get(extra and then some)
1256 		NextSubTest();
1257 		CHK(msgExtraGet.AddInt32("more_extras", 101112) == B_OK);
1258 		msgExtraGet.RemoveName(typeField);		// Clear "type" fields to be fair, since SFE() just adds another
1259 		CHK(mime.GetFileExtensions(&msgExtraGet) == B_OK);	// Reinitializes result (clearing extra fields)
1260 		CHK(msgExtraGet == msgExtraSet);
1261 		CHK(msgExtraGet != msg1);
1262 
1263 	}
1264 	// Normal function
1265 	NextSubTest();
1266 	{
1267 		BMessage msg(WHAT);
1268 		BMimeType mime(testType);
1269 
1270 		// Uninstall then reinstall to clear attributes
1271 		if (mime.IsInstalled())
1272 			CHK(mime.Delete() == B_OK);
1273 		if (!mime.IsInstalled())
1274 			CHK(mime.Install() == B_OK);
1275 		CHK(mime.IsInstalled());
1276 
1277 		// Initial Set()/Get()
1278 		msg1.RemoveName(typeField);		// Clear "type" fields, since SFE() just adds another
1279 		msg2.RemoveName(typeField);
1280 		CHK(msg != msg1);
1281 		CHK(msg != msg2);
1282 		CHK(mime.SetFileExtensions(&msg1) == B_OK);
1283 		CHK(mime.GetFileExtensions(&msg) == B_OK);
1284 		CHK(msg1.AddString(typeField, testType) == B_OK);	// Add in "type" fields as GFE() does
1285 		CHK(msg2.AddString(typeField, testType) == B_OK);
1286 		CHK(msg == msg1);
1287 		CHK(msg != msg2);
1288 
1289 		// Followup Set()/Get()
1290 		NextSubTest();
1291 		CHK(msg.MakeEmpty() == B_OK);
1292 		msg1.RemoveName(typeField);		// Clear "type" fields, since SFE() just adds another
1293 		msg2.RemoveName(typeField);
1294 		CHK(msg != msg1);
1295 		CHK(msg != msg2);
1296 		CHK(mime.SetFileExtensions(&msg2) == B_OK);
1297 		CHK(mime.GetFileExtensions(&msg) == B_OK);
1298 		CHK(msg1.AddString(typeField, testType) == B_OK);	// Add in "type" fields as GFE() does
1299 		CHK(msg2.AddString(typeField, testType) == B_OK);
1300 		CHK(msg != msg1);
1301 		CHK(msg == msg2);
1302 
1303 		// Clear
1304 		NextSubTest();
1305 		CHK(msg.MakeEmpty() == B_OK);
1306 		msg1.RemoveName(typeField);		// Clear "type" fields, since SFE() just adds another
1307 		msg2.RemoveName(typeField);
1308 		CHK(msg != msg1);
1309 		CHK(msg != msg2);
1310 #if !TEST_R5
1311 		CHK(mime.SetFileExtensions(NULL) == B_OK);		// R5 == CRASH! despite what the BeBook says
1312 		CHK(mime.GetFileExtensions(&msg) != B_OK);
1313 		CHK(msg1.AddString(typeField, testType) == B_OK);	// Add in "type" fields as GFE() does
1314 		CHK(msg2.AddString(typeField, testType) == B_OK);
1315 		CHK(msg != msg1);
1316 		CHK(msg != msg2);
1317 #endif
1318 	}
1319 }
1320 
1321 
1322 // Icon Test Helper Function
1323 
1324 void
IconTest(IconHelper & helper)1325 MimeTypeTest::IconTest(IconHelper &helper) {
1326 	BBitmap *bmp = helper.TempBitmap();
1327 	// Unitialized
1328 	NextSubTest();
1329 	{
1330 		BMimeType mime;
1331 		CHK(mime.InitCheck() == B_NO_INIT);
1332 		CHK(helper.GetIcon(mime, bmp) != B_OK);	// R5 == B_BAD_VALUE
1333 		CHK(helper.SetIcon(mime, bmp) != B_OK);	// R5 == B_BAD_VALUE
1334 	}
1335 	// Non-installed type
1336 	NextSubTest();
1337 	{
1338 		BMimeType mime(testType);
1339 		CHK(mime.InitCheck() == B_OK);
1340 		// Make sure the type isn't installed
1341 		if (mime.IsInstalled())
1342 			CHK(mime.Delete() == B_OK);
1343 		CHK(!mime.IsInstalled());
1344 		// Test
1345 		CHK(helper.GetIcon(mime, bmp) != B_OK);	// R5 == B_ENTRY_NOT_FOUND
1346 		CHK(!mime.IsInstalled());
1347 		CHK(helper.SetIcon(mime, helper.Bitmap1()) == B_OK);
1348 		CHK(mime.IsInstalled());
1349 		CHK(helper.GetIcon(mime, bmp) == B_OK);
1350 		CHK(*bmp == *helper.Bitmap1());
1351 	}
1352 	// NULL params
1353 	NextSubTest();
1354 	{
1355 		BMimeType mime(testType);
1356 		CHK(mime.InitCheck() == B_OK);
1357 		// Make sure the type isn't installed
1358 		if (mime.IsInstalled())
1359 			CHK(mime.Delete() == B_OK);
1360 		CHK(!mime.IsInstalled());
1361 		// Uninstalled
1362 		CHK(helper.GetIcon(mime, NULL) != B_OK);	// B_BAD_VALUE
1363 		CHK(!mime.IsInstalled());
1364 		CHK(helper.SetIcon(mime, NULL) != B_OK);	// R5 == Installs, B_ENTRY_NOT_FOUND
1365 													// Haiku == Doesn't install, B_ENTRY_NOT_FOUND
1366 #if TEST_R5
1367 		CHK(mime.IsInstalled());
1368 #else
1369 		CHK(!mime.IsInstalled());
1370 		CHK(mime.Install() == B_OK);
1371 #endif
1372 		CHK(helper.GetIcon(mime, bmp) != B_OK);	// B_ENTRY_NOT_FOUND
1373 		// Installed
1374 		CHK(helper.GetIcon(mime, NULL) != B_OK);	// B_BAD_VALUE
1375 		CHK(helper.SetIcon(mime, helper.Bitmap1()) == B_OK);
1376 		CHK(helper.GetIcon(mime, bmp) == B_OK);
1377 		CHK(*bmp == *helper.Bitmap1());
1378 		CHK(helper.SetIcon(mime, NULL) == B_OK);
1379 		CHK(helper.GetIcon(mime, bmp) != B_OK);	// B_ENTRY_NOT_FOUND
1380 	}
1381 	// Delete test
1382 	NextSubTest();
1383 	{
1384 #if !TEST_R5
1385 		BMimeType mime(testType);
1386 		CHK(mime.InitCheck() == B_OK);
1387 		// Make sure the type isn't installed
1388 		if (mime.IsInstalled())
1389 			CHK(mime.Delete() == B_OK);
1390 		CHK(!mime.IsInstalled());
1391 		CHK(helper.DeleteIcon(mime) != B_OK);
1392 		CHK(!mime.IsInstalled());
1393 		CHK(helper.SetIcon(mime, helper.Bitmap2()) == B_OK);
1394 		CHK(mime.IsInstalled());
1395 		fill_bitmap(*bmp, 100);
1396 		CHK(*bmp != *helper.Bitmap2());
1397 		CHK(helper.GetIcon(mime, bmp) == B_OK);
1398 		CHK(*bmp == *helper.Bitmap2());
1399 		CHK(helper.DeleteIcon(mime) == B_OK);
1400 		CHK(mime.IsInstalled());
1401 		CHK(helper.GetIcon(mime, bmp) != B_OK);
1402 #endif
1403 	}
1404 	// Invalid Bitmap Size (small -- 10x10)
1405 	NextSubTest();
1406 	{
1407 		BMimeType mime(testType);
1408 		CHK(mime.InitCheck() == B_OK);
1409 		// Uninstall then reinstall to clear attributes
1410 		if (mime.IsInstalled())
1411 			CHK(mime.Delete() == B_OK);
1412 		if (!mime.IsInstalled())
1413 			CHK(mime.Install() == B_OK);
1414 		CHK(mime.IsInstalled());
1415 		// Init Test Bitmap
1416 		BBitmap testBmp(BRect(0,0,9,9), B_CMAP8);
1417 		fill_bitmap(testBmp, 3);
1418 		// Test Set()
1419 		CHK(testBmp != *helper.Bitmap1());
1420 		CHK(helper.SetIcon(mime, helper.Bitmap1()) == B_OK);
1421 		CHK(helper.GetIcon(mime, bmp) == B_OK);
1422 		CHK(*bmp == *helper.Bitmap1());
1423 		CHK(helper.SetIcon(mime, &testBmp) != B_OK);	// R5 == B_BAD_VALUE
1424 		CHK(helper.GetIcon(mime, bmp) == B_OK);
1425 		CHK(*bmp == *helper.Bitmap1());
1426 		CHK(*bmp != testBmp);
1427 		// Test Get()
1428 		fill_bitmap(testBmp, 3);
1429 		CHK(helper.SetIcon(mime, helper.Bitmap1()) == B_OK);
1430 		CHK(helper.GetIcon(mime, &testBmp) != B_OK);	// R5 == B_BAD_VALUE
1431 	}
1432 	// Invalid Bitmap Size (large -- 100x100)
1433 	NextSubTest();
1434 	{
1435 		BMimeType mime(testType);
1436 		CHK(mime.InitCheck() == B_OK);
1437 		// Uninstall then reinstall to clear attributes
1438 		if (mime.IsInstalled())
1439 			CHK(mime.Delete() == B_OK);
1440 		if (!mime.IsInstalled())
1441 			CHK(mime.Install() == B_OK);
1442 		CHK(mime.IsInstalled());
1443 		// Init Test Bitmap
1444 		BBitmap testBmp(BRect(0,0,99,99), B_CMAP8);
1445 		// Test Set()
1446 		fill_bitmap(testBmp, 3);
1447 		CHK(testBmp != *helper.Bitmap1());
1448 		CHK(helper.SetIcon(mime, helper.Bitmap1()) == B_OK);
1449 		CHK(helper.GetIcon(mime, bmp) == B_OK);
1450 		CHK(*bmp == *helper.Bitmap1());
1451 		CHK(helper.SetIcon(mime, &testBmp) != B_OK);	// R5 == B_BAD_VALUE
1452 		CHK(helper.GetIcon(mime, bmp) == B_OK);
1453 		CHK(*bmp == *helper.Bitmap1());
1454 		CHK(*bmp != testBmp);
1455 		// Test Get()
1456 		fill_bitmap(testBmp, 3);
1457 		CHK(helper.SetIcon(mime, helper.Bitmap1()) == B_OK);
1458 		CHK(helper.GetIcon(mime, &testBmp) != B_OK);	// R5 == B_BAD_VALUE
1459 	}
1460 	// Non-B_CMAP8 Color Depth (not really supported under R5)
1461 	NextSubTest();
1462 	{
1463 #if !TEST_R5
1464 		BMimeType mime(testType);
1465 		CHK(mime.InitCheck() == B_OK);
1466 		// Uninstall then reinstall to clear attributes
1467 		if (mime.IsInstalled())
1468 			CHK(mime.Delete() == B_OK);
1469 		if (!mime.IsInstalled())
1470 			CHK(mime.Install() == B_OK);
1471 		CHK(mime.IsInstalled());
1472 		// Init Test Bitmap
1473 		BBitmap testBmp(helper.BitmapBounds(), B_RGB32);
1474 		// Test Set()
1475 //		fill_bitmap(testBmp, 4);
1476 		fill_bitmap32(testBmp, 10, 20, 30, 40);				// Fill our 32-bit bitmap
1477 		BBitmap testBmp8(helper.BitmapBounds(), B_CMAP8);	// Create an 8-bit bitmap the same size
1478 //		dump_bitmap(testBmp8);
1479 		reduce_color_depth(testBmp, testBmp8);				// Make it an 8-bit version of the 32-bit bitmap
1480 //		dump_bitmap(testBmp8);
1481 		CHK(testBmp != *helper.Bitmap1());
1482 		CHK(helper.SetIcon(mime, helper.Bitmap1()) == B_OK);
1483 		CHK(helper.GetIcon(mime, bmp) == B_OK);
1484 		CHK(*bmp == *helper.Bitmap1());
1485 		CHK(helper.SetIcon(mime, &testBmp) == B_OK);
1486 			// R5 == B_OK, despite being invalid color depth; however, icon is not actually set,
1487 			// and any subsequent call to GetIcon() will cause the application to crash...
1488 		CHK(helper.GetIcon(mime, bmp) == B_OK);	// R5 == CRASH!, Haiku == Damn right I can handle that shit
1489 		CHK(*bmp != *helper.Bitmap1());
1490 		CHK(*bmp != testBmp);				// Shouldn't match, since SetIcon() reduces to B_CMAP8
1491 		CHK(*bmp == testBmp8);				// *Should* match, since it's the result of a similar reduction
1492 #endif
1493 	}
1494 	// Normal Function
1495 	NextSubTest();
1496 	{
1497 		BMimeType mime(testType);
1498 		CHK(mime.InitCheck() == B_OK);
1499 		// Uninstall then reinstall to clear attributes
1500 		if (mime.IsInstalled())
1501 			CHK(mime.Delete() == B_OK);
1502 		if (!mime.IsInstalled())
1503 			CHK(mime.Install() == B_OK);
1504 		CHK(mime.IsInstalled());
1505 		// Set() then Get()
1506 		fill_bitmap(*bmp, 3);
1507 		CHK(*bmp != *helper.Bitmap1());
1508 		CHK(helper.SetIcon(mime, helper.Bitmap1()) == B_OK);
1509 		CHK(helper.GetIcon(mime, bmp) == B_OK);
1510 		CHK(*bmp == *helper.Bitmap1());
1511 		// Set() then Get() again
1512 		fill_bitmap(*bmp, 3);
1513 		CHK(helper.SetIcon(mime, helper.Bitmap2()) == B_OK);
1514 		CHK(helper.GetIcon(mime, bmp) == B_OK);
1515 		CHK(*bmp == *helper.Bitmap2());
1516 		CHK(*bmp != *helper.Bitmap1());
1517 	}
1518 }
1519 
1520 // Icon For Type Helper Functions
1521 
1522 void
IconForTypeTest(IconForTypeHelper & helper)1523 MimeTypeTest::IconForTypeTest(IconForTypeHelper &helper) {
1524 	IconTest(helper);	// First run all the icon tests
1525 		// Then do some IconForType() specific tests
1526 
1527 	BBitmap *bmp = helper.TempBitmap();
1528 
1529 	// Invalid MIME string
1530 	NextSubTest();
1531 	{
1532 		BMimeType mime(testType);
1533 		CHK(mime.InitCheck() == B_OK);
1534 		// Uninstall then reinstall to clear attributes
1535 		if (mime.IsInstalled())
1536 			CHK(mime.Delete() == B_OK);
1537 		if (!mime.IsInstalled())
1538 			CHK(mime.Install() == B_OK);
1539 		CHK(mime.IsInstalled());
1540 		// Set() then Get()
1541 		fill_bitmap(*bmp, 3);
1542 		CHK(*bmp != *helper.Bitmap1());
1543 		CHK(mime.SetIconForType(testTypeInvalid, helper.Bitmap1(), helper.Size()) != B_OK);	// R5 == B_BAD_VALUE
1544 		CHK(mime.GetIconForType(testTypeInvalid, bmp, helper.Size()) != B_OK);				// R5 == B_BAD_VALUE
1545 		CHK(*bmp != *helper.Bitmap1());
1546 	}
1547 	// NULL MIME string (just like calling respective {Get,Set}Icon() function)
1548 	NextSubTest();
1549 	{
1550 		BMimeType mime(testType);
1551 		CHK(mime.InitCheck() == B_OK);
1552 		// Uninstall then reinstall to clear attributes
1553 		if (mime.IsInstalled())
1554 			CHK(mime.Delete() == B_OK);
1555 		if (!mime.IsInstalled())
1556 			CHK(mime.Install() == B_OK);
1557 		CHK(mime.IsInstalled());
1558 		// Set() then Get()
1559 		fill_bitmap(*bmp, 3);
1560 		CHK(*bmp != *helper.Bitmap1());
1561 		CHK(mime.SetIconForType(NULL, helper.Bitmap1(), helper.Size()) == B_OK);
1562 		CHK(mime.GetIconForType(NULL, bmp, helper.Size()) == B_OK);
1563 		CHK(*bmp == *helper.Bitmap1());
1564 		// Verify GetIcon() does the same thing
1565 		fill_bitmap(*bmp, 3);
1566 		CHK(*bmp != *helper.Bitmap1());
1567 		CHK(mime.GetIcon(bmp, helper.Size()) == B_OK);
1568 		CHK(*bmp == *helper.Bitmap1());
1569 		// Delete with dual NULL calls
1570 		CHK(mime.SetIconForType(NULL, NULL, helper.Size()) == B_OK);
1571 		CHK(mime.GetIconForType(NULL, bmp, helper.Size()) != B_OK);	// B_ENTRY_NOT_FOUND
1572 		CHK(mime.GetIcon(bmp, helper.Size()) != B_OK);				// B_ENTRY_NOT_FOUND
1573 	}
1574 }
1575 
1576 void
LargeIconTest()1577 MimeTypeTest::LargeIconTest() {
1578 	IconHelper helper(B_LARGE_ICON);
1579 	IconTest(helper);
1580 }
1581 
1582 void
MiniIconTest()1583 MimeTypeTest::MiniIconTest() {
1584 	IconHelper helper(B_MINI_ICON);
1585 	IconTest(helper);
1586 }
1587 
1588 void
LargeIconForTypeTest()1589 MimeTypeTest::LargeIconForTypeTest() {
1590 	IconForTypeHelper helper(testType, B_LARGE_ICON);
1591 	IconForTypeTest(helper);
1592 }
1593 
1594 void
MiniIconForTypeTest()1595 MimeTypeTest::MiniIconForTypeTest() {
1596 	IconForTypeHelper helper(testType, B_MINI_ICON);
1597 	IconForTypeTest(helper);
1598 }
1599 
isMIMESupertype(const char * type)1600 bool isMIMESupertype(const char *type) {
1601 	BMimeType sub, super;
1602 	status_t err;
1603 	err = !type || !BMimeType::IsValid(type);
1604 
1605 	// See if the type is the same as it's supertype
1606 	if (!err)
1607 		err = sub.SetTo(type);
1608 	if (!err)
1609 		return sub.GetSupertype(&super) == B_BAD_VALUE;
1610 			// This is what R5::GetSupertype() returns when called on a supertype;
1611 
1612 	return false;
1613 }
1614 
1615 void
VerifyInstalledTypes()1616 MimeTypeTest::VerifyInstalledTypes() {
1617 	// Check GetInstalledTypes(1)
1618 	NextSubTest();
1619 	{
1620 		BMessage msg;
1621 
1622 		// Get the list of installed types
1623 		CHK(BMimeType::GetInstalledTypes(&msg) == B_OK);
1624 
1625 		// Add all the type strings to a std::set
1626 		std::set<std::string> typeSet;
1627 		SetAdapter typeAdapter(typeSet);
1628 		FillWithMimeTypes(typeAdapter, msg, "types");
1629 
1630 		// Manually verify that the set of types returned by GetInstalledTypes()
1631 		// and the types present in the database are exactly the same (ignoring
1632 		// any files with names made of invalid characters, in case some bozo
1633 		// manually added such a file :-)
1634 		BDirectory rootDir(mimeDatabaseDir.c_str());
1635 		BEntry superEntry;
1636 		CHK(rootDir.InitCheck() == B_OK);
1637 		rootDir.Rewind();
1638 		while (true) {
1639 			status_t err = rootDir.GetNextEntry(&superEntry);
1640 			if (err == B_ENTRY_NOT_FOUND)
1641 				break;	// End of directory listing
1642 
1643 			CHK(!err);	// Any other error is unacceptable :-)
1644 
1645 			// Get the leaf name
1646 			char superLeafMixed[B_PATH_NAME_LENGTH+1];
1647 			CHK(superEntry.GetName(superLeafMixed) == B_OK);
1648 			std::string superLeaf;
1649 			to_lower(superLeafMixed, superLeaf);
1650 
1651 			// We're only interested in directories, as they map to
1652 			// supertypes (and since they map thusly, they must also
1653 			// be valid MIME strings)
1654 			if (superEntry.IsDirectory() && BMimeType::IsValid(superLeaf.c_str())) {
1655 				// First, find and remove the supertype from our set
1656 				CHK(typeSet.find(superLeaf.c_str()) != typeSet.end());
1657 				typeSet.erase(superLeaf.c_str());
1658 
1659 				// Second, iterate through all the entries in the directory.
1660 				// If the entry designates a valid MIME string, find it
1661 				// in the set and remove it.
1662 				BDirectory superDir(&superEntry);
1663 				BEntry subEntry;
1664 				CHK(superDir.InitCheck() == B_OK);
1665 				superDir.Rewind();
1666 				while (true) {
1667 					status_t err = superDir.GetNextEntry(&subEntry);
1668 					if (err == B_ENTRY_NOT_FOUND)
1669 						break;	// End of directory listing
1670 
1671 					CHK(!err);	// Any other error is unacceptable :-)
1672 
1673 					// Get the leaf name
1674 					char subLeafMixed[B_PATH_NAME_LENGTH+1];
1675 					CHK(subEntry.GetName(subLeafMixed) == B_OK);
1676 					std::string subLeaf;
1677 					to_lower(subLeafMixed, subLeaf);
1678 
1679 					// Verify it's a valid mime string. If so, find and remove from our set
1680 					std::string subType = superLeaf + "/" + subLeaf;
1681 					if (BMimeType::IsValid(subType.c_str())) {
1682 						if (typeSet.find(subType.c_str()) == typeSet.end())
1683 							cout << "Fuckup == '" << subType << "'" << endl;
1684 						CHK(typeSet.find(subType.c_str()) != typeSet.end());
1685 						typeSet.erase(subType.c_str());
1686 					}
1687 				}
1688 			}
1689 		}
1690 
1691 		// At this point our set should be empty :-) If it's not, you might check
1692 		// that you haven't added any superfluous files to your MIME database (like
1693 		// a __mime_table backup, for instance).
1694 		CHK(typeSet.size() == 0);
1695 	}
1696 	NextSubTest();
1697 	// Check GetInstalledTypes(2) and GetInstalledSupertypes()
1698 	{
1699 		BMessage msg;
1700 
1701 		// Get the list of installed types
1702 		CHK(BMimeType::GetInstalledSupertypes(&msg) == B_OK);
1703 //		msg.PrintToStream();
1704 
1705 		// Add all the type strings to a std::set
1706 		std::set<std::string> typeSet;
1707 		SetAdapter typeAdapter(typeSet);
1708 		FillWithMimeTypes(typeAdapter, msg, "super_types");
1709 
1710 		// Manually verify that the set of types returned by GetInstalledSupertypes()
1711 		// and the types present in the database are exactly the same (ignoring
1712 		// any files with names made of invalid characters, in case some bozo
1713 		// manually added such a file :-)
1714 		BDirectory rootDir(mimeDatabaseDir.c_str());
1715 		BEntry superEntry;
1716 		CHK(rootDir.InitCheck() == B_OK);
1717 		rootDir.Rewind();
1718 		while (true) {
1719 			status_t err = rootDir.GetNextEntry(&superEntry);
1720 			if (err == B_ENTRY_NOT_FOUND)
1721 				break;	// End of directory listing
1722 
1723 			CHK(!err);	// Any other error is unacceptable :-)
1724 
1725 			// Get the leaf name
1726 			char superLeafMixed[B_PATH_NAME_LENGTH+1];
1727 			CHK(superEntry.GetName(superLeafMixed) == B_OK);
1728 			std::string superLeaf;
1729 			to_lower(superLeafMixed, superLeaf);
1730 
1731 			// We're only interested in directories, as they map to
1732 			// supertypes (and since they map thusly, they must also
1733 			// be valid MIME strings)
1734 			if (superEntry.IsDirectory() && BMimeType::IsValid(superLeaf.c_str())) {
1735 				// First, find and remove the supertype from our set
1736 				CHK(typeSet.find(superLeaf.c_str()) != typeSet.end());
1737 				typeSet.erase(superLeaf.c_str());
1738 
1739 				// Second, get the list of corresponding subtypes and add them
1740 				// to a std::set to be used for verification
1741 				BMessage msg;
1742 				CHK(BMimeType::GetInstalledTypes(superLeaf.c_str(), &msg) == B_OK);
1743 //				msg.PrintToStream();
1744 
1745 				std::set<std::string> subtypeSet;
1746 				SetAdapter subtypeAdapter(subtypeSet);
1747 				FillWithMimeTypes(subtypeAdapter, msg, "types");
1748 
1749 				// Third, iterate through all the entries in the directory.
1750 				// If the entry designates a valid MIME string, find it
1751 				// in the subtype set and remove it.
1752 				BDirectory superDir(&superEntry);
1753 				BEntry subEntry;
1754 				CHK(superDir.InitCheck() == B_OK);
1755 				superDir.Rewind();
1756 				while (true) {
1757 					status_t err = superDir.GetNextEntry(&subEntry);
1758 					if (err == B_ENTRY_NOT_FOUND)
1759 						break;	// End of directory listing
1760 
1761 					CHK(!err);	// Any other error is unacceptable :-)
1762 
1763 					// Get the leaf name
1764 					char subLeafMixed[B_PATH_NAME_LENGTH+1];
1765 					CHK(subEntry.GetName(subLeafMixed) == B_OK);
1766 					std::string subLeaf;
1767 					to_lower(subLeafMixed, subLeaf);
1768 
1769 					// Verify it's a valid mime string. If so, find and remove from our set
1770 					std::string subType = superLeaf + "/" + subLeaf;
1771 					if (BMimeType::IsValid(subType.c_str())) {
1772 						CHK(subtypeSet.find(subType.c_str()) != subtypeSet.end());
1773 						subtypeSet.erase(subType.c_str());
1774 					}
1775 				}
1776 
1777 				// At this point our subtype set should be empty :-)
1778 				CHK(subtypeSet.size() == 0);
1779 
1780 			}
1781 		}
1782 
1783 		// At this point our set should be empty :-)
1784 		CHK(typeSet.size() == 0);
1785 	}
1786 
1787 }
1788 
1789 void
InstalledTypesTest()1790 MimeTypeTest::InstalledTypesTest() {
1791 	// NULL params
1792 	{
1793 		BMessage msg;
1794 		NextSubTest();
1795 
1796 #if !TEST_R5
1797 		CHK(BMimeType::GetInstalledTypes(NULL) != B_OK);			// R5 == CRASH!!!, Haiku == B_BAD_VALUE
1798 #endif
1799 		NextSubTest();
1800 #if !TEST_R5
1801 		CHK(BMimeType::GetInstalledTypes("text", NULL) != B_OK);	// R5 == CRASH!!!, Haiku == B_BAD_VALUE
1802 #endif
1803 		NextSubTest();
1804 		CHK(BMimeType::GetInstalledTypes(NULL, &msg) == B_OK);		// Same as GetInstalledTypes(&msg)
1805 // 		msg.PrintToStream();
1806 		NextSubTest();
1807 #if !TEST_R5
1808 		CHK(BMimeType::GetInstalledTypes(NULL, NULL) != B_OK);		// R5 == CRASH!!!, Haiku == B_BAD_VALUE
1809 #endif
1810 		NextSubTest();
1811 #if !TEST_R5
1812 		CHK(BMimeType::GetInstalledSupertypes(NULL) != B_OK);		// R5 == CRASH!!!, Haiku == B_BAD_VALUE
1813 #endif
1814 	}
1815 	// Invalid supertype param to GetInstalledTypes(char *super, BMessage*)
1816 	{
1817 		BMessage msg;
1818 		NextSubTest();
1819 		CHK(!BMimeType::IsValid(testTypeSuperInvalid));
1820 		CHK(BMimeType::GetInstalledTypes(testTypeSuperInvalid, &msg) != B_OK);			// R5 == B_BAD_VALUE
1821 		NextSubTest();
1822 		CHK(BMimeType::IsValid(testTypeSuperValid));
1823 		CHK(BMimeType::GetInstalledTypes(testTypeSuperValid, &msg) != B_OK);	// R5 == B_ENTRY_NOT_FOUND
1824 	}
1825 	// Normal Function -- GetInstalledTypes(BMessage*)
1826 	// This test gets the list of installed types, then iterates through
1827 	// the actual database directory listings and verifies they're identical.
1828 	{
1829 		VerifyInstalledTypes();
1830 		BMimeType mime(testTypeApp1);
1831 		CHK(mime.InitCheck() == B_OK);
1832 		if (mime.IsInstalled()) {
1833 			CHK(mime.Delete() == B_OK);
1834 			VerifyInstalledTypes();
1835 			CHK(mime.Install() == B_OK);
1836 			VerifyInstalledTypes();
1837 		} else {
1838 			CHK(mime.Install() == B_OK);
1839 			VerifyInstalledTypes();
1840 			CHK(mime.Delete() == B_OK);
1841 			VerifyInstalledTypes();
1842 		}
1843 	}
1844 	// Normal Function -- GetInstalledSupertypes()/GetInstalledTypes(char*,BMessage*)
1845 	// This test gets the list of installed super types, then iterates through
1846 	// the actual database directory listings and verifies they're identical.
1847 
1848 }
1849 
1850 // Short Description
1851 
1852 void
ShortDescriptionTest()1853 MimeTypeTest::ShortDescriptionTest() {
1854 	DescriptionTest(&BMimeType::GetShortDescription, &BMimeType::SetShortDescription,
1855 #if TEST_R5
1856 					   NULL
1857 #else
1858 					   &BMimeType::DeleteShortDescription
1859 #endif
1860 	);
1861 }
1862 
1863 // Long Description
1864 
1865 void
LongDescriptionTest()1866 MimeTypeTest::LongDescriptionTest() {
1867 	DescriptionTest(&BMimeType::GetLongDescription, &BMimeType::SetLongDescription,
1868 #if TEST_R5
1869 					   NULL
1870 #else
1871 					   &BMimeType::DeleteLongDescription
1872 #endif
1873 	);
1874 }
1875 
1876 // DescriptionTest Helper Function
1877 void
DescriptionTest(GetDescriptionFunc getDescr,SetDescriptionFunc setDescr,DeleteDescriptionFunc deleteDescr)1878 MimeTypeTest::DescriptionTest(GetDescriptionFunc getDescr, SetDescriptionFunc setDescr,
1879 							    DeleteDescriptionFunc deleteDescr)
1880 {
1881 	char str[B_MIME_TYPE_LENGTH+1];
1882 
1883 	// Uninitialized
1884 	NextSubTest();
1885 	{
1886 		str[0] = 0;
1887 		BMimeType mime;
1888 		CPPUNIT_ASSERT(mime.InitCheck() == B_NO_INIT);
1889 		CPPUNIT_ASSERT((mime.*getDescr)(str) != B_OK);	// R5 == B_BAD_VALUE
1890 		CPPUNIT_ASSERT((mime.*setDescr)(str) != B_OK);	// R5 == B_BAD_VALUE
1891 	}
1892 	// Non-installed type
1893 	NextSubTest();
1894 	{
1895 		str[0] = 0;
1896 		BMimeType mime(testType);
1897 		CHK(mime.InitCheck() == B_OK);
1898 		// Make sure the type isn't installed
1899 		if (mime.IsInstalled())
1900 			CHK(mime.Delete() == B_OK);
1901 		CHK(!mime.IsInstalled());
1902 		CHK((mime.*getDescr)(str) != B_OK);		// R5 == B_ENTRY_NOT_FOUND
1903 		CHK(!mime.IsInstalled());
1904 		CHK((mime.*setDescr)(testDescr) == B_OK);	// R5 == Installs (but doesn't set), B_OK
1905 		CHK(mime.IsInstalled());
1906 		CHK((mime.*getDescr)(str) == B_OK);
1907 		CHK(strcmp(str, testDescr) == 0);
1908 	}
1909 	// Non-installed type, NULL params
1910 	NextSubTest();
1911 	{
1912 #if !TEST_R5	// NOTE: These tests crash for R5::LongDescription calls but not for R5::ShortDescription
1913 				// calls. Considering the general instability exihibited by most R5 calls when passed
1914 				// NULL pointers, however, I wouldn't suggest it, and thus they aren't even tested here.
1915 		BMimeType mime(testType);
1916 		CHK(mime.InitCheck() == B_OK);
1917 		// Make sure the type isn't installed
1918 		if (mime.IsInstalled())
1919 			CHK(mime.Delete() == B_OK);
1920 		CHK(!mime.IsInstalled());
1921 		CHK((mime.*getDescr)(NULL) == B_BAD_VALUE);
1922 		CHK(!mime.IsInstalled());
1923 		CHK((mime.*setDescr)(NULL) == B_ENTRY_NOT_FOUND);	// Trying to delete non-existent attribute
1924 		CHK(!mime.IsInstalled());
1925 		CHK((mime.*setDescr)(testDescr) == B_OK);
1926 		CHK(mime.IsInstalled());
1927 		str[0] = 0;
1928 		CHK((mime.*getDescr)(str) == B_OK);
1929 		CHK(strcmp(str, testDescr) == 0);
1930 		CHK((mime.*setDescr)(NULL) == B_OK);	// Delete the attribute
1931 		CHK(mime.IsInstalled());
1932 		CHK((mime.*getDescr)(str) == B_ENTRY_NOT_FOUND);
1933 #endif
1934 	}
1935 	// Delete test
1936 	NextSubTest();
1937 	{
1938 #if !TEST_R5
1939 		entry_ref ref;
1940 		BMimeType mime(testType);
1941 		CHK(mime.InitCheck() == B_OK);
1942 		// Make sure the type isn't installed
1943 		if (mime.IsInstalled())
1944 			CHK(mime.Delete() == B_OK);
1945 		CHK(!mime.IsInstalled());
1946 		CHK((mime.*deleteDescr)() != B_OK);
1947 		CHK(!mime.IsInstalled());
1948 		CHK((mime.*setDescr)(testDescr) == B_OK);
1949 		CHK(mime.IsInstalled());
1950 		str[0] = 0;
1951 		CHK((mime.*getDescr)(str) == B_OK);
1952 		CHK(strcmp(str, testDescr) == 0);
1953 		CHK((mime.*deleteDescr)() == B_OK);
1954 		CHK(mime.IsInstalled());
1955 		CHK((mime.*getDescr)(str) != B_OK);
1956 #endif
1957 	}
1958 	// Installed type
1959 	NextSubTest();
1960 	{
1961 		str[0] = 0;
1962 		BMimeType mime(testType);
1963 		CHK(mime.InitCheck() == B_OK);
1964 		// Uninstall then reinstall to clear attributes
1965 		if (mime.IsInstalled())
1966 			CHK(mime.Delete() == B_OK);
1967 		if (!mime.IsInstalled())
1968 			CHK(mime.Install() == B_OK);
1969 		// Get() with no description installed
1970 		CHK(mime.IsInstalled());
1971 		CHK((mime.*getDescr)(str) == B_ENTRY_NOT_FOUND);	// R5 == B_ENTRY_NOT_FOUND
1972 		// Initial Set()/Get()
1973 		CHK((mime.*setDescr)(testDescr) == B_OK);
1974 		CHK((mime.*getDescr)(str) == B_OK);
1975 		CHK(strcmp(str, testDescr) == 0);
1976 		// Followup Set()/Get()
1977 		CHK((mime.*setDescr)(testDescr2) == B_OK);
1978 		CHK((mime.*getDescr)(str) == B_OK);
1979 		CHK(strcmp(str, testDescr2) == 0);
1980 	}
1981 	// Installed Type, Description Too Long
1982 	NextSubTest();
1983 	{
1984 		str[0] = 0;
1985 		CHK(strlen(longDescr) > (B_MIME_TYPE_LENGTH+1));
1986 		BMimeType mime(testType);
1987 		CHK(mime.InitCheck() == B_OK);
1988 		// Uninstall then reinstall to clear attributes
1989 		if (mime.IsInstalled())
1990 			CHK(mime.Delete() == B_OK);
1991 		if (!mime.IsInstalled())
1992 			CHK(mime.Install() == B_OK);
1993 		// Initial Set()/Get()
1994 		CHK((mime.*setDescr)(longDescr) != B_OK);		// R5 == B_BAD_VALUE
1995 		CHK((mime.*getDescr)(str) == B_ENTRY_NOT_FOUND);
1996 		// Followup Set()/Get()
1997 		CHK((mime.*setDescr)(testDescr) == B_OK);
1998 		CHK((mime.*setDescr)(longDescr) != B_OK);		// R5 == B_BAD_VALUE
1999 		CHK((mime.*getDescr)(str) == B_OK);
2000 		CHK(strcmp(str, testDescr) == 0);
2001 	}
2002 
2003 }
2004 
2005 
2006 // Preferred App
2007 
2008 void
PreferredAppTest()2009 MimeTypeTest::PreferredAppTest() {
2010 	char str[B_MIME_TYPE_LENGTH+1];
2011 	sprintf(str, "%s", testSig);
2012 
2013 	// Uninitialized
2014 	NextSubTest();
2015 	{
2016 		BMimeType mime;
2017 		CPPUNIT_ASSERT(mime.InitCheck() == B_NO_INIT);
2018 		CPPUNIT_ASSERT(mime.GetPreferredApp(str) != B_OK);	// R5 == B_BAD_VALUE
2019 		CPPUNIT_ASSERT(mime.SetPreferredApp(str) != B_OK);	// R5 == B_BAD_VALUE
2020 	}
2021 	// Non-installed type
2022 	NextSubTest();
2023 	{
2024 		BMimeType mime(testType);
2025 		CHK(mime.InitCheck() == B_OK);
2026 		// Make sure the type isn't installed
2027 		if (mime.IsInstalled())
2028 			CHK(mime.Delete() == B_OK);
2029 		CHK(!mime.IsInstalled());
2030 		CHK(mime.GetPreferredApp(str) != B_OK);		// R5 == B_ENTRY_NOT_FOUND
2031 		CHK(!mime.IsInstalled());
2032 		CHK(mime.SetPreferredApp(testSig) == B_OK);	// R5 == Installs (but doesn't set), B_OK
2033 		CHK(mime.IsInstalled());
2034 		CHK(mime.GetPreferredApp(str) == B_OK);
2035 		CHK(strcmp(str, testSig) == 0);
2036 	}
2037 	// Non-installed type, NULL params
2038 	NextSubTest();
2039 	{
2040 		BMimeType mime(testType);
2041 		CHK(mime.InitCheck() == B_OK);
2042 		// Make sure the type isn't installed
2043 		if (mime.IsInstalled())
2044 			CHK(mime.Delete() == B_OK);
2045 #if TEST_R5
2046 		CHK(!mime.IsInstalled());
2047 		CHK(mime.GetPreferredApp(NULL) != B_OK);		// R5 == B_ENTRY_NOT_FOUND
2048 		CHK(!mime.IsInstalled());
2049 		CHK(mime.SetPreferredApp(NULL) != B_OK);		// R5 == Installs (but doesn't set), B_ENTRY_NOT_FOUND
2050 		CHK(mime.IsInstalled());
2051 		CHK(mime.GetPreferredApp(str) == B_ENTRY_NOT_FOUND);
2052 #else
2053 		CHK(!mime.IsInstalled());
2054 		CHK(mime.GetPreferredApp(NULL) != B_OK);		// Haiku == B_BAD_VALUE
2055 		CHK(!mime.IsInstalled());
2056 		CHK(mime.SetPreferredApp(NULL) != B_OK);		// Haiku == B_ENTRY_NOT_FOUND
2057 		CHK(!mime.IsInstalled());
2058 		CHK(mime.SetPreferredApp(testSig) == B_OK);
2059 		CHK(mime.IsInstalled());
2060 		str[0] = 0;
2061 		CHK(mime.GetPreferredApp(str) == B_OK);
2062 		CHK(strcmp(str, testSig) == 0);
2063 		CHK(mime.SetPreferredApp(NULL) == B_OK);
2064 		CHK(mime.IsInstalled());
2065 		str[0] = 0;
2066 		CHK(mime.GetPreferredApp(str) != B_OK);			// Haiku == B_ENTRY_NOT_FOUND
2067 #endif // !TEST_R5
2068 	}
2069 	// Installed type, NULL params
2070 	NextSubTest();
2071 	{
2072 		BMimeType mime(testType);
2073 		CHK(mime.InitCheck() == B_OK);
2074 		// Uninstall then reinstall to clear attributes
2075 		if (mime.IsInstalled())
2076 			CHK(mime.Delete() == B_OK);
2077 		if (!mime.IsInstalled())
2078 			CHK(mime.Install() == B_OK);
2079 		CHK(mime.IsInstalled());
2080 		CHK(mime.GetPreferredApp(NULL) != B_OK);		// R5 == B_BAD_ADDRESS
2081 		CHK(mime.SetPreferredApp(NULL) != B_OK);		// R5 == B_ENTRY_NOT_FOUND
2082 		CHK(mime.GetPreferredApp(NULL) != B_OK);		// R5 == B_BAD_ADDRESS
2083 	}
2084 	// Delete test
2085 	NextSubTest();
2086 	{
2087 #if !TEST_R5
2088 		entry_ref ref;
2089 		BMimeType mime(testType);
2090 		CHK(mime.InitCheck() == B_OK);
2091 		// Make sure the type isn't installed
2092 		if (mime.IsInstalled())
2093 			CHK(mime.Delete() == B_OK);
2094 		CHK(!mime.IsInstalled());
2095 		CHK(mime.DeletePreferredApp() != B_OK);
2096 		CHK(!mime.IsInstalled());
2097 		CHK(mime.SetPreferredApp(testSig) == B_OK);
2098 		CHK(mime.IsInstalled());
2099 		str[0] = 0;
2100 		CHK(mime.GetPreferredApp(str) == B_OK);
2101 		CHK(strcmp(str, testSig) == 0);
2102 		CHK(mime.DeletePreferredApp() == B_OK);
2103 		CHK(mime.IsInstalled());
2104 		CHK(mime.GetPreferredApp(str) != B_OK);
2105 #endif
2106 	}
2107 	// Installed type
2108 	NextSubTest();
2109 	{
2110 		BMimeType mime(testType);
2111 		CHK(mime.InitCheck() == B_OK);
2112 		// Uninstall then reinstall to clear attributes
2113 		if (mime.IsInstalled())
2114 			CHK(mime.Delete() == B_OK);
2115 		if (!mime.IsInstalled())
2116 			CHK(mime.Install() == B_OK);
2117 		// Get() with no description installed
2118 		CHK(mime.IsInstalled());
2119 		CHK(mime.GetPreferredApp(str) == B_ENTRY_NOT_FOUND);	// R5 == B_ENTRY_NOT_FOUND
2120 		// Initial Set()/Get()
2121 		CHK(mime.SetPreferredApp(testSig) == B_OK);
2122 		CHK(mime.GetPreferredApp(str) == B_OK);
2123 		CHK(strcmp(str, testSig) == 0);
2124 		// Followup Set()/Get()
2125 		CHK(mime.SetPreferredApp(testSig2) == B_OK);
2126 		CHK(mime.GetPreferredApp(str) == B_OK);
2127 		CHK(strcmp(str, testSig2) == 0);
2128 	}
2129 	// Installed Type, Signature Too Long
2130 	NextSubTest();
2131 	{
2132 		CHK(strlen(longDescr) > (B_MIME_TYPE_LENGTH+1));
2133 		BMimeType mime(testType);
2134 		CHK(mime.InitCheck() == B_OK);
2135 		// Uninstall then reinstall to clear attributes
2136 		if (mime.IsInstalled())
2137 			CHK(mime.Delete() == B_OK);
2138 		if (!mime.IsInstalled())
2139 			CHK(mime.Install() == B_OK);
2140 		// Initial Set()/Get()
2141 		CHK(mime.SetPreferredApp(longSig) != B_OK);		// R5 == B_BAD_VALUE
2142 		CHK(mime.GetPreferredApp(str) == B_ENTRY_NOT_FOUND);
2143 		// Followup Set()/Get()
2144 		CHK(mime.SetPreferredApp(testSig) == B_OK);
2145 		CHK(mime.SetPreferredApp(longSig) != B_OK);		// R5 == B_BAD_VALUE
2146 		CHK(mime.GetPreferredApp(str) == B_OK);
2147 		CHK(strcmp(str, testSig) == 0);
2148 	}
2149 
2150 }
2151 
2152 // Converts every character in str to lowercase and places
2153 // the result in result.
2154 void
to_lower(const char * str,std::string & result)2155 to_lower(const char *str, std::string &result) {
2156 	CHK(str != NULL);
2157 	result = "";
2158 	for (uint i = 0; i < strlen(str); i++)
2159 		result += tolower(str[i]);
2160 }
2161 
2162 std::string
to_lower(const char * str)2163 to_lower(const char *str) {
2164 	std::string result;
2165 	to_lower(str, result);
2166 	return result;
2167 }
2168 
2169 
2170 // Manually removes the file in the MIME database corresponding to
2171 // the given MIME type
2172 void
remove_type(const char * type,const char * databaseDir)2173 remove_type(const char *type, const char *databaseDir) {
2174 	CHK(type != NULL);
2175 
2176 	// Since the MIME types are converted to lower case before their
2177 	// corresponding file is created in the database, we need to do
2178 	// the same
2179 	std::string typeLower;
2180 	to_lower(type, typeLower);
2181 
2182 	BEntry entry((std::string(mimeDatabaseDir) + "/" + typeLower).c_str());
2183 	CHK(entry.InitCheck() == B_OK);
2184 	if (entry.Exists())
2185 		CHK(entry.Remove() == B_OK);
2186 	CHK(!entry.Exists());
2187 }
2188 
2189 // Manually verifies that the file in the MIME database corresponding to
2190 // the given MIME type exists
2191 bool
type_exists(const char * type,const char * databaseDir)2192 type_exists(const char *type, const char *databaseDir) {
2193 	CHK(type != NULL);
2194 
2195 	// Since the MIME types are converted to lower case before their
2196 	// corresponding file is created in the database, we need to do
2197 	// the same
2198 	std::string typeLower;
2199 	to_lower(type, typeLower);
2200 
2201 	BEntry entry((std::string(databaseDir) + "/" + typeLower).c_str());
2202 	CHK(entry.InitCheck() == B_OK);
2203 	return entry.Exists();
2204 }
2205 
2206 void
InstallDeleteTest()2207 MimeTypeTest::InstallDeleteTest() {
2208 	// Uninitialzized
2209 	NextSubTest();
2210 	{
2211 		BMimeType mime;
2212 		CHK(mime.InitCheck() == B_NO_INIT);
2213 		CHK(!mime.IsInstalled());
2214 		CHK(mime.Install() != B_OK);	// R5 == B_BAD_VALUE
2215 		CHK(mime.Delete() != B_OK);	// R5 == B_BAD_VALUE
2216 	}
2217 	// Invalid Type String
2218 	NextSubTest();
2219 	{
2220 		BMimeType mime(testTypeInvalid);
2221 		CHK(mime.InitCheck() != B_OK);	// R5 == B_BAD_VALUE
2222 		CHK(!mime.IsInstalled());
2223 		CHK(mime.Install() != B_OK);	// R5 == B_BAD_VALUE
2224 		CHK(mime.Delete() != B_OK);	// R5 == B_BAD_VALUE
2225 	}
2226 	// Normal function
2227 	NextSubTest();
2228 	{
2229 		remove_type(testType);
2230 		BMimeType mime(testType);
2231 		CHK(mime.InitCheck() == B_OK);
2232 		CHK(!mime.IsInstalled());
2233 		CHK(mime.Delete() != B_OK);	// R5 == B_ENTRY_NOT_FOUND, Haiku == B_ENTRY_NOT_FOUND
2234 		CHK(!type_exists(testType));
2235 		CHK(mime.Install() == B_OK);
2236 		CHK(type_exists(testType));
2237 		CHK(mime.IsInstalled());
2238 #if !TEST_R5
2239 		CHK(mime.Install() != B_OK);	// We ought to return something standard and logical here, as R5 is random
2240 #endif
2241 		CHK(mime.Delete() == B_OK);
2242 		CHK(!type_exists(testType));
2243 		CHK(!mime.IsInstalled());
2244 	}
2245 
2246 }
2247 
FillWithMimeTypes(ContainerAdapter & container,BMessage & typeMessage,const char * fieldName)2248 void FillWithMimeTypes(ContainerAdapter &container, BMessage &typeMessage, const char* fieldName) {
2249 	type_code type;
2250 	int32 count;
2251 	status_t err;
2252 
2253 //	typeMessage.PrintToStream();
2254 
2255 	// Get a count of types in the message
2256 	err = typeMessage.GetInfo(fieldName, &type, &count);
2257 	if (err == B_NAME_NOT_FOUND)
2258 		count = 0;			// No such types installed in the database! :-)
2259 	else
2260 		CHK(err == B_OK);	// Any other error is unacceptable
2261 
2262 	// Add them all to the container, after converting to lowercase and
2263 	// checking validity
2264 	for (int i = 0; i < count; i++) {
2265 		char *str;
2266 		CHK(typeMessage.FindString(fieldName, i, (const char**)&str) == B_OK);
2267 		std::string strLower;
2268 		to_lower(str, strLower);
2269 		// Make sure it's a valid type string, since the R5::GetInstalled*Types()
2270 		// functions do no such verification, and we ignore invalid type files
2271 		// in the database.
2272 		if (BMimeType::IsValid(strLower.c_str()))
2273 			container.Add(strLower);
2274 	}
2275 }
2276 
2277 bool
types_fields_are_identical(const BMessage & msg1,const BMessage & msg2)2278 types_fields_are_identical(const BMessage &msg1, const BMessage &msg2)
2279 {
2280 	const char *str1;
2281 	const char *str2;
2282 	int i = 0;
2283 	bool result = true;
2284 	while (true) {
2285 		status_t err1 = msg1.FindString(typesField, i, &str1);
2286 		status_t err2 = msg2.FindString(typesField, i, &str2);
2287 		if (err1 != err2) {
2288 			result = false;
2289 			break;
2290 		}
2291 		if (err1 != B_OK)
2292 			break;
2293 		result &= to_lower(str1) == to_lower(str2);
2294 		i++;
2295 	}
2296 	return result;
2297 }
2298 
2299 bool
is_supporting_app_for_all_types_in_message(const char * app,const BMessage & msg)2300 is_supporting_app_for_all_types_in_message(const char *app, const BMessage &msg)
2301 {
2302 	const char *str;
2303 	bool result = true;
2304 	for (int i = 0; msg.FindString(typesField, i, &str) == B_OK; i++) {
2305 		BMimeType supportedType(str);
2306 		BMessage appMsg;
2307 
2308 		// Get a list of supporting apps
2309 		CHK(supportedType.InitCheck() == B_OK);
2310 		CHK(supportedType.GetSupportingApps(&appMsg) == B_OK);
2311 //		cout << "-----------------------------------------------------------" << endl;
2312 //		cout << str << endl;
2313 //		cout << "-----------------------------------------------------------" << endl;
2314 //		appMsg.PrintToStream();
2315 
2316 		// Look for our supporting app
2317 		int32 directlySupportingAppsCount;
2318 		CHK(appMsg.FindInt32("be:sub", &directlySupportingAppsCount) == B_OK);
2319 		bool foundType = false;
2320 		const char *supportingApp;
2321 		for (int j = 0;
2322 			   j < directlySupportingAppsCount
2323 			     && appMsg.FindString(applicationsField, &supportingApp) == B_OK;
2324 			       j++)
2325 		{
2326 			foundType |= to_lower(app) == to_lower(supportingApp);
2327 		}
2328 		result &= foundType;
2329 	}
2330 	return result;
2331 }
2332 
2333 void
SupportedTypesTest()2334 MimeTypeTest::SupportedTypesTest() {
2335 #if TEST_R5
2336 	Outputf("(no tests actually performed for R5 version)\n");
2337 #else
2338 	// Create some messages to sling around
2339 	const int32 WHAT = 0;
2340 	BMessage msg1a(WHAT), msg1b(WHAT), msg2(WHAT), msg3(WHAT), msgEmpty(WHAT);
2341 
2342 	CHK(msg3.AddString(typesField, testType1) == B_OK);
2343 	CHK(msg3.AddString(typesField, testType2) == B_OK);
2344 	CHK(msg3.AddString(typesField, testType3) == B_OK);
2345 
2346 	CHK(msg2.AddString(typesField, testType2) == B_OK);
2347 	CHK(msg2.AddString(typesField, testType3) == B_OK);
2348 
2349 	CHK(msg1a.AddString(typesField, testType5) == B_OK);
2350 
2351 	CHK(msg1b.AddString(typesField, testType2) == B_OK);
2352 
2353 	CHK(msg1a == msg1a);
2354 	CHK(msg1b == msg1b);
2355 	CHK(msg2 == msg2);
2356 	CHK(msg3 == msg3);
2357 	CHK(msg1a != msg2);
2358 	CHK(msg1a != msg3);
2359 	CHK(msg1a != msgEmpty);
2360 
2361 	// Uninitialized
2362 	NextSubTest();
2363 	{
2364 		BMimeType mime;
2365 		BMessage msg;
2366 
2367 		CHK(mime.InitCheck() == B_NO_INIT);
2368 		CHK(mime.SetSupportedTypes(&msg, true) != B_OK);
2369 		CHK(mime.SetSupportedTypes(&msg, false) != B_OK);
2370 		CHK(mime.GetSupportedTypes(&msg) != B_OK);
2371 		CHK(mime.DeleteSupportedTypes() != B_OK);
2372 	}
2373 
2374 	// Test that deleting a type from the database also removes
2375 	// the app as a supporting app for all types it previously
2376 	// supported
2377 	NextSubTest();
2378 	{
2379 		BMessage msg;
2380 		BMimeType mime(testType);
2381 		CHK(mime.InitCheck() == B_OK);
2382 		if (!mime.IsInstalled())
2383 			CHK(mime.Install() == B_OK);
2384 
2385 		// Set a list of supported types
2386 		CHK(mime.SetSupportedTypes(&msg3, true) == B_OK);
2387 
2388 		// Verify that each of those types now lists the
2389 		// type as a directly supporting app
2390 		CHK(is_supporting_app_for_all_types_in_message(testType, msg3) == true);
2391 
2392 		// Delete the type
2393 		CHK(mime.Delete() == B_OK);
2394 
2395 		// Verify that each of those types no longer lists the
2396 		// type as a directly supporting app
2397 		CHK(is_supporting_app_for_all_types_in_message(testType, msg3) == false);
2398 	}
2399 
2400 	// Test that SetSupportedTypes(..., false) does not remove the app as a supporting
2401 	// app for newly unsupported types, while SetSupportedTypes(..., true) does. Also
2402 	// test that supported types stranded by multiple sequential calls to
2403 	// SetSupportedTypes(..., false) are properly updated so as to no longer list the
2404 	// app as a supporting app once SetSupportedTypes(..., true) is finally called.
2405 	NextSubTest();
2406 	{
2407 		BMessage msg;
2408 		BMimeType mime(testType);
2409 		CHK(mime.InitCheck() == B_OK);
2410 
2411 		// Uninstall then reinstall to clear attributes, etc
2412 		if (mime.IsInstalled())
2413 			CHK(mime.Delete() == B_OK);
2414 		if (!mime.IsInstalled())
2415 			CHK(mime.Install() == B_OK);
2416 		CHK(mime.IsInstalled());
2417 
2418 		// Set a list of supported types
2419 		CHK(mime.SetSupportedTypes(&msg3, true) == B_OK);
2420 		CHK(mime.GetSupportedTypes(&msg) == B_OK);
2421 		CHK(types_fields_are_identical(msg3, msg) == true);
2422 		CHK(types_fields_are_identical(msg2, msg) == false);
2423 		CHK(types_fields_are_identical(msg1a, msg) == false);
2424 		CHK(types_fields_are_identical(msg1b, msg) == false);
2425 		CHK(types_fields_are_identical(msgEmpty, msg) == false);
2426 
2427 		// Verify that each of those types now lists the
2428 		// type as a directly supporting app
2429 		CHK(is_supporting_app_for_all_types_in_message(testType, msg3) == true);
2430 		CHK(is_supporting_app_for_all_types_in_message(testType, msg2) == true);
2431 		CHK(is_supporting_app_for_all_types_in_message(testType, msg1a) == false);
2432 		CHK(is_supporting_app_for_all_types_in_message(testType, msg1b) == true);
2433 
2434 		// Set (no sync) to a new list of supported types containing one
2435 		// fewer type than the original list
2436 		CHK(mime.SetSupportedTypes(&msg2, false) == B_OK);
2437 		CHK(mime.GetSupportedTypes(&msg) == B_OK);
2438 		CHK(types_fields_are_identical(msg3, msg) == false);
2439 		CHK(types_fields_are_identical(msg2, msg) == true);
2440 		CHK(types_fields_are_identical(msg1a, msg) == false);
2441 		CHK(types_fields_are_identical(msg1b, msg) == false);
2442 		CHK(types_fields_are_identical(msgEmpty, msg) == false);
2443 
2444 		// Verify that the app is still listed as a supporting app for
2445 		// *all* of the originally supported types (even the one no longer
2446 		// listed as being supported)
2447 		CHK(is_supporting_app_for_all_types_in_message(testType, msg3) == true);
2448 		CHK(is_supporting_app_for_all_types_in_message(testType, msg2) == true);
2449 		CHK(is_supporting_app_for_all_types_in_message(testType, msg1a) == false);
2450 		CHK(is_supporting_app_for_all_types_in_message(testType, msg1b) == true);
2451 
2452 		// Set (no sync) to a new list of supported types containing an
2453 		// entirely new, never supported type
2454 		CHK(mime.SetSupportedTypes(&msg1a, false) == B_OK);
2455 		CHK(mime.GetSupportedTypes(&msg) == B_OK);
2456 		CHK(types_fields_are_identical(msg3, msg) == false);
2457 		CHK(types_fields_are_identical(msg2, msg) == false);
2458 		CHK(types_fields_are_identical(msg1a, msg) == true);
2459 		CHK(types_fields_are_identical(msg1b, msg) == false);
2460 		CHK(types_fields_are_identical(msgEmpty, msg) == false);
2461 
2462 		// Verify that the app is still listed as a supporting app for
2463 		// *all* of the originally supported types (none of which are
2464 		// supported any longer) as well as the newly supported type.
2465 		CHK(is_supporting_app_for_all_types_in_message(testType, msg3) == true);
2466 		CHK(is_supporting_app_for_all_types_in_message(testType, msg2) == true);
2467 		CHK(is_supporting_app_for_all_types_in_message(testType, msg1a) == true);
2468 		CHK(is_supporting_app_for_all_types_in_message(testType, msg1b) == true);
2469 
2470 		// Set (no sync) to a new list of supported types containing only
2471 		// one of the originally supported types that had been previously
2472 		// removed.
2473 		CHK(mime.SetSupportedTypes(&msg1b, false) == B_OK);
2474 		CHK(mime.GetSupportedTypes(&msg) == B_OK);
2475 		CHK(types_fields_are_identical(msg3, msg) == false);
2476 		CHK(types_fields_are_identical(msg2, msg) == false);
2477 		CHK(types_fields_are_identical(msg1a, msg) == false);
2478 		CHK(types_fields_are_identical(msg1b, msg) == true);
2479 		CHK(types_fields_are_identical(msgEmpty, msg) == false);
2480 
2481 		// Verify that the app is still listed as a supporting app for
2482 		// *all* of the originally supported types (only one of which is
2483 		// supported any longer) as well as the previous supported type.
2484 		CHK(is_supporting_app_for_all_types_in_message(testType, msg3) == true);
2485 		CHK(is_supporting_app_for_all_types_in_message(testType, msg2) == true);
2486 		CHK(is_supporting_app_for_all_types_in_message(testType, msg1a) == true);
2487 		CHK(is_supporting_app_for_all_types_in_message(testType, msg1b) == true);
2488 
2489 		// Set (with sync) to the same list as last time (containing the
2490 		// one type from the original list of supported types)
2491 		CHK(mime.SetSupportedTypes(&msg1b, true) == B_OK);
2492 		CHK(mime.GetSupportedTypes(&msg) == B_OK);
2493 		CHK(types_fields_are_identical(msg3, msg) == false);
2494 		CHK(types_fields_are_identical(msg2, msg) == false);
2495 		CHK(types_fields_are_identical(msg1a, msg) == false);
2496 		CHK(types_fields_are_identical(msg1b, msg) == true);
2497 		CHK(types_fields_are_identical(msgEmpty, msg) == false);
2498 
2499 		// Verify that the app is now only listed as a supporting app for the
2500 		// most recently supported type.
2501 		CHK(is_supporting_app_for_all_types_in_message(testType, msg3) == false);
2502 		CHK(is_supporting_app_for_all_types_in_message(testType, msg2) == false);
2503 		CHK(is_supporting_app_for_all_types_in_message(testType, msg1a) == false);
2504 		CHK(is_supporting_app_for_all_types_in_message(testType, msg1b) == true);
2505 
2506 		// Test SetSupportedTypes(NULL, false) for shits and giggles
2507 		CHK(mime.SetSupportedTypes(NULL, false) == B_OK);
2508 		CHK(mime.GetSupportedTypes(&msg) == B_ENTRY_NOT_FOUND);
2509 		CHK(is_supporting_app_for_all_types_in_message(testType, msg3) == false);
2510 		CHK(is_supporting_app_for_all_types_in_message(testType, msg2) == false);
2511 		CHK(is_supporting_app_for_all_types_in_message(testType, msg1a) == false);
2512 		CHK(is_supporting_app_for_all_types_in_message(testType, msg1b) == true);
2513 
2514 		// Now test that SetSupportedTypes(NULL, true) updates the supporting
2515 		// apps mappings, even if the supported types attribute has already
2516 		// been removed.
2517 		CHK(mime.SetSupportedTypes(NULL, true) == B_ENTRY_NOT_FOUND);
2518 		CHK(mime.GetSupportedTypes(&msg) == B_ENTRY_NOT_FOUND);
2519 		CHK(is_supporting_app_for_all_types_in_message(testType, msg3) == false);
2520 		CHK(is_supporting_app_for_all_types_in_message(testType, msg2) == false);
2521 		CHK(is_supporting_app_for_all_types_in_message(testType, msg1a) == false);
2522 		CHK(is_supporting_app_for_all_types_in_message(testType, msg1b) == false);
2523 	}
2524 #endif	// #if TEST_R5 else
2525 }
2526 
2527 void
SupportingAppsTest()2528 MimeTypeTest::SupportingAppsTest() {
2529 /*	{
2530 		BMessage msg;
2531 		BMimeType::GetInstalledTypes(&msg);
2532 		msg.PrintToStream();
2533 	}
2534 	{
2535 		BMessage msg;
2536 		BMimeType mime("application/octet-stream");
2537 		CHK(mime.InitCheck() == B_OK);
2538 		CHK(mime.GetSupportingApps(&msg) == B_OK);
2539 		msg.PrintToStream();
2540 	}
2541 	{
2542 		BMessage msg;
2543 		BMimeType mime("text");
2544 		CHK(mime.InitCheck() == B_OK);
2545 		CHK(mime.GetSupportingApps(&msg) == B_OK);
2546 		msg.PrintToStream();
2547 	}
2548 	{
2549 		BMessage msg;
2550 		BMimeType mime("text/html");
2551 		CHK(mime.InitCheck() == B_OK);
2552 		CHK(mime.GetSupportingApps(&msg) == B_OK);
2553 		msg.PrintToStream();
2554 	} */
2555 	NextSubTest();
2556 	if (true)
2557 	{
2558 		std::set<std::string> typeList;							// Stores all installed MIME types
2559 		std::set<std::string> appList;							// Stores all installed application subtypes
2560 		std::map< std::string, std::set<std::string> > typeAppMap;	// Stores mapping of types to apps that support them
2561 		std::map< std::string, std::set<std::string> > fakeTypeAppMap;	// Used to keep timing info for R5 and Haiku tests orthogonal
2562 
2563 		// Get a list of all the types in the database
2564 		{
2565 			BMessage msg;
2566 			CHK(BMimeType::GetInstalledTypes(&msg) == B_OK);
2567 			SetAdapter typeAdapter(typeList);
2568 			FillWithMimeTypes(typeAdapter, msg, "types");
2569 		}
2570 
2571 		// Get a list of all the apps in the database
2572 		{
2573 			BMessage msg;
2574 			CHK(BMimeType::GetInstalledTypes(applicationSupertype, &msg) == B_OK);
2575 			SetAdapter appAdapter(appList);
2576 			FillWithMimeTypes(appAdapter, msg, "types");
2577 		}
2578 
2579 		// For each app in the database, manually get a list of the MIME types
2580 		// it supports by reading its META:FILE_TYPES attribute from the database,
2581 		// and add the app to the type->app map for each such type
2582 		{
2583 			std::set<std::string>::iterator i;
2584 			for (i = appList.begin(); i != appList.end(); i++) {
2585 				// Grab the next application
2586 				std::string app = *i;
2587 
2588 				// The leaf is all we're interested in -- it's the subtype
2589 //				CHK(StorageKit::split_path(app.c_str(), dir, leaf) == B_OK);
2590 				std::string appFile = std::string(mimeDatabaseDir) + "/" + app;
2591 //				printf("'%s'\n", appFile.c_str());
2592 				BNode node(appFile.c_str());
2593 				CHK(node.InitCheck() == B_OK);
2594 
2595 				// Find out how much data there is in the META:FILE_TYPES attribute
2596 				// (assuming it even exists, which it may not...)
2597 				attr_info info;
2598 				if (node.GetAttrInfo("META:FILE_TYPES", &info) == B_OK) {
2599 //					printf("attr_info: type == %lx, size == %lld\n", info.type, info.size);
2600 					// Attribute exists, so alloc a buffer and read it
2601 					char *buffer = new char[info.size+1];
2602 					CHK(node.ReadAttr("META:FILE_TYPES", B_MESSAGE_TYPE, 0, buffer, info.size) == info.size);
2603 					BMessage msg;
2604 					if (msg.Unflatten(buffer) == B_OK) {
2605 //						msg.PrintToStream();
2606 
2607 						// Fill up a list with all the supported types
2608 						std::set<std::string> supportList;
2609 						SetAdapter supportAdapter(supportList);
2610 						FillWithMimeTypes(supportAdapter, msg, "types");
2611 
2612 						// For each type, add the current application as a supporting
2613 						// app in our type->apps map
2614 						for (std::set<std::string>::iterator type = supportList.begin();
2615 								type != supportList.end();
2616 									type++)
2617 						{
2618 							NextSubTest();
2619 							typeAppMap[*type].insert(app);
2620 							fakeTypeAppMap[*type].insert(app);
2621 						}
2622 					} else {
2623 						// Just in case some bozo writes something other than a flattened
2624 						// BMessage to the META:FILE_TYPES attribute, we'll only issue a
2625 						// warning when the BMessage can't unflatten itself
2626 						printf("Warning: Unable to unflatten META:FILE_TYPES attribute for '%s' type.\n",
2627 							app.c_str());
2628 					}
2629 
2630 					delete buffer;
2631 
2632 				}
2633 			}
2634 		}
2635 
2636 //#if !TEST_R5
2637 		// Now, add in all the types listed in MIME_DB_DIR/__mime_table
2638 		{
2639 			BEntry entry((std::string(mimeDatabaseDir) + "/__mime_table").c_str());
2640 			CHK(entry.InitCheck() == B_OK);
2641 			if (entry.Exists()) {
2642 				BFile file(&entry, B_READ_ONLY);
2643 				CHK(file.InitCheck() == B_OK);
2644 				BMessage msg;
2645 				CHK(msg.Unflatten(&file) == B_OK);
2646 
2647 //				msg.PrintToStream();
2648 
2649 				char *type;
2650 				uint32 typeVal;
2651 				int32 count;
2652 				for (int i = 0; msg.GetInfo(B_STRING_TYPE, i, &type, &typeVal, &count) == B_OK; i++ ) {
2653 					// Add all the associated applications. Interestingly (or maybe not),
2654 					// any types appearing ONLY in the __mime_table and not in the mime
2655 					// database fail when GetSupportingApps is called on them. Thus, we
2656 					// add them to the type->app map, but not to the list of types.
2657 					const char *app;
2658 					for (int j = 0; j < count; j++) {
2659 						CHK(msg.FindString(type, j, &app) == B_OK);
2660 #if TEST_R5
2661 						typeAppMap[type].insert(to_lower(app));
2662 #else
2663 						fakeTypeAppMap[type].insert(to_lower(app));
2664 #endif
2665 					}
2666 				}
2667 			}
2668 		}
2669 //#endif
2670 
2671 		// For each installed type, get a list of the supported apps, and
2672 		// verify that the list matches the list we generated. Also check
2673 		// that the list of apps for the type's supertype (if it exists)
2674 		// is a subset of the list we generated for said supertype.
2675 		for (std::set<std::string>::iterator i = typeList.begin(); i != typeList.end(); i++) {
2676 			// Get the current type
2677 			std::string type = *i;
2678 			BMimeType mime(type.c_str());
2679 			CHK(mime.InitCheck() == B_OK);
2680 //			printf("------------------------------------------------------------\n");
2681 //			printf("%s\n", type.c_str());
2682 
2683 			// Get the set of supporting apps for this type (and its supertype, if
2684 			// it's not a supertype itself) that we discovered by manually culling
2685 			// the database
2686 			std::set<std::string> appSetSuper;
2687 			BMimeType superType;
2688 			if (mime.GetSupertype(&superType) == B_OK)
2689 				appSetSuper = typeAppMap[superType.Type()];		// Copy the supertype
2690 /*
2691 			printf("sub.size == %ld\n", appSet.size());
2692 			std::set<std::string>::iterator i;
2693 			for (i = appSet.begin(); i != appSet.end(); i++) {
2694 				printf("  %s\n", (*i).c_str());
2695 			}
2696 			printf("super.size == %ld\n", appSetSuper.size());
2697 			for (i = appSetSuper.begin(); i != appSetSuper.end(); i++) {
2698 				printf("  %s\n", (*i).c_str());
2699 			}
2700 			char* str;
2701 			if (mime.GetPreferredApp(str) == B_OK) {
2702 				printf("preferred app:\n");
2703 				printf("  %s\n", str);
2704 			}
2705 */
2706 			// Get the set of supporting apps via GetSupportingApps(), then
2707 			// add them to a list.
2708 			BMessage msg;
2709 			CHK(mime.GetSupportingApps(&msg) == B_OK);
2710 			std::queue<std::string> appList;
2711 			QueueAdapter appAdapter(appList);
2712 			FillWithMimeTypes(appAdapter, msg, "applications");
2713 
2714 //			msg.PrintToStream();
2715 
2716 
2717 		}
2718 	}
2719 }
2720 
2721 void
WildcardAppsTest()2722 MimeTypeTest::WildcardAppsTest() {
2723 	// NULL param
2724 	NextSubTest();
2725 	{
2726 #if TEST_R5
2727 		CHK(BMimeType::GetWildcardApps(NULL) == B_OK);			// R5 == B_OK (???)
2728 #else
2729 		CHK(BMimeType::GetWildcardApps(NULL) == B_BAD_VALUE);
2730 #endif
2731 	}
2732 	// Normal function (compare to BMimeType("application/octet-stream").GetSupportingApps())
2733 	NextSubTest();
2734 	{
2735 		BMessage msg1, msg2;
2736 		CHK(BMimeType::GetWildcardApps(&msg1) == B_OK);
2737 		BMimeType mime(wildcardType);
2738 		CHK(mime.InitCheck() == B_OK);
2739 		CHK(mime.GetSupportingApps(&msg2) == B_OK);
2740 		CHK(msg1 == msg2);
2741 	}
2742 }
2743 
2744 
2745 // init_long_types
2746 static
2747 void
init_long_types(char * notTooLongType,char * tooLongType)2748 init_long_types(char *notTooLongType, char *tooLongType)
2749 {
2750 // R5: Allows buffer sizes up to `B_MIME_TYPE_LENGTH + 1'
2751 // Haiku: We stay consistent: `*_LENGTH' defines the buffer size.
2752 #ifdef TEST_R5
2753 	const int notTooLongLength = B_MIME_TYPE_LENGTH;
2754 #else
2755 	const int notTooLongLength = B_MIME_TYPE_LENGTH - 1;
2756 #endif
2757 	const int tooLongLength = notTooLongLength + 1;
2758 	strcpy(notTooLongType, "image/");
2759 	memset(notTooLongType + strlen(notTooLongType), 'a',
2760 		   notTooLongLength - strlen(notTooLongType));
2761 	notTooLongType[notTooLongLength] = '\0';
2762 	strcpy(tooLongType, "image/");
2763 	memset(tooLongType + strlen(tooLongType), 'a',
2764 		   tooLongLength - strlen(tooLongType));
2765 	tooLongType[tooLongLength] = '\0';
2766 }
2767 
2768 // InitTest
2769 void
InitTest()2770 MimeTypeTest::InitTest()
2771 {
2772 	// tests:
2773 	// * constructors
2774 	// * SetTo(), SetType()
2775 	// * Unset()
2776 	// * InitCheck()
2777 	// (* Type())
2778 
2779 	// We test only a few types here. Exhausting testing is done in
2780 	// ValidityTest().
2781 	const char *validType	= "image/gif";
2782 	const char *validType2	= "application/octet-stream";
2783 	const char *invalidType	= "invalid type";
2784 	char notTooLongType[B_MIME_TYPE_LENGTH + 3];
2785 	char tooLongType[B_MIME_TYPE_LENGTH + 3];
2786 	init_long_types(notTooLongType, tooLongType);
2787 
2788 	// default constructor
2789 	NextSubTest();
2790 	{
2791 		BMimeType type;
2792 		CHK(type.InitCheck() == B_NO_INIT);
2793 		CHK(type.Type() == NULL);
2794 		type.Unset();
2795 		CHK(type.InitCheck() == B_NO_INIT);
2796 		CHK(type.Type() == NULL);
2797 	}
2798 
2799 	// BMimeType(const char *)
2800 	// valid type
2801 	NextSubTest();
2802 	{
2803 		BMimeType type(validType);
2804 		CHK(type.InitCheck() == B_OK);
2805 		CHK(string(type.Type()) == validType);
2806 		type.Unset();
2807 		CHK(type.InitCheck() == B_NO_INIT);
2808 		CHK(type.Type() == NULL);
2809 	}
2810 	// invalid type
2811 	NextSubTest();
2812 	{
2813 		BMimeType type(invalidType);
2814 		CHK(type.InitCheck() == B_BAD_VALUE);
2815 		CHK(type.Type() == NULL);
2816 		type.Unset();
2817 		CHK(type.InitCheck() == B_NO_INIT);
2818 		CHK(type.Type() == NULL);
2819 	}
2820 	// long, but not too long type
2821 	NextSubTest();
2822 	{
2823 		BMimeType type(notTooLongType);
2824 		CHK(type.InitCheck() == B_OK);
2825 		CHK(string(type.Type()) == notTooLongType);
2826 		type.Unset();
2827 		CHK(type.InitCheck() == B_NO_INIT);
2828 		CHK(type.Type() == NULL);
2829 	}
2830 	// too long type
2831 	NextSubTest();
2832 	{
2833 		BMimeType type(tooLongType);
2834 		CHK(type.InitCheck() == B_BAD_VALUE);
2835 		CHK(type.Type() == NULL);
2836 		type.Unset();
2837 		CHK(type.InitCheck() == B_NO_INIT);
2838 		CHK(type.Type() == NULL);
2839 	}
2840 
2841 	// SetTo()
2842 	// valid type
2843 	NextSubTest();
2844 	{
2845 		BMimeType type;
2846 		CHK(type.SetTo(validType) == B_OK);
2847 		CHK(type.InitCheck() == B_OK);
2848 		CHK(string(type.Type()) == validType);
2849 		type.Unset();
2850 		CHK(type.InitCheck() == B_NO_INIT);
2851 		CHK(type.Type() == NULL);
2852 	}
2853 	// invalid type
2854 	NextSubTest();
2855 	{
2856 		BMimeType type;
2857 		CHK(type.SetTo(invalidType) == B_BAD_VALUE);
2858 		CHK(type.InitCheck() == B_BAD_VALUE);
2859 		CHK(type.Type() == NULL);
2860 		type.Unset();
2861 		CHK(type.InitCheck() == B_NO_INIT);
2862 		CHK(type.Type() == NULL);
2863 	}
2864 	// long, but not too long type
2865 	NextSubTest();
2866 	{
2867 		BMimeType type;
2868 		CHK(type.SetTo(notTooLongType) == B_OK);
2869 		CHK(type.InitCheck() == B_OK);
2870 		CHK(string(type.Type()) == notTooLongType);
2871 		type.Unset();
2872 		CHK(type.InitCheck() == B_NO_INIT);
2873 		CHK(type.Type() == NULL);
2874 	}
2875 	// too long type
2876 	NextSubTest();
2877 	{
2878 		BMimeType type;
2879 		CHK(type.SetTo(tooLongType) == B_BAD_VALUE);
2880 		CHK(type.InitCheck() == B_BAD_VALUE);
2881 		CHK(type.Type() == NULL);
2882 		type.Unset();
2883 		CHK(type.InitCheck() == B_NO_INIT);
2884 		CHK(type.Type() == NULL);
2885 	}
2886 
2887 	// SetType()
2888 	// valid type
2889 	NextSubTest();
2890 	{
2891 		BMimeType type;
2892 		CHK(type.SetType(validType) == B_OK);
2893 		CHK(type.InitCheck() == B_OK);
2894 		CHK(string(type.Type()) == validType);
2895 		type.Unset();
2896 		CHK(type.InitCheck() == B_NO_INIT);
2897 		CHK(type.Type() == NULL);
2898 	}
2899 	// invalid type
2900 	NextSubTest();
2901 	{
2902 		BMimeType type;
2903 		CHK(type.SetType(invalidType) == B_BAD_VALUE);
2904 		CHK(type.InitCheck() == B_BAD_VALUE);
2905 		CHK(type.Type() == NULL);
2906 		type.Unset();
2907 		CHK(type.InitCheck() == B_NO_INIT);
2908 		CHK(type.Type() == NULL);
2909 	}
2910 	// long, but not too long type
2911 	NextSubTest();
2912 	{
2913 		BMimeType type;
2914 		CHK(type.SetType(notTooLongType) == B_OK);
2915 		CHK(type.InitCheck() == B_OK);
2916 		CHK(string(type.Type()) == notTooLongType);
2917 		type.Unset();
2918 		CHK(type.InitCheck() == B_NO_INIT);
2919 		CHK(type.Type() == NULL);
2920 	}
2921 	// too long type
2922 	NextSubTest();
2923 	{
2924 		BMimeType type;
2925 		CHK(type.SetType(tooLongType) == B_BAD_VALUE);
2926 		CHK(type.InitCheck() == B_BAD_VALUE);
2927 		CHK(type.Type() == NULL);
2928 		type.Unset();
2929 		CHK(type.InitCheck() == B_NO_INIT);
2930 		CHK(type.Type() == NULL);
2931 	}
2932 
2933 	// reinitialization
2934 	NextSubTest();
2935 	{
2936 		BMimeType type(validType);
2937 		CHK(type.InitCheck() == B_OK);
2938 		CHK(string(type.Type()) == validType);
2939 		CHK(type.SetTo(validType2) == B_OK);
2940 		CHK(type.InitCheck() == B_OK);
2941 		CHK(string(type.Type()) == validType2);
2942 	}
2943 	// bad args
2944 	NextSubTest();
2945 	{
2946 		BMimeType type(NULL);
2947 		CHK(type.Type() == NULL);
2948 		CHK(type.InitCheck() != B_OK);		// R5 == B_NO_INIT, Haiku == B_BAD_VALUE
2949 		CHK(type.Type() == NULL);
2950 		CHK(type.SetTo(NULL) != B_OK);		// R5 == B_NO_INIT, Haiku == B_BAD_VALUE
2951 		CHK(type.Type() == NULL);
2952 		CHK(type.SetType(NULL) != B_OK);	// R5 == B_NO_INIT, Haiku == B_BAD_VALUE
2953 		CHK(type.Type() == NULL);
2954 	}
2955 }
2956 
2957 // StringTest
2958 void
StringTest()2959 MimeTypeTest::StringTest()
2960 {
2961 	// tests:
2962 	// * IsValid() (static/non static)
2963 	// * Type()
2964 	// * IsSupertypeOnly()
2965 	// * GetSupertype()
2966 	// * Contains()
2967 	// * operator==()
2968 
2969 	char notTooLongType[B_MIME_TYPE_LENGTH + 3];
2970 	char tooLongType[B_MIME_TYPE_LENGTH + 3];
2971 	init_long_types(notTooLongType, tooLongType);
2972 	struct mime_type_test {
2973 		const char	*type;
2974 		bool		super_type;
2975 		status_t	error;
2976 	};
2977 	mime_type_test tests[] = {
2978 		// valid types
2979 		{ "application",					true,	B_OK, },
2980 		{ "application/octet-stream",		false,	B_OK, },
2981 		{ "audio",							true,	B_OK, },
2982 		{ "audio/x-aiff",					false,	B_OK, },
2983 		{ "image",							true,	B_OK, },
2984 		{ "image/gif",						false,	B_OK, },
2985 		{ "message",						true,	B_OK, },
2986 		{ "message/rfc822",					false,	B_OK, },
2987 		{ "multipart",						true,	B_OK, },
2988 		{ "multipart/mixed",				false,	B_OK, },
2989 		{ "text",							true,	B_OK, },
2990 		{ "text/plain",						false,	B_OK, },
2991 		{ "video",							true,	B_OK, },
2992 		{ "video/x-msvideo",				false,	B_OK, },
2993 		{ "unknown",						true,	B_OK, },
2994 		{ "unknown/mime-type",				false,	B_OK, },
2995 		{ "$%&./`'~*+#|!^",					false,	B_OK, },
2996 		// invalid types
2997 		{ "",								false,	B_BAD_VALUE, },
2998 		{ "application/",					false,	B_BAD_VALUE, },
2999 		{ "audio/",							false,	B_BAD_VALUE, },
3000 		{ "image/",							false,	B_BAD_VALUE, },
3001 		{ "message/",						false,	B_BAD_VALUE, },
3002 		{ "multipart/",						false,	B_BAD_VALUE, },
3003 		{ "text/",							false,	B_BAD_VALUE, },
3004 		{ "video/",							false,	B_BAD_VALUE, },
3005 		{ "unknown/",						false,	B_BAD_VALUE, },
3006 		{ "/gif",							false,	B_BAD_VALUE, },
3007 		{ "image/very/nice",				false,	B_BAD_VALUE, },
3008 		{ "tex t/plain",					false,	B_BAD_VALUE, },
3009 		{ "text/pla in",					false,	B_BAD_VALUE, },
3010 		{ "tex\tt/plain",					false,	B_BAD_VALUE, },
3011 		{ "text/pla\tin",					false,	B_BAD_VALUE, },
3012 		{ "tex\nt/plain",					false,	B_BAD_VALUE, },
3013 		{ "text/pla\nin",					false,	B_BAD_VALUE, },
3014 		{ "tex<t/plain",					false,	B_BAD_VALUE, },
3015 		{ "text/pla<in",					false,	B_BAD_VALUE, },
3016 		{ "tex>t/plain",					false,	B_BAD_VALUE, },
3017 		{ "text/pla>in",					false,	B_BAD_VALUE, },
3018 		{ "tex@t/plain",					false,	B_BAD_VALUE, },
3019 		{ "text/pla@in",					false,	B_BAD_VALUE, },
3020 		{ "tex,t/plain",					false,	B_BAD_VALUE, },
3021 		{ "text/pla,in",					false,	B_BAD_VALUE, },
3022 		{ "tex;t/plain",					false,	B_BAD_VALUE, },
3023 		{ "text/pla;in",					false,	B_BAD_VALUE, },
3024 		{ "tex:t/plain",					false,	B_BAD_VALUE, },
3025 		{ "text/pla:in",					false,	B_BAD_VALUE, },
3026 		{ "tex\"t/plain",					false,	B_BAD_VALUE, },
3027 		{ "text/pla\"in",					false,	B_BAD_VALUE, },
3028 		{ "tex(t/plain",					false,	B_BAD_VALUE, },
3029 		{ "text/pla(in",					false,	B_BAD_VALUE, },
3030 		{ "tex)t/plain",					false,	B_BAD_VALUE, },
3031 		{ "text/pla)in",					false,	B_BAD_VALUE, },
3032 		{ "tex[t/plain",					false,	B_BAD_VALUE, },
3033 		{ "text/pla[in",					false,	B_BAD_VALUE, },
3034 		{ "tex]t/pla]in",					false,	B_BAD_VALUE, },
3035 		{ "tex?t/plain",					false,	B_BAD_VALUE, },
3036 		{ "text/pla?in",					false,	B_BAD_VALUE, },
3037 		{ "tex=t/plain",					false,	B_BAD_VALUE, },
3038 		{ "text/pla=in",					false,	B_BAD_VALUE, },
3039 		{ "tex\\t/plain",					false,	B_BAD_VALUE, },
3040 		{ "text/pla\\in",					false,	B_BAD_VALUE, },
3041 		// (not) too long types
3042 		{ notTooLongType,					false,	B_OK, },
3043 		{ tooLongType,						false,	B_BAD_VALUE, },
3044 	};
3045 	int32 testCount = sizeof(tests) / sizeof(mime_type_test);
3046 	// test loop
3047 	for (int32 i = 0; i < testCount; i++) {
3048 		NextSubTest();
3049 		mime_type_test &test = tests[i];
3050 		BMimeType type(test.type);
3051 		CHK(type.InitCheck() == test.error);
3052 		bool valid = (test.error == B_OK);
3053 		bool validSuper = (valid && test.super_type);
3054 		// Type()
3055 		if (valid)
3056 			CHK(string(type.Type()) == test.type);
3057 		else
3058 			CHK(type.Type() == NULL);
3059 		// IsValid(), IsSuperTypeOnly()
3060 		CHK(type.IsValid() == valid);
3061 		CHK(type.IsSupertypeOnly() == validSuper);
3062 		CHK(BMimeType::IsValid(test.type) == valid);
3063 		// GetSupertype()
3064 		if (valid && !validSuper) {
3065 			BMimeType super;
3066 			CHK(type.GetSupertype(&super) == B_OK);
3067 			CHK(super.InitCheck() == B_OK);
3068 			CHK(super.Contains(&type) == true);
3069 			BString typeString(test.type);
3070 			BString superString(typeString.String(),
3071 								typeString.FindFirst('/'));
3072 			CHK(superString == super.Type());
3073 		} else {
3074 			BMimeType super;
3075 			CHK(type.GetSupertype(&super) == B_BAD_VALUE);
3076 		}
3077 		// Contains(), ==
3078 		for (int32 k = 0; k < testCount; k++) {
3079 			mime_type_test &test2 = tests[k];
3080 			BMimeType type2(test2.type);
3081 			CHK(type2.InitCheck() == test2.error);
3082 			bool valid2 = (test2.error == B_OK);
3083 			bool validSuper2 = (valid && test2.super_type);
3084 			bool equal = (!strcmp(test.type, test2.type));
3085 			// ==
3086 			if (valid || valid2) {
3087 				CHK((type == type2) == equal);
3088 				CHK((type == test2.type) == equal);
3089 			} else {
3090 				CHK((type == type2) == false);
3091 				CHK((type == test2.type) == false);
3092 			}
3093 			// Contains()
3094 			if (valid || valid2) {
3095 				if (equal)
3096 					CHK(type.Contains(&type2) == true);
3097 				else if (validSuper && valid2 && !validSuper2) {
3098 					BMimeType super2;
3099 					CHK(type2.GetSupertype(&super2) == B_OK);
3100 					bool contains = string(super2.Type()) == type.Type();
3101 					CHK(type.Contains(&type2) == contains);
3102 				} else
3103 					CHK(type.Contains(&type2) == false);
3104 			} else
3105 				CHK(type.Contains(&type2) == false);
3106 		}
3107 	}
3108 	// bad args
3109 	NextSubTest();
3110 	{
3111 		BMimeType type("image/gif");
3112 // R5: crashes when passing NULL
3113 #if !TEST_R5
3114 		CHK(BMimeType::IsValid(NULL) == false);
3115 		CHK(type.GetSupertype(NULL) == B_BAD_VALUE);
3116 		CHK(type.Contains(NULL) == false);
3117 #endif
3118 		CHK((type == NULL) == false);
3119 	}
3120 }
3121 
3122 // an easy to construct equivalent of a notification message
3123 class NotificationMessage {
3124 public:
NotificationMessage(int32 which,string type,string extraType,bool largeIcon)3125 	NotificationMessage(int32 which, string type, string extraType,
3126 						bool largeIcon)
3127 		: which(which), type(type), hasExtraType(true), extraType(extraType),
3128 		  hasLargeIcon(true), largeIcon(largeIcon)
3129 	{
3130 	}
3131 
NotificationMessage(int32 which,string type,string extraType)3132 	NotificationMessage(int32 which, string type, string extraType)
3133 		: which(which), type(type), hasExtraType(true), extraType(extraType),
3134 		  hasLargeIcon(false), largeIcon(false)
3135 	{
3136 	}
3137 
NotificationMessage(int32 which,string type,bool largeIcon)3138 	NotificationMessage(int32 which, string type, bool largeIcon)
3139 		: which(which), type(type), hasExtraType(false), extraType(),
3140 		  hasLargeIcon(true), largeIcon(largeIcon)
3141 	{
3142 	}
3143 
NotificationMessage(int32 which,string type)3144 	NotificationMessage(int32 which, string type)
3145 		: which(which), type(type), hasExtraType(false), extraType(),
3146 		  hasLargeIcon(false), largeIcon(false)
3147 	{
3148 	}
3149 
3150 public:
3151 	int32	which;
3152 	string	type;
3153 	bool	hasExtraType;
3154 	string	extraType;
3155 	bool	hasLargeIcon;
3156 	bool	largeIcon;
3157 };
3158 
3159 // FillAttrInfo
3160 static
3161 void
FillAttrInfo(BMessage & info,int32 variation=0)3162 FillAttrInfo(BMessage &info, int32 variation = 0)
3163 {
3164 	switch (variation) {
3165 		case 0:
3166 		default:
3167 			CHK(info.AddString("attr:name", "attribute1") == B_OK);
3168 			CHK(info.AddString("attr:public_name", "Nice Attribute1") == B_OK);
3169 			CHK(info.AddInt32("attr:type", B_STRING_TYPE) == B_OK);
3170 			CHK(info.AddBool("attr:public", true) == B_OK);
3171 			CHK(info.AddBool("attr:editable", true) == B_OK);
3172 			break;
3173 		case 1:
3174 			CHK(info.AddString("attr:name", "attribute2") == B_OK);
3175 			CHK(info.AddString("attr:public_name", "Nice Attribute2") == B_OK);
3176 			CHK(info.AddInt32("attr:type", B_BOOL_TYPE) == B_OK);
3177 			CHK(info.AddBool("attr:public", false) == B_OK);
3178 			CHK(info.AddBool("attr:editable", false) == B_OK);
3179 			break;
3180 	}
3181 }
3182 
3183 // MonitoringTest
3184 void
MonitoringTest()3185 MimeTypeTest::MonitoringTest()
3186 {
3187 	// tests:
3188 	// * Start/StopWatching()
3189 	// * updates
3190 
3191 	// test:
3192 	// * StartWatching()
3193 	// * change something, check message queue (not empty)
3194 	//   - add type
3195 	//   - set icon, preferred app, attr info, file ext., short/long desc.,
3196 	//     icon for, app hint, sniffer rule
3197 	//   - remove type
3198 	// * StopWatching(anotherTarget)
3199 	// * change something, check message queue (not empty)
3200 	// * StopWatching()
3201 	// * change something, check message queue (empty)
3202 
3203 	CHK(fApplication != NULL);
3204 	NextSubTest();
3205 	// StartWatching()
3206 	BMessenger target(&fApplication->Handler(), fApplication);
3207 	CHK(BMimeType::StartWatching(target) == B_OK);
3208 	// install
3209 	BMimeType type(testType);
3210 	CHK(type.InitCheck() == B_OK);
3211 	CHK(type.IsInstalled() == false);
3212 	CHK(type.Install() == B_OK);
3213 	// icon
3214 	IconHelper iconHelperLarge(B_LARGE_ICON);
3215 	IconHelper iconHelperMini(B_MINI_ICON);
3216 	CHK(type.SetIcon(iconHelperLarge.Bitmap1(), B_LARGE_ICON) == B_OK);
3217 	CHK(type.SetIcon(iconHelperMini.Bitmap1(), B_MINI_ICON) == B_OK);
3218 	// preferred app
3219 	CHK(type.SetPreferredApp(testTypeApp) == B_OK);
3220 	// attr info
3221 	BMessage attrInfo;
3222 	FillAttrInfo(attrInfo);
3223 	CHK(type.SetAttrInfo(&attrInfo) == B_OK);
3224 	// file extensions
3225 	BMessage extensions;
3226 	CHK(extensions.AddString("extensions", "arg") == B_OK);
3227 	CHK(extensions.AddString("extensions", "ugh") == B_OK);
3228 	CHK(type.SetFileExtensions(&extensions) == B_OK);
3229 	// long/short description
3230 	CHK(type.SetLongDescription("quite short for a long description") == B_OK);
3231 	CHK(type.SetShortDescription("short description") == B_OK);
3232 	// icon for type
3233 	CHK(type.SetIconForType("text/plain", iconHelperLarge.Bitmap1(),
3234 							B_LARGE_ICON) == B_OK);
3235 	CHK(type.SetIconForType("text/plain", iconHelperMini.Bitmap1(),
3236 							B_MINI_ICON) == B_OK);
3237 	// app hint
3238 	entry_ref appHintRef;
3239 	CHK(get_ref_for_path("/boot/beos/apps/StyledEdit", &appHintRef) == B_OK);
3240 	CHK(type.SetAppHint(&appHintRef) == B_OK);
3241 	// sniffer rule
3242 	const char *snifferRule = "0.5 [0:0] ('ARGH')";
3243 	CHK(type.SetSnifferRule(snifferRule) == B_OK);
3244 	{
3245 	//   - set icon, preferred app, attr info, file ext., short/long desc.,
3246 	//     icon for, app hint, sniffer rule
3247 		typedef NotificationMessage NM;
3248 		NotificationMessage messages[] = {
3249 			NM(B_MIME_TYPE_CREATED, testType),
3250 			NM(B_ICON_CHANGED, testType, true),
3251 			NM(B_ICON_CHANGED, testType, false),
3252 			NM(B_PREFERRED_APP_CHANGED, testType),
3253 			NM(B_ATTR_INFO_CHANGED, testType),
3254 			NM(B_FILE_EXTENSIONS_CHANGED, testType),
3255 			NM(B_LONG_DESCRIPTION_CHANGED, testType),
3256 			NM(B_SHORT_DESCRIPTION_CHANGED, testType),
3257 			NM(B_ICON_FOR_TYPE_CHANGED, testType, "text/plain", true),
3258 			NM(B_ICON_FOR_TYPE_CHANGED, testType, "text/plain", false),
3259 			NM(B_APP_HINT_CHANGED, testType),
3260 			NM(B_SNIFFER_RULE_CHANGED, testType),
3261 		};
3262 		CheckNotificationMessages(messages, sizeof(messages) / sizeof(NM));
3263 	}
3264 
3265 	// set the same values once again
3266 	NextSubTest();
3267 	// icon
3268 	CHK(type.SetIcon(iconHelperLarge.Bitmap1(), B_LARGE_ICON) == B_OK);
3269 	CHK(type.SetIcon(iconHelperMini.Bitmap1(), B_MINI_ICON) == B_OK);
3270 	// preferred app
3271 	CHK(type.SetPreferredApp(testTypeApp) == B_OK);
3272 	// attr info
3273 	CHK(type.SetAttrInfo(&attrInfo) == B_OK);
3274 // file extensions
3275 	CHK(extensions.AddString("extensions", "arg") == B_OK);
3276 	CHK(extensions.AddString("extensions", "ugh") == B_OK);
3277 	CHK(type.SetFileExtensions(&extensions) == B_OK);
3278 	// long/short description
3279 	CHK(type.SetLongDescription("quite short for a long description") == B_OK);
3280 	CHK(type.SetShortDescription("short description") == B_OK);
3281 	// icon for type
3282 	CHK(type.SetIconForType("text/plain", iconHelperLarge.Bitmap1(),
3283 							B_LARGE_ICON) == B_OK);
3284 	CHK(type.SetIconForType("text/plain", iconHelperMini.Bitmap1(),
3285 							B_MINI_ICON) == B_OK);
3286 	// app hint
3287 	CHK(type.SetAppHint(&appHintRef) == B_OK);
3288 	// sniffer rule
3289 	CHK(type.SetSnifferRule(snifferRule) == B_OK);
3290 	{
3291 	//   - set icon, preferred app, attr info, file ext., short/long desc.,
3292 	//     icon for, app hint, sniffer rule
3293 		typedef NotificationMessage NM;
3294 		NotificationMessage messages[] = {
3295 			NM(B_ICON_CHANGED, testType, true),
3296 			NM(B_ICON_CHANGED, testType, false),
3297 			NM(B_PREFERRED_APP_CHANGED, testType),
3298 			NM(B_ATTR_INFO_CHANGED, testType),
3299 			NM(B_FILE_EXTENSIONS_CHANGED, testType),
3300 			NM(B_LONG_DESCRIPTION_CHANGED, testType),
3301 			NM(B_SHORT_DESCRIPTION_CHANGED, testType),
3302 			NM(B_ICON_FOR_TYPE_CHANGED, testType, "text/plain", true),
3303 			NM(B_ICON_FOR_TYPE_CHANGED, testType, "text/plain", false),
3304 			NM(B_APP_HINT_CHANGED, testType),
3305 			NM(B_SNIFFER_RULE_CHANGED, testType),
3306 		};
3307 		CheckNotificationMessages(messages, sizeof(messages) / sizeof(NM));
3308 	}
3309 
3310 	// set different values
3311 	NextSubTest();
3312 	// icon
3313 	CHK(type.SetIcon(iconHelperLarge.Bitmap2(), B_LARGE_ICON) == B_OK);
3314 	CHK(type.SetIcon(iconHelperMini.Bitmap2(), B_MINI_ICON) == B_OK);
3315 	// preferred app
3316 	CHK(type.SetPreferredApp("application/x-vnd.Be-STEE") == B_OK);
3317 	// attr info
3318 	BMessage attrInfo2;
3319 	FillAttrInfo(attrInfo2, 1);
3320 	CHK(type.SetAttrInfo(&attrInfo2) == B_OK);
3321 	// file extensions
3322 	CHK(extensions.AddString("extensions", "uff") == B_OK);
3323 	CHK(extensions.AddString("extensions", "err") == B_OK);
3324 	CHK(type.SetFileExtensions(&extensions) == B_OK);
3325 	// long/short description
3326 	CHK(type.SetLongDescription("not that short description") == B_OK);
3327 	CHK(type.SetShortDescription("pretty short description") == B_OK);
3328 	// icon for type
3329 	CHK(type.SetIconForType("text/plain", iconHelperLarge.Bitmap2(),
3330 							B_LARGE_ICON) == B_OK);
3331 	CHK(type.SetIconForType("text/plain", NULL,
3332 							B_LARGE_ICON) == B_OK);
3333 	CHK(type.SetIconForType("text/plain", iconHelperMini.Bitmap2(),
3334 							B_MINI_ICON) == B_OK);
3335 	// app hint
3336 	entry_ref appHintRef2;
3337 	CHK(get_ref_for_path("/boot/beos/apps/NetPositive", &appHintRef2) == B_OK);
3338 	CHK(type.SetAppHint(&appHintRef2) == B_OK);
3339 	// sniffer rule
3340 	const char *snifferRule2 = "0.7 [0:5] ('YEAH!')";
3341 	CHK(type.SetSnifferRule(snifferRule2) == B_OK);
3342 	// delete
3343 	CHK(type.Delete() == B_OK);
3344 	{
3345 	//   - set icon, preferred app, attr info, file ext., short/long desc.,
3346 	//     icon for, app hint, sniffer rule
3347 		typedef NotificationMessage NM;
3348 		NotificationMessage messages[] = {
3349 			NM(B_ICON_CHANGED, testType, true),
3350 			NM(B_ICON_CHANGED, testType, false),
3351 			NM(B_PREFERRED_APP_CHANGED, testType),
3352 			NM(B_ATTR_INFO_CHANGED, testType),
3353 			NM(B_FILE_EXTENSIONS_CHANGED, testType),
3354 			NM(B_LONG_DESCRIPTION_CHANGED, testType),
3355 			NM(B_SHORT_DESCRIPTION_CHANGED, testType),
3356 			NM(B_ICON_FOR_TYPE_CHANGED, testType, "text/plain", true),
3357 			NM(B_ICON_FOR_TYPE_CHANGED, testType, "text/plain", true),
3358 			NM(B_ICON_FOR_TYPE_CHANGED, testType, "text/plain", false),
3359 			NM(B_APP_HINT_CHANGED, testType),
3360 			NM(B_SNIFFER_RULE_CHANGED, testType),
3361 			NM(B_MIME_TYPE_DELETED, testType),
3362 		};
3363 		CheckNotificationMessages(messages, sizeof(messages) / sizeof(NM));
3364 	}
3365 
3366 	// StopWatching() and try again -- no messages should be sent anymore
3367 	CHK(BMimeType::StopWatching(target) == B_OK);
3368 	// install
3369 	CHK(type.InitCheck() == B_OK);
3370 	CHK(type.IsInstalled() == false);
3371 	CHK(type.Install() == B_OK);
3372 	// icon
3373 	CHK(type.SetIcon(iconHelperLarge.Bitmap1(), B_LARGE_ICON) == B_OK);
3374 	CHK(type.SetIcon(iconHelperMini.Bitmap1(), B_MINI_ICON) == B_OK);
3375 	// preferred app
3376 	CHK(type.SetPreferredApp(testTypeApp) == B_OK);
3377 	// attr info
3378 	CHK(type.SetAttrInfo(&attrInfo) == B_OK);
3379 	// file extensions
3380 	CHK(extensions.AddString("extensions", "arg") == B_OK);
3381 	CHK(extensions.AddString("extensions", "ugh") == B_OK);
3382 	CHK(type.SetFileExtensions(&extensions) == B_OK);
3383 	// long/short description
3384 	CHK(type.SetLongDescription("quite short for a long description") == B_OK);
3385 	CHK(type.SetShortDescription("short description") == B_OK);
3386 	// icon for type
3387 	CHK(type.SetIconForType("text/plain", iconHelperLarge.Bitmap1(),
3388 							B_LARGE_ICON) == B_OK);
3389 	CHK(type.SetIconForType("text/plain", iconHelperMini.Bitmap1(),
3390 							B_MINI_ICON) == B_OK);
3391 	// app hint
3392 	CHK(type.SetAppHint(&appHintRef) == B_OK);
3393 	// sniffer rule
3394 	CHK(type.SetSnifferRule(snifferRule) == B_OK);
3395 	// delete
3396 	CHK(type.Delete() == B_OK);
3397 	{
3398 		CheckNotificationMessages(NULL, 0);
3399 	}
3400 
3401 	// bad args
3402 	// StopWatching() another target
3403 	NextSubTest();
3404 	// install
3405 	CHK(type.InitCheck() == B_OK);
3406 	CHK(type.IsInstalled() == false);
3407 	CHK(type.Install() == B_OK);
3408 	// try to start/stop watching with an invalid target, stop the wrong target
3409 	BMessenger target2(fApplication);
3410 	CHK(target2.IsValid() == true);
3411 	BMessenger target3("application/does-not_exist");
3412 	CHK(target3.IsValid() == false);
3413 // R5: An invalid messenger is fine for any reason?!
3414 #if !TEST_R5
3415 	CHK(BMimeType::StartWatching(target3) == B_BAD_VALUE);
3416 #endif
3417 	CHK(BMimeType::StartWatching(target) == B_OK);
3418 #if !TEST_R5
3419 	CHK(BMimeType::StopWatching(target3) == B_BAD_VALUE);
3420 #endif
3421 	CHK(BMimeType::StopWatching(target2) != B_OK);	// R5 == B_BAD_VALUE, Haiku == B_ENTRY_NOT_FOUND
3422 	CHK(BMimeType::StopWatching(target) == B_OK);
3423 	// delete
3424 	CHK(type.Delete() == B_OK);
3425 }
3426 
3427 // CheckNotificationMessage
3428 void
CheckNotificationMessages(const NotificationMessage * messages,int32 count)3429 MimeTypeTest::CheckNotificationMessages(const NotificationMessage *messages,
3430 										int32 count)
3431 {
3432 	// wait for the messages
3433 	snooze(100000);
3434 	if (fApplication) {
3435 		BMessageQueue &queue = fApplication->Handler().Queue();
3436 		CPPUNIT_ASSERT( queue.Lock() );
3437 		try {
3438 			int32 messageNum = 0;
3439 			while (BMessage *_message = queue.NextMessage()) {
3440 				BMessage message(*_message);
3441 				delete _message;
3442 //printf("\nmessage: %ld\n", messageNum);
3443 //message.PrintToStream();
3444 				CPPUNIT_ASSERT( messageNum < count );
3445 				const NotificationMessage &entry = messages[messageNum];
3446 				CPPUNIT_ASSERT( message.what == B_META_MIME_CHANGED );
3447 				// which
3448 				int32 which;
3449 				CPPUNIT_ASSERT( message.FindInt32("be:which", &which)
3450 								== B_OK );
3451 				CPPUNIT_ASSERT( entry.which == which );
3452 				// type
3453 				const char *type;
3454 				CPPUNIT_ASSERT( message.FindString("be:type", &type) == B_OK );
3455 				CPPUNIT_ASSERT( entry.type == type );
3456 				// extra type
3457 				const char *extraType;
3458 				if (entry.hasExtraType) {
3459 					CPPUNIT_ASSERT( message.FindString("be:extra_type",
3460 													   &extraType) == B_OK);
3461 					CPPUNIT_ASSERT( entry.extraType == extraType );
3462 				} else {
3463 					CPPUNIT_ASSERT( message.FindString("be:extra_type",
3464 										&extraType) == B_NAME_NOT_FOUND);
3465 				}
3466 				// large icon
3467 				bool largeIcon;
3468 				if (entry.hasLargeIcon) {
3469 					CPPUNIT_ASSERT( message.FindBool("be:large_icon",
3470 													 &largeIcon) == B_OK);
3471 					CPPUNIT_ASSERT( entry.largeIcon == largeIcon );
3472 				} else {
3473 					CPPUNIT_ASSERT( message.FindBool("be:large_icon",
3474 										&largeIcon) == B_NAME_NOT_FOUND);
3475 				}
3476 				messageNum++;
3477 			}
3478 			CPPUNIT_ASSERT( messageNum == count );
3479 		} catch (CppUnit::Exception exception) {
3480 			queue.Unlock();
3481 			throw exception;
3482 		}
3483 		queue.Unlock();
3484 	}
3485 }
3486 
3487 // helper class for update_mime_info() tests
3488 class MimeInfoTestFile {
3489 public:
MimeInfoTestFile(string name,string type,const void * data=NULL,int32 size=-1)3490 	MimeInfoTestFile(string name, string type, const void *data = NULL,
3491 					 int32 size = -1)
3492 		: name(name),
3493 		  type(type),
3494 		  data(NULL),
3495 		  size(0)
3496 	{
3497 		if (data) {
3498 			if (size == -1)
3499 				this->size = strlen((const char*)data) + 1;
3500 			else
3501 				this->size = size;
3502 			this->data = new char[this->size];
3503 			memcpy(this->data, data, this->size);
3504 		}
3505 	}
3506 
~MimeInfoTestFile()3507 	~MimeInfoTestFile()
3508 	{
3509 		delete[] data;
3510 	}
3511 
Create()3512 	status_t Create()
3513 	{
3514 		BFile file(name.c_str(), B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
3515 		status_t error = file.InitCheck();
3516 		if (error == B_OK && data) {
3517 			ssize_t written = file.Write(data, size);
3518 			if (written < 0)
3519 				error = written;
3520 			else if (written != size)
3521 				error = B_ERROR;
3522 		}
3523 		return error;
3524 	}
3525 
Delete()3526 	status_t Delete()
3527 	{
3528 		return BEntry(name.c_str()).Remove();
3529 	}
3530 
3531 	string	name;
3532 	string	type;
3533 	char	*data;
3534 	int32	size;
3535 };
3536 
3537 // UpdateMimeInfoTest
3538 void
UpdateMimeInfoTest()3539 MimeTypeTest::UpdateMimeInfoTest()
3540 {
3541 // Uncomment the following lines to enjoy the quiet time provided
3542 // by a nice, full mime update. :-)
3543 
3544 //	cout << "begin..." << endl;
3545 //	CHK(update_mime_info(NULL, true, true, false) == B_OK);
3546 //	cout << "end..." << endl;
3547 
3548 	// tests:
3549 	// * update_mime_info()
3550 
3551 	// Note:
3552 	// * Only synchronous calls are tested.
3553 	// * Updating all files is not tested as it takes too long.
3554 
3555 	// individual files
3556 	execCommand(string("mkdir ") + testDir + "/subdir1 "
3557 				+ testDir + "/subdir2 "
3558 				+ testDir + "/subdir2/subsubdir1");
3559 	MimeInfoTestFile files[] = {
3560 		MimeInfoTestFile(string(testDir) + "/file1.cpp", "text/x-source-code"),
3561 		MimeInfoTestFile(string(testDir) + "/subdir1/file1.gif", "image/gif"),
3562 		MimeInfoTestFile(string(testDir) + "/subdir2/subsubdir1/file1",
3563 						 "text/html", "<html>\n<body>\n</body></html>\n")
3564 	};
3565 	int fileCount = sizeof(files) / sizeof(MimeInfoTestFile);
3566 	// synchronous
3567 	for (int32 i = 0; i < fileCount; i++) {
3568 		NextSubTest();
3569 		MimeInfoTestFile &file = files[i];
3570 		// no recursion
3571 		CHK(file.Create() == B_OK);
3572 		CHK(update_mime_info(file.name.c_str(), false, true, false) == B_OK);
3573 		BNode node(file.name.c_str());
3574 		CHK(node.InitCheck() == B_OK);
3575 		BString type;
3576 		CHK(node.ReadAttrString("BEOS:TYPE", &type) == B_OK);
3577 		node.Unset();
3578 		CHK(type == file.type.c_str());
3579 		CHK(file.Delete() == B_OK);
3580 		// recursion
3581 		CHK(file.Create() == B_OK);
3582 		CHK(update_mime_info(file.name.c_str(), true, true, false) == B_OK);
3583 		CHK(node.SetTo(file.name.c_str()) == B_OK);
3584 		type = "";
3585 		CHK(node.ReadAttrString("BEOS:TYPE", &type) == B_OK);
3586 		node.Unset();
3587 		CHK(type == file.type.c_str());
3588 		CHK(file.Delete() == B_OK);
3589 	}
3590 
3591 //------------------------------------------------------------------------------
3592 // Asynchronous calls
3593 //------------------------------------------------------------------------------
3594 
3595 	const bigtime_t kSnoozeTime = 500000;
3596 	for (int32 i = 0; i < fileCount; i++) {
3597 		NextSubTest();
3598 		MimeInfoTestFile &file = files[i];
3599 		// no recursion
3600 		CHK(file.Create() == B_OK);
3601 		CHK(update_mime_info(file.name.c_str(), false, false, false) == B_OK);
3602 		// give the system some time to do the update asynchronously
3603 		snooze(kSnoozeTime);
3604 		BNode node(file.name.c_str());
3605 		CHK(node.InitCheck() == B_OK);
3606 		BString type;
3607 		CHK(node.ReadAttrString("BEOS:TYPE", &type) == B_OK);
3608 		node.Unset();
3609 		CHK(type == file.type.c_str());
3610 		CHK(file.Delete() == B_OK);
3611 		// recursion
3612 		CHK(file.Create() == B_OK);
3613 		CHK(update_mime_info(file.name.c_str(), true, false, false) == B_OK);
3614 		// give the system some time to do the update asynchronously
3615 		snooze(kSnoozeTime);
3616 		CHK(node.SetTo(file.name.c_str()) == B_OK);
3617 		type = "";
3618 		CHK(node.ReadAttrString("BEOS:TYPE", &type) == B_OK);
3619 		node.Unset();
3620 		CHK(type == file.type.c_str());
3621 		CHK(file.Delete() == B_OK);
3622 	}
3623 
3624 // TODO: The BeBook says: "if force is true, files are updated even if they've
3625 // been updated already."
3626 // As I understand this, calling update_mime_info() with force == true on a
3627 // file, should set the BEOS:TYPE attribute regardless of whether it already
3628 // had a value. The following test shows, that BEOS:TYPE remains unchanged
3629 // though.
3630 #if TEST_OBOS
3631 	for (int32 i = 0; i < fileCount; i++) {
3632 		MimeInfoTestFile &file = files[i];
3633 //printf("file: %s\n", file.name.c_str());
3634 		CHK(file.Create() == B_OK);
3635 		// add a type attribute
3636 		BNode node(file.name.c_str());
3637 		CHK(node.InitCheck() == B_OK);
3638 		BString type("text/plain");
3639 		CHK(node.WriteAttrString("BEOS:TYPE", &type) == B_OK);
3640 		// update, force == false
3641 		CHK(update_mime_info(file.name.c_str(), false, true, false) == B_OK);
3642 		type = "";
3643 		CHK(node.ReadAttrString("BEOS:TYPE", &type) == B_OK);
3644 		CHK(type == "text/plain");
3645 		// update, force == true
3646 		CHK(update_mime_info(file.name.c_str(), false, true, true) == B_OK);
3647 		type = "";
3648 		CHK(node.ReadAttrString("BEOS:TYPE", &type) == B_OK);
3649 		node.Unset();
3650 //printf("%s <-> %s\n", type.String(), file.type.c_str());
3651 		CHK(type == file.type.c_str());
3652 		CHK(file.Delete() == B_OK);
3653 	}
3654 #endif	// TEST_OBOS
3655 
3656 	// directory
3657 	NextSubTest();
3658 	// create
3659 	for (int32 i = 0; i < fileCount; i++) {
3660 		MimeInfoTestFile &file = files[i];
3661 		CHK(file.Create() == B_OK);
3662 	}
3663 	// update, not recursive
3664 	CHK(update_mime_info(testDir, false, true, false) == B_OK);
3665 	// check
3666 	for (int32 i = 0; i < fileCount; i++) {
3667 		MimeInfoTestFile &file = files[i];
3668 		BNode node(file.name.c_str());
3669 		CHK(node.InitCheck() == B_OK);
3670 		BString type;
3671 		CHK(node.ReadAttrString("BEOS:TYPE", &type) == B_ENTRY_NOT_FOUND);
3672 	}
3673 	// delete, re-create
3674 	for (int32 i = 0; i < fileCount; i++) {
3675 		MimeInfoTestFile &file = files[i];
3676 		CHK(file.Delete() == B_OK);
3677 		CHK(file.Create() == B_OK);
3678 	}
3679 	// update, recursive
3680 	CHK(update_mime_info(testDir, true, true, false) == B_OK);
3681 	for (int32 i = 0; i < fileCount; i++) {
3682 		MimeInfoTestFile &file = files[i];
3683 		BNode node(file.name.c_str());
3684 		CHK(node.InitCheck() == B_OK);
3685 		BString type;
3686 		CHK(node.ReadAttrString("BEOS:TYPE", &type) == B_OK);
3687 		node.Unset();
3688 		CHK(type == file.type.c_str());
3689 	}
3690 	// delete
3691 	for (int32 i = 0; i < fileCount; i++) {
3692 		MimeInfoTestFile &file = files[i];
3693 		CHK(file.Delete() == B_OK);
3694 	}
3695 
3696 	// bad args: non-existing file
3697 	NextSubTest();
3698 	BEntry entry(files[0].name.c_str());
3699 	CHK(entry.InitCheck() == B_OK);
3700 	CHK(entry.Exists() == false);
3701 //#if TEST_R5
3702 	CHK(update_mime_info(files[0].name.c_str(), false, true, false) == B_OK);
3703 //#else
3704 //	CHK(update_mime_info(files[0].name.c_str(), false, true, false) == B_ENTRY_NOT_FOUND);
3705 //#endif
3706 }
3707 
3708 // WriteStringAttr
3709 static
3710 status_t
WriteStringAttr(BNode & node,string name,string _value)3711 WriteStringAttr(BNode &node, string name, string _value)
3712 {
3713 	// Wrapper for BNode::WriteAttrString() taking string rather than
3714 	// const char*/BString* parameters.
3715 	BString value(_value.c_str());
3716 	return node.WriteAttrString(name.c_str(), &value);
3717 }
3718 
3719 const uint32 MINI_ICON_TYPE = 'MICN';
3720 const uint32 LARGE_ICON_TYPE = 'ICON';
3721 
3722 // helper class for create_app_meta_mime() tests
3723 class AppMimeTestFile {
3724 public:
AppMimeTestFile(string name,string type,string signature,string snifferRule,const void * miniIcon=NULL,const void * largeIcon=NULL)3725 	AppMimeTestFile(string name, string type, string signature,
3726 					string snifferRule,
3727 					const void *miniIcon = NULL, const void *largeIcon = NULL)
3728 		: name(name),
3729 		  type(type),
3730 		  signature(signature),
3731 		  snifferRule(snifferRule),
3732 		  miniIcon(NULL),
3733 		  largeIcon(NULL)
3734 	{
3735 		SetMiniIcon(miniIcon);
3736 		SetLargeIcon(largeIcon);
3737 	}
3738 
~AppMimeTestFile()3739 	~AppMimeTestFile()
3740 	{
3741 		SetMiniIcon(NULL);
3742 		SetLargeIcon(NULL);
3743 	}
3744 
SetMiniIcon(const void * icon)3745 	void SetMiniIcon(const void *icon)
3746 	{
3747 		if (miniIcon) {
3748 			delete[] miniIcon;
3749 			miniIcon = NULL;
3750 		}
3751 		if (icon) {
3752 			miniIcon = new char[256];
3753 			memcpy(miniIcon, icon, 256);
3754 		}
3755 	}
3756 
SetLargeIcon(const void * icon)3757 	void SetLargeIcon(const void *icon)
3758 	{
3759 		if (largeIcon) {
3760 			delete[] largeIcon;
3761 			largeIcon = NULL;
3762 		}
3763 		if (icon) {
3764 			largeIcon = new char[1024];
3765 			memcpy(largeIcon, icon, 1024);
3766 		}
3767 	}
3768 
Create(bool setAttributes,bool setResources)3769 	status_t Create(bool setAttributes, bool setResources)
3770 	{
3771 		BFile file(name.c_str(), B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
3772 		status_t error = file.InitCheck();
3773 		// attributes
3774 		if (error == B_OK && setAttributes) {
3775 			// type
3776 			if (type.length() > 0)
3777 				error = WriteStringAttr(file, "BEOS:TYPE", type);
3778 			// signature
3779 			if (error == B_OK)
3780 				error = WriteStringAttr(file, "BEOS:APP_SIG", signature);
3781 			// sniffer rule
3782 			if (error == B_OK)
3783 				error = WriteStringAttr(file, "BEOS:SNIFF_RULE", snifferRule);
3784 			// mini icon
3785 			if (error == B_OK && miniIcon) {
3786 				ssize_t written = file.WriteAttr("BEOS:M:STD_ICON",
3787 												 MINI_ICON_TYPE, 0, miniIcon,
3788 												 256);
3789 				if (written < 0)
3790 					error = written;
3791 				else if (written != 256)
3792 					error = B_ERROR;
3793 			}
3794 			// large icon (ignored)
3795 			if (error == B_OK && largeIcon) {
3796 				ssize_t written = file.WriteAttr("META:L:STD_ICON",
3797 												 LARGE_ICON_TYPE, 0, largeIcon,
3798 												 1024);
3799 				if (written < 0)
3800 					error = written;
3801 				else if (written != 1024)
3802 					error = B_ERROR;
3803 			}
3804 		}
3805 		// resources
3806 		if (error == B_OK && setResources) {
3807 			BResources resources;
3808 			error = resources.SetTo(&file, true);
3809 			// type (ignored)
3810 			if (error == B_OK && type.length() > 0) {
3811 				error = resources.AddResource(B_STRING_TYPE, 2, type.c_str(),
3812 											  type.length() + 1, "BEOS:TYPE");
3813 			}
3814 			// signature (ignored)
3815 			if (error == B_OK) {
3816 				error = resources.AddResource(B_STRING_TYPE, 1,
3817 											  signature.c_str(),
3818 											  signature.length() + 1,
3819 											  "BEOS:APP_SIG");
3820 			}
3821 			// mini icon (ignored)
3822 			if (error == B_OK && miniIcon) {
3823 				error = resources.AddResource(MINI_ICON_TYPE, 101, miniIcon,
3824 											  256, "BEOS:M:STD_ICON");
3825 			}
3826 			// file types (ignored)
3827 			if (error == B_OK) {
3828 				BMessage msg;
3829 				char *buffer = NULL;
3830 				error = msg.AddString("types", "text/x-email");
3831 				if (!error)
3832 					error = msg.AddString("types", "video/mpeg");
3833 				if (!error) {
3834 					buffer = new char[msg.FlattenedSize()];
3835 					if (!buffer)
3836 						error = B_NO_MEMORY;
3837 				}
3838 				if (!error)
3839 					error = msg.Flatten(buffer, msg.FlattenedSize());
3840 				if (!error)
3841 					error = resources.AddResource(B_MESSAGE_TYPE, 1, buffer,
3842 													msg.FlattenedSize(), "BEOS:FILE_TYPES");
3843 				delete [] buffer;
3844 			}
3845 		}
3846 		return error;
3847 	}
3848 
Delete(bool deleteMimeType)3849 	status_t Delete(bool deleteMimeType)
3850 	{
3851 		status_t error = BEntry(name.c_str()).Remove();
3852 		if (error == B_OK && deleteMimeType) {
3853 			BMimeType type;
3854 			// the type need not necessarily exist
3855 			error = type.SetTo(signature.c_str());
3856 			if (error == B_OK && deleteMimeType && type.IsInstalled())
3857 				error = type.Delete();
3858 		}
3859 		return error;
3860 	}
3861 
3862 	string	name;
3863 	string	type;
3864 	string	signature;
3865 	string	snifferRule;
3866 	char	*miniIcon;
3867 	char	*largeIcon;
3868 };
3869 
3870 // CheckAppMetaMime
3871 static
3872 void
CheckAppMetaMime(AppMimeTestFile & file)3873 CheckAppMetaMime(AppMimeTestFile &file)
3874 {
3875 	BMimeType type;
3876 	CHK(type.SetTo(file.signature.c_str()) == B_OK);
3877 	CHK(type.IsInstalled() == true);
3878 	// short description
3879 	char shortDescription[B_MIME_TYPE_LENGTH + 1];
3880 	CHK(type.GetShortDescription(shortDescription) == B_OK);
3881 	BPath path(file.name.c_str(), NULL, true);
3882 	CHK(string(path.Leaf()) == shortDescription);
3883 	// preferred app
3884 	char preferredApp[B_MIME_TYPE_LENGTH + 1];
3885 	CHK(type.GetPreferredApp(preferredApp) == B_OK);
3886 	CHK(file.signature == preferredApp);
3887 	// META:PPATH
3888 	BNode typeFile;
3889 	string typeFilename(string(mimeDatabaseDir) + "/" + file.signature);
3890 //	cout << "typeFilename == '" << typeFilename << "'" << endl;
3891 	CHK(typeFile.SetTo(typeFilename.c_str()) == B_OK);
3892 	char filePath[B_PATH_NAME_LENGTH + 1];
3893 	CHK(typeFile.ReadAttr("META:PPATH", B_STRING_TYPE, 0, filePath,
3894 						  B_PATH_NAME_LENGTH + 1) > 0);
3895 	CHK(path == filePath);
3896 	// mini icon
3897 	if (file.miniIcon) {
3898 		BBitmap icon(BRect(0, 0, 15, 15), B_CMAP8);
3899 		CHK(type.GetIcon(&icon, B_MINI_ICON) == B_OK);
3900 		CHK(memcmp(icon.Bits(), file.miniIcon, 256) == 0);
3901 	}
3902 	// large icon
3903 /*	if (file.miniIcon) {
3904 		BBitmap icon(BRect(0, 0, 31, 31), B_CMAP8);
3905 		CHK(type.GetIcon(&icon, B_LARGE_ICON) == B_OK);
3906 		CHK(memcmp(icon.Bits(), file.largeIcon, 1024) == 0);
3907 	}*/
3908 }
3909 
3910 // CreateAppMetaMimeTest
3911 void
CreateAppMetaMimeTest()3912 MimeTypeTest::CreateAppMetaMimeTest()
3913 {
3914 
3915 // Uncomment the following lines to enjoy the quiet time provided by
3916 // a nice, full create_app_meta_mime() update. :-)
3917 
3918 //	cout << "begin..." << endl;
3919 //	CHK(create_app_meta_mime(NULL, true, true, false) == B_OK);
3920 //	cout << "end" << endl;
3921 
3922 	// tests:
3923 	// * create_app_meta_mime()
3924 
3925 	// Note:
3926 	// * Only synchronous calls are tested.
3927 	// * The recursive flag isn't tested -- the BeBook sais, it is unused.
3928 	// * Updating all apps is not tested as it takes too long.
3929 
3930 	// Create a couple of icons to play around with
3931 	char miniIcon1[256];
3932 	char miniIcon2[256];
3933 	for (int ch = 0; ch < 256; ch++) {
3934 		miniIcon1[ch] = ch;
3935 		miniIcon2[ch] = 255-ch;
3936 	}
3937 	char largeIcon1[1024];
3938 	char largeIcon2[1024];
3939 	for (int i = 0; i < 1024; i++) {
3940 		char ch = i % 256;
3941 		largeIcon1[i] = ch;
3942 		largeIcon2[i] = 255-ch;
3943 	}
3944 
3945 	// attributes and resources
3946 	NextSubTest();
3947 	execCommand(string("mkdir ") + testDir + "/subdir1 "
3948 				+ testDir + "/subdir2 "
3949 				+ testDir + "/subdir2/subsubdir1");
3950 	AppMimeTestFile files[] = {
3951 		// AppMimeTestFile(name, type, sig, rule, miniIcon, largeIcon)
3952 		AppMimeTestFile(string(testDir) + "/file1",
3953 						"",
3954 						"application/x-vnd.obos.mime.test.test1",
3955 						"0.0 ('abc')",
3956 						miniIcon1,
3957 						NULL),
3958 		AppMimeTestFile(string(testDir) + "/file2",
3959 						"text/x-source-code",
3960 						"application/x-vnd.obos.mime.test.test2",
3961 						"0.0 ('xyz')",
3962 						miniIcon2,
3963 						largeIcon2),
3964 		AppMimeTestFile(string(testDir) + "/file3",
3965 						"application/x-vnd.Be-elfexecutable",
3966 						"application/x-vnd.obos.mime.test.test3",
3967 						"0.0 ('rst')",
3968 						NULL,
3969 						largeIcon1),
3970 	};
3971 	const int fileCount = sizeof(files) / sizeof(AppMimeTestFile);
3972 //------------------------------------------------------------------------------
3973 // Synchronous calls
3974 //------------------------------------------------------------------------------
3975 	for (int32 i = 0; i < fileCount; i++) {
3976 		NextSubTest();
3977 		// create file, create_app_meta_mime()
3978 		AppMimeTestFile &file = files[i];
3979 		CHK(file.Create(true, true) == B_OK);
3980 		CHK(create_app_meta_mime(file.name.c_str(), false, true, false)
3981 			== B_OK);
3982 		// check the MIME type
3983 		CheckAppMetaMime(file);
3984 		// clean up
3985 		CHK(file.Delete(true) == B_OK);
3986 	}
3987 //	snooze(999000000);
3988 
3989 	// attributes only
3990 	for (int32 i = 0; i < fileCount; i++) {
3991 		NextSubTest();
3992 		// create file, create_app_meta_mime()
3993 		AppMimeTestFile &file = files[i];
3994 		CHK(file.Create(true, false) == B_OK);
3995 		CHK(create_app_meta_mime(file.name.c_str(), false, true, false)
3996 			== B_OK);
3997 		// check the MIME type
3998 		CheckAppMetaMime(file);
3999 		// clean up
4000 		CHK(file.Delete(true) == B_OK);
4001 	}
4002 
4003 	// resources only
4004 	for (int32 i = 0; i < fileCount; i++) {
4005 		NextSubTest();
4006 		// create file, create_app_meta_mime()
4007 		AppMimeTestFile &file = files[i];
4008 		CHK(file.Create(false, true) == B_OK);
4009 		CHK(create_app_meta_mime(file.name.c_str(), false, true, false)
4010 			== B_OK);
4011 		// check the MIME type
4012 		BMimeType type;
4013 		CHK(type.SetTo(file.signature.c_str()) == B_OK);
4014 		CHK(type.IsInstalled() == false);
4015 		// clean up
4016 		CHK(file.Delete(false) == B_OK);
4017 	}
4018 
4019 //------------------------------------------------------------------------------
4020 // Asynchronous calls
4021 //------------------------------------------------------------------------------
4022 	const bigtime_t kSnoozeTime = 500000;
4023 	for (int32 i = 0; i < fileCount; i++) {
4024 		NextSubTest();
4025 		// create file, create_app_meta_mime()
4026 		AppMimeTestFile &file = files[i];
4027 		CHK(file.Create(true, true) == B_OK);
4028 		CHK(create_app_meta_mime(file.name.c_str(), false, false, false)
4029 			== B_OK);
4030 		// give the system some time to do the update asynchronously
4031 		snooze(kSnoozeTime);
4032 		// check the MIME type
4033 		CheckAppMetaMime(file);
4034 		// clean up
4035 		CHK(file.Delete(true) == B_OK);
4036 	}
4037 
4038 	// attributes only
4039 	for (int32 i = 0; i < fileCount; i++) {
4040 		NextSubTest();
4041 		// create file, create_app_meta_mime()
4042 		AppMimeTestFile &file = files[i];
4043 		CHK(file.Create(true, false) == B_OK);
4044 		CHK(create_app_meta_mime(file.name.c_str(), false, false, false)
4045 			== B_OK);
4046 		// give the system some time to do the update asynchronously
4047 		snooze(kSnoozeTime);
4048 		// check the MIME type
4049 		CheckAppMetaMime(file);
4050 		// clean up
4051 		CHK(file.Delete(true) == B_OK);
4052 	}
4053 
4054 	// resources only
4055 	for (int32 i = 0; i < fileCount; i++) {
4056 		NextSubTest();
4057 		// create file, create_app_meta_mime()
4058 		AppMimeTestFile &file = files[i];
4059 		CHK(file.Create(false, true) == B_OK);
4060 		CHK(create_app_meta_mime(file.name.c_str(), false, false, false)
4061 			== B_OK);
4062 		// give the system some time to do the update asynchronously
4063 		snooze(kSnoozeTime);
4064 		BMimeType type;
4065 		CHK(type.SetTo(file.signature.c_str()) == B_OK);
4066 		CHK(type.IsInstalled() == false);
4067 		// clean up
4068 		CHK(file.Delete(false) == B_OK);
4069 	}
4070 
4071 
4072 	// test the force flag
4073 // TODO: The BeBook says: "If force is true, entries are created even if they
4074 // already exist."
4075 // As I understand this, re-calling create_app_meta_mime() with force == true,
4076 // after modifying the original file (e.g. the mini icon attribute) or
4077 // calling it on another file with the same signature, should update the
4078 // database entry. But the following tests show, that this doesn't happen.
4079 // They fail in the third CheckAppMetaMime().
4080 #if !TEST_R5
4081 	// same file, same signature, other parameters
4082 	{
4083 		char icon1[256];
4084 		char icon2[256];
4085 		memset(icon1, 1, 256);
4086 		memset(icon2, 2, 256);
4087 		AppMimeTestFile file1(string(testDir) + "/file1",
4088 						"application/x-vnd.Be-elfexecutable",
4089 						"application/x-vnd.obos.mime.test.test1",
4090 						icon1);
4091 		AppMimeTestFile file2(string(testDir) + "/file1",
4092 						"application/x-vnd.Be-elfexecutable",
4093 						"application/x-vnd.obos.mime.test.test1",
4094 						icon2);
4095 		// create file 1, create_app_meta_mime()
4096 		CHK(file1.Create(true, true) == B_OK);
4097 		CHK(create_app_meta_mime(file1.name.c_str(), false, true, false)
4098 			== B_OK);
4099 		// check the MIME type
4100 		CheckAppMetaMime(file1);
4101 		// create file 2, create_app_meta_mime(), no force
4102 		CHK(file2.Create(true, true) == B_OK);
4103 		CHK(create_app_meta_mime(file2.name.c_str(), false, true, false)
4104 			== B_OK);
4105 		// check the MIME type
4106 		CheckAppMetaMime(file1);
4107 		// create_app_meta_mime(), force
4108 		CHK(create_app_meta_mime(file2.name.c_str(), false, true, true)
4109 			== B_OK);
4110 		// check the MIME type
4111 		CheckAppMetaMime(file2);
4112 		// clean up
4113 		CHK(file2.Delete(true) == B_OK);
4114 	}
4115 	// different file, same signature, other parameters
4116 	{
4117 		char icon1[256];
4118 		char icon2[256];
4119 		memset(icon1, 1, 256);
4120 		memset(icon2, 2, 256);
4121 		AppMimeTestFile file1(string(testDir) + "/file1",
4122 						"application/x-vnd.Be-elfexecutable",
4123 						"application/x-vnd.obos.mime.test.test1",
4124 						icon1);
4125 		AppMimeTestFile file2(string(testDir) + "/file2",
4126 						"application/x-vnd.Be-elfexecutable",
4127 						"application/x-vnd.obos.mime.test.test1",
4128 						icon2);
4129 		// create file 1, create_app_meta_mime()
4130 		CHK(file1.Create(true, true) == B_OK);
4131 		CHK(create_app_meta_mime(file1.name.c_str(), false, true, false)
4132 			== B_OK);
4133 		// check the MIME type
4134 		CheckAppMetaMime(file1);
4135 		// create file 2, create_app_meta_mime(), no force
4136 		CHK(file2.Create(true, true) == B_OK);
4137 		CHK(create_app_meta_mime(file2.name.c_str(), false, true, false)
4138 			== B_OK);
4139 		// check the MIME type
4140 		CheckAppMetaMime(file1);
4141 		// create_app_meta_mime(), force
4142 		CHK(create_app_meta_mime(file2.name.c_str(), false, true, true)
4143 			== B_OK);
4144 		// check the MIME type
4145 		CheckAppMetaMime(file2);
4146 		// clean up
4147 		CHK(file1.Delete(true) == B_OK);
4148 		CHK(file2.Delete(true) == B_OK);
4149 	}
4150 #endif	// !TEST_R5
4151 
4152 	// bad args
4153 	NextSubTest();
4154 	// no signature
4155 	CHK(files[0].Create(false, false) == B_OK);
4156 	CHK(create_app_meta_mime(files[0].name.c_str(), false, true, false)
4157 		== B_OK);
4158 	CHK(files[0].Delete(false) == B_OK);
4159 	// non-existing file
4160 	CHK(create_app_meta_mime(files[0].name.c_str(), false, true, false)
4161 		== B_OK);
4162 
4163 }
4164 
4165 // CheckIconData
4166 static
4167 void
CheckIconData(const char * device,int32 iconSize,const void * data)4168 CheckIconData(const char *device, int32 iconSize, const void* data)
4169 {
4170 	// open the device
4171 	int fd = open(device, O_RDONLY);
4172 	CHK(fd != -1);
4173 	// get the icon
4174 	char buffer[1024];
4175 	device_icon iconData = {
4176 		iconSize,
4177 		buffer
4178 	};
4179 	int error = ioctl(fd, B_GET_ICON, &iconData);
4180 	// close the device
4181 	CHK(close(fd) == 0);
4182 	CHK(error == 0);
4183 	// compare the icon data
4184 	CHK(memcmp(data, buffer, iconSize * iconSize) == 0);
4185 }
4186 
4187 // GetDeviceIconTest
4188 void
GetDeviceIconTest()4189 MimeTypeTest::GetDeviceIconTest()
4190 {
4191 	// tests:
4192 	// * get_device_icon()
4193 
4194 	// test a volume device, a non-volume device, and an invalid dev name
4195 	struct test_case {
4196 		const char	*path;
4197 		bool		valid;
4198 	} testCases[] = {
4199 		{ "/dev/zero", false },
4200 		{ "/boot", true },
4201 		{ "/boot/home", false }
4202 	};
4203 	const int testCaseCount = sizeof(testCases) / sizeof(test_case);
4204 	for (int32 i = 0; i < testCaseCount; i++) {
4205 		NextSubTest();
4206 		test_case &testCase = testCases[i];
4207 		// get device name from path name
4208 		fs_info info;
4209 		const char *deviceName = testCase.path;
4210 		if (testCase.valid) {
4211 			dev_t dev = dev_for_path(testCase.path);
4212 			CHK(dev > 0);
4213 			CHK(fs_stat_dev(dev, &info) == 0);
4214 			deviceName = info.device_name;
4215 		}
4216 		// the two valid and one invalid icon size
4217 		const int32	iconSizes[] = { 16, 32, 20 };
4218 		const bool 	validSizes[] = { true, true, false };
4219 		const int sizeCount = sizeof(iconSizes) / sizeof(int32);
4220 		for (int32 k = 0; k < sizeCount; k++) {
4221 			int32 size = iconSizes[k];
4222 			bool valid = testCase.valid && validSizes[k];
4223 			char buffer[1024];
4224 			if (valid) {
4225 				CHK(get_device_icon(deviceName, buffer, size) == B_OK);
4226 				CheckIconData(deviceName, size, buffer);
4227 				// bad args: NULL buffer
4228 // R5: Wanna see KDL? Here you go...
4229 #if !TEST_R5
4230 				CHK(get_device_icon(deviceName, NULL, size) == B_BAD_VALUE);
4231 #endif
4232 			} else
4233 				CHK(get_device_icon(deviceName, buffer, size) != B_OK);
4234 		}
4235 	}
4236 }
4237 
4238 // SnifferRuleTest
4239 void
SnifferRuleTest()4240 MimeTypeTest::SnifferRuleTest()
4241 {
4242 	// tests:
4243 	// * status_t GetSnifferRule(BString *result) const;
4244 	// * status_t SetSnifferRule(const char *);
4245 	// * static status_t CheckSnifferRule(const char *rule, BString *parseError);
4246 
4247 	// test a couple of valid and invalid rules
4248 	struct test_case {
4249 		const char	*rule;
4250 		const char	*error;	// NULL, if valid
4251 	} testCases[] = {
4252 		// valid rules
4253 		{ "1.0 (\"ABCD\")", NULL },
4254 		{ "1.0 ('ABCD')", NULL },
4255 		{ "  1.0 ('ABCD')  ", NULL },
4256 		{ "0.8 [0:3] ('ABCDEFG' | 'abcdefghij')", NULL },
4257 		{ "0.5([10]'ABCD'|[17]'abcd'|[13]'EFGH')", NULL } ,
4258 		{ "0.5  \n   [0:3]  \t ('ABCD' \n | 'abcd' | 'EFGH')", NULL },
4259 		{ "0.8 [  0  :  3  ] ('ABCDEFG' | 'abcdefghij')", NULL },
4260 		{ "0.8 [0:3] ('ABCDEFG' & 'abcdefg')", NULL },
4261 // These two rules are accepted by the R5 sniffer checker, but not
4262 // by the parser. Thus, we're not accepting them with either.
4263 #if TEST_R5
4264 		{ "1.0 ('ABCD') | ('EFGH')", NULL },
4265 		{ "1.0 [0:3] ('ABCD') | [2:4] ('EFGH')", NULL },
4266 #else
4267 		{ "1.0 ('ABCD') | ('EFGH')", "Sniffer pattern error: missing pattern" },
4268 		{ "1.0 [0:3] ('ABCD') | [2:4] ('EFGH')", "Sniffer pattern error: missing pattern" },
4269 #endif
4270 		{ "0.8 [0:3] (\\077Mkl0x34 & 'abcdefgh')", NULL },
4271 		{ "0.8 [0:3] (\\077034 & 'abcd')", NULL },
4272 		{ "0.8 [0:3] (\\077\\034 & 'ab')", NULL },
4273 		{ "0.8 [0:3] (\\77\\034 & 'ab')", NULL },
4274 		{ "0.8 [0:3] (\\7 & 'a')", NULL },
4275 		{ "0.8 [0:3] (\"\\17\" & 'a')", NULL },
4276 		{ "0.8 [0:3] ('\\17' & 'a')", NULL },
4277 		{ "0.8 [0:3] (\\g & 'a')", NULL },
4278 		{ "0.8 [0:3] (\\g&\\b)", NULL },
4279 		{ "0.8 [0:3] (\\g\\&b & 'abc')", NULL },
4280 		{ "0.8 [0:3] (0x3457 & 'ab')", NULL },
4281 		{ "0.8 [0:3] (0xA4b7 & 'ab')", NULL },
4282 		{ "0.8 [0:3] ('ab\"' & 'abc')", NULL },
4283 		{ "0.8 [0:3] (\"ab\\\"\" & 'abc')", NULL },
4284 		{ "0.8 [0:3] (\"ab\\A\" & 'abc')", NULL },
4285 		{ "0.8 [0:3] (\"ab'\" & 'abc')", NULL },
4286 		{ "0.8 [0:3] (\"ab\\\\\" & 'abc')", NULL },
4287 		{ "0.8 [-5:-3] (\"abc\" & 'abc')", NULL },
4288 // Also accepted by the R5 sniffer but not the R5 parser. We reject.
4289 #if TEST_R5
4290 		{ "0.8 [5:3] (\"abc\" & 'abc')", NULL },
4291 #else
4292 		{ "0.8 [5:3] (\"abc\" & 'abc')", "Sniffer Parser Error -- Invalid range: [5:3]" },
4293 #endif
4294 		{ "1.0 ('ABCD')", NULL },
4295 		{ ".2 ('ABCD')", NULL },
4296 		{ "0. ('ABCD')", NULL },
4297 		{ "1 ('ABCD')", NULL },
4298 		{ "+1 ('ABCD')", NULL },
4299 // We accept extended notation floating point numbers now, but
4300 // not invalid priorities. Thus our checker chokes on these rules,
4301 // whilest R5's does not
4302 #if TEST_R5
4303 		{ "1E25 ('ABCD')", NULL },
4304 		{ "1e25 ('ABCD')", NULL },
4305 #else
4306 		{ "1E25 ('ABCD')", "Sniffer pattern error: invalid priority" },
4307 		{ "1e25 ('ABCD')", "Sniffer pattern error: invalid priority" },
4308 #endif
4309 
4310 // R5 chokes on this rule :-( Why? I don't know. :-)
4311 #if TEST_R5
4312 		{ "1e-3 ('ABCD')", "Sniffer pattern error: missing pattern" },
4313 #else
4314 		{ "1e-3 ('ABCD')", NULL },
4315 #endif
4316 		{ "+.003e2 ('ABCD')", NULL },
4317 // R5 chokes on this one too. See how much better our checker/parser is? ;-)
4318 #if TEST_R5
4319 		{ "-123e-9999999999 ('ABCD')", "Sniffer pattern error: bad token" },	// Hooray for the stunning accuracy of floating point :-)
4320 #else
4321 		{ "-123e-9999999999 ('ABCD')", NULL },	// Hooray for the stunning accuracy of floating point :-)
4322 #endif
4323 		// invalid rules
4324 		{ "0.0 ('')",
4325 			"Sniffer pattern error: illegal empty pattern" },
4326 		{ "('ABCD')",
4327 			"Sniffer pattern error: match level expected" },
4328 		{ "[0:3] ('ABCD')",
4329 			"Sniffer pattern error: match level expected" },
4330 		{ "0.8 [0:3] ( | 'abcdefghij')",
4331 		  "Sniffer pattern error: missing pattern" },
4332 		{ "0.8 [0:3] ('ABCDEFG' | )",
4333 		  "Sniffer pattern error: missing pattern" },
4334 		{ "[0:3] ('ABCD')",
4335 			"Sniffer pattern error: match level expected" },
4336 		{ "1.0 (ABCD')",
4337 #if TEST_R5
4338 			"Sniffer pattern error: misplaced single quote"
4339 #else
4340 			"Sniffer pattern error: invalid character 'A'"
4341 #endif
4342 		},
4343 		{ "1.0 ('ABCD)",
4344 #if TEST_R5
4345 			"Sniffer pattern error: unterminated rule"
4346 #else
4347 			"Sniffer pattern error: unterminated single-quoted string"
4348 #endif
4349 		},
4350 		{ "1.0 (ABCD)",
4351 #if TEST_R5
4352 			"Sniffer pattern error: missing pattern"
4353 #else
4354 			"Sniffer pattern error: invalid character 'A'"
4355 #endif
4356 		},
4357 		{ "1.0 (ABCD 'ABCD')",
4358 #if TEST_R5
4359 			"Sniffer pattern error: missing pattern"
4360 #else
4361 			"Sniffer pattern error: invalid character 'A'"
4362 #endif
4363 		},
4364 		{ "1.0 'ABCD')",
4365 #if TEST_R5
4366 			"Sniffer pattern error: missing pattern"
4367 #else
4368 			"Sniffer pattern error: missing pattern"
4369 #endif
4370 		},
4371 		{ "1.0 ('ABCD'",
4372 			"Sniffer pattern error: unterminated rule" },
4373 		{ "1.0 'ABCD'",
4374 #if TEST_R5
4375 			"Sniffer pattern error: missing sniff pattern"
4376 #else
4377 			"Sniffer pattern error: missing pattern"
4378 #endif
4379 		},
4380 		{ "0.5 [0:3] ('ABCD' | 'abcd' | [13] 'EFGH')",
4381 		  	"Sniffer pattern error: missing pattern" },
4382 		{ "0.5('ABCD'|'abcd'|[13]'EFGH')",
4383 		  	"Sniffer pattern error: missing pattern" },
4384 		{ "0.5[0:3]([10]'ABCD'|[17]'abcd'|[13]'EFGH')",
4385 		  	"Sniffer pattern error: missing pattern" },
4386 		{ "0.8 [0x10:3] ('ABCDEFG' | 'abcdefghij')",
4387 		  	"Sniffer pattern error: pattern offset expected" },
4388 		{ "0.8 [0:A] ('ABCDEFG' | 'abcdefghij')",
4389 #if TEST_R5
4390 		  	"Sniffer pattern error: pattern range end expected"
4391 #else
4392 			"Sniffer pattern error: invalid character 'A'"
4393 #endif
4394 		},
4395 		{ "0.8 [0:3] ('ABCDEFG' & 'abcdefghij')",
4396 		  	"Sniffer pattern error: pattern and mask lengths do not match" },
4397 		{ "0.8 [0:3] ('ABCDEFG' & 'abcdefg' & 'xyzwmno')",
4398 #if TEST_R5
4399 		  	"Sniffer pattern error: unterminated rule"
4400 #else
4401 			"Sniffer pattern error: expecting '|', ')', or possibly '&'"
4402 #endif
4403 		},
4404 		{ "0.8 [0:3] (\\g&b & 'a')",
4405 #if TEST_R5
4406 			"Sniffer pattern error: missing mask"
4407 #else
4408 			"Sniffer pattern error: invalid character 'b'"
4409 #endif
4410 		},
4411 		{ "0.8 [0:3] (\\19 & 'a')",
4412 		  	"Sniffer pattern error: pattern and mask lengths do not match" },
4413 		{ "0.8 [0:3] (0x345 & 'ab')",
4414 		  	"Sniffer pattern error: bad hex literal" },
4415 		{ "0.8 [0:3] (0x3457M & 'abc')",
4416 #if TEST_R5
4417 		  	"Sniffer pattern error: expecting '|' or '&'"
4418 #else
4419 			"Sniffer pattern error: invalid character 'M'"
4420 #endif
4421 		},
4422 		{ "0.8 [0:3] (0x3457\\7 & 'abc')",
4423 #if TEST_R5
4424 		  	"Sniffer pattern error: expecting '|' or '&'"
4425 #else
4426 			"Sniffer pattern error: expecting '|', ')', or possibly '&'"
4427 #endif
4428 		},
4429 
4430 		// Miscellaneous tests designed to hit every remaining
4431 		// relevant "throw new Err()" statement in our scanner.
4432 		// R5 versions may come later, but I don't really see any
4433 		// good reason why at this point...
4434 #if !TEST_R5
4435 		{ "\x03  ", "Sniffer pattern error: invalid character '\x03'" },
4436 		{ "\"blah", "Sniffer pattern error: unterminated double-quoted string" },
4437 		{ "0xThisIsNotAHexCode", "Sniffer pattern error: incomplete hex code" },
4438 		{ "0xAndNeitherIsThis:-)", "Sniffer pattern error: bad hex literal" },
4439 		{ ".NotAFloat", "Sniffer pattern error: incomplete floating point number" },
4440 		{ "-NotANumber", "Sniffer pattern error: incomplete signed number" },
4441 		{ "+NotANumber", "Sniffer pattern error: incomplete signed number" },
4442 
4443 		{ "0.0e", "Sniffer pattern error: incomplete extended-notation floating point number" },
4444 		{ "1.0e", "Sniffer pattern error: incomplete extended-notation floating point number" },
4445 		{ ".0e", "Sniffer pattern error: incomplete extended-notation floating point number" },
4446 		{ "0e", "Sniffer pattern error: incomplete extended-notation floating point number" },
4447 		{ "1e", "Sniffer pattern error: incomplete extended-notation floating point number" },
4448 		{ "-1e", "Sniffer pattern error: incomplete extended-notation floating point number" },
4449 		{ "+1e", "Sniffer pattern error: incomplete extended-notation floating point number" },
4450 		{ "-1.e", "Sniffer pattern error: incomplete extended-notation floating point number" },
4451 		{ "+1.e", "Sniffer pattern error: incomplete extended-notation floating point number" },
4452 		{ "-1.0e", "Sniffer pattern error: incomplete extended-notation floating point number" },
4453 		{ "+1.0e", "Sniffer pattern error: incomplete extended-notation floating point number" },
4454 
4455 		{ "0.0e-", "Sniffer pattern error: incomplete extended-notation floating point number" },
4456 		{ "1.0e-", "Sniffer pattern error: incomplete extended-notation floating point number" },
4457 		{ ".0e-", "Sniffer pattern error: incomplete extended-notation floating point number" },
4458 		{ "0e-", "Sniffer pattern error: incomplete extended-notation floating point number" },
4459 		{ "1e-", "Sniffer pattern error: incomplete extended-notation floating point number" },
4460 		{ "-1e-", "Sniffer pattern error: incomplete extended-notation floating point number" },
4461 		{ "+1e-", "Sniffer pattern error: incomplete extended-notation floating point number" },
4462 		{ "-1.e-", "Sniffer pattern error: incomplete extended-notation floating point number" },
4463 		{ "+1.e-", "Sniffer pattern error: incomplete extended-notation floating point number" },
4464 		{ "-1.0e-", "Sniffer pattern error: incomplete extended-notation floating point number" },
4465 		{ "+1.0e-", "Sniffer pattern error: incomplete extended-notation floating point number" },
4466 
4467 		{ "0.0e+", "Sniffer pattern error: incomplete extended-notation floating point number" },
4468 		{ "1.0e+", "Sniffer pattern error: incomplete extended-notation floating point number" },
4469 		{ ".0e+", "Sniffer pattern error: incomplete extended-notation floating point number" },
4470 		{ "0e+", "Sniffer pattern error: incomplete extended-notation floating point number" },
4471 		{ "1e+", "Sniffer pattern error: incomplete extended-notation floating point number" },
4472 		{ "-1e+", "Sniffer pattern error: incomplete extended-notation floating point number" },
4473 		{ "+1e+", "Sniffer pattern error: incomplete extended-notation floating point number" },
4474 		{ "-1.e+", "Sniffer pattern error: incomplete extended-notation floating point number" },
4475 		{ "+1.e+", "Sniffer pattern error: incomplete extended-notation floating point number" },
4476 		{ "-1.0e+", "Sniffer pattern error: incomplete extended-notation floating point number" },
4477 		{ "+1.0e+", "Sniffer pattern error: incomplete extended-notation floating point number" },
4478 
4479 		{ "\\11\\", "Sniffer pattern error: incomplete escape sequence" },
4480 		{ "\"Escape!! \\", "Sniffer pattern error: incomplete escape sequence" },
4481 		{ "'Escape!! \\", "Sniffer pattern error: incomplete escape sequence" },
4482 
4483 		{ "\\x", "Sniffer pattern error: incomplete escaped hex code" },
4484 		{ "\\xNotAHexCode", "Sniffer pattern error: incomplete escaped hex code" },
4485 		{ "\\xAlsoNotAHexCode", "Sniffer pattern error: incomplete escaped hex code" },
4486 		{ "\\x0", "Sniffer pattern error: incomplete escaped hex code" },
4487 
4488 		{ "1.0 (\\377)", NULL },
4489 		{ "\\400", "Sniffer pattern error: invalid octal literal (octals must be between octal 0 and octal 377 inclusive)" },
4490 		{ "\\777", "Sniffer pattern error: invalid octal literal (octals must be between octal 0 and octal 377 inclusive)" },
4491 		{ "1.0 (\\800)", NULL },
4492 
4493 		{ NULL, "Sniffer pattern error: NULL pattern" },
4494 
4495 		{ "-2", "Sniffer pattern error: invalid priority" },
4496 		{ "+2", "Sniffer pattern error: invalid priority" },
4497 
4498 		{ "1.0", "Sniffer pattern error: missing expression" },
4499 #endif	// !TEST_R5
4500 
4501 //! \todo Our parser chokes on this rule and I have no idea why
4502 // I don't currently understand what's wrong with the following rule...
4503 // R5 rejects it though, for whatever reason.
4504 #if TEST_R5
4505 		{ "1E-25 ('ABCD')", "Sniffer pattern error: missing pattern" },
4506 #else
4507 //		{ "1E-25 ('ABCD')", NULL },
4508 #endif
4509 	};
4510 
4511 	const int testCaseCount = sizeof(testCases) / sizeof(test_case);
4512 	BMimeType type;
4513 	CHK(type.SetTo(testType) == B_OK);
4514 	CHK(type.Install() == B_OK);
4515 	for (int32 i = 0; i < testCaseCount; i++) {
4516 		NextSubTest();
4517 		test_case &testCase = testCases[i];
4518 		BString parseError;
4519 		status_t error = BMimeType::CheckSnifferRule(testCase.rule,
4520 													 &parseError);
4521 //		printf("\n---------------------\n");
4522 //		printf("rule == '%s', %s\n", testCase.rule, (testCase.error ? "should not pass" : "should pass"));
4523 		if (testCase.error == NULL) {
4524 if (error != B_OK)
4525 printf("\nerror:\n%s\n", parseError.String());
4526 			CHK(error == B_OK);
4527 			CHK(type.SetSnifferRule(testCase.rule) == B_OK);
4528 			BString rule;
4529 			CHK(type.GetSnifferRule(&rule) == B_OK);
4530 			CHK(rule == testCase.rule);
4531 		} else {
4532 //			printf("error == 0x%lx\n", error);
4533 //			if (parseError.FindLast(testCase.error) < 0) {
4534 //				printf("\nexpected:\n%s\n", testCase.error);
4535 //				printf("\nfound:\n%s\n", parseError.String());
4536 //			}
4537 			CHK(error == (testCase.rule ? B_BAD_MIME_SNIFFER_RULE : B_BAD_VALUE));
4538 			CHK(parseError.FindLast(testCase.error) >= 0);
4539 
4540 // R5 treats a NULL rule string as an error, and thus R5::SetSnifferRule(NULL) fails.
4541 // We also treat a NULL rule string as an error, but OBOS::SetSnifferRule(NULL) does
4542 // not fail, as all OBOS::BMimeType::Set*(NULL) calls are equivalent to the
4543 // corresponding OBOS::BMimeType::Delete*() calls.
4544 #if TEST_R5
4545 			CHK(type.SetSnifferRule(testCase.rule) == B_BAD_MIME_SNIFFER_RULE);
4546 #else
4547 			if (testCase.rule)
4548 				CHK(type.SetSnifferRule(testCase.rule) == B_BAD_MIME_SNIFFER_RULE);
4549 			else
4550 				CHK(type.SetSnifferRule(testCase.rule) == B_OK);
4551 #endif
4552 		}
4553 	}
4554 
4555 	// bad args: NULL rule/result string
4556 	NextSubTest();
4557 	BString parseError;
4558 	CHK(BMimeType::CheckSnifferRule("0.0 ('')", NULL)
4559 		== B_BAD_MIME_SNIFFER_RULE);
4560 // R5: crashes when passing a NULL rule/result buffer.
4561 #if !TEST_R5
4562 	CHK(BMimeType::CheckSnifferRule(NULL, &parseError) == B_BAD_VALUE);
4563 	CHK(BMimeType::CheckSnifferRule(NULL, NULL) == B_BAD_VALUE);
4564 	CHK(type.GetSnifferRule(NULL) == B_BAD_VALUE);
4565 #endif
4566 
4567 	BString rule;
4568 
4569 	// NULL rule to SetSnifferRule unsets the attribute
4570 	NextSubTest();
4571 #if TEST_R5
4572 	CHK(type.IsInstalled() == true);
4573 	CHK(type.SetSnifferRule(NULL) == B_OK);
4574 	CHK(type.GetSnifferRule(&rule) == B_ENTRY_NOT_FOUND);
4575 #else
4576 	CHK(type.IsInstalled() == true);
4577 	if (type.GetSnifferRule(&rule) == B_ENTRY_NOT_FOUND)
4578 		CHK(type.SetSnifferRule("0.0 ('abc')") == B_OK);
4579 	CHK(type.GetSnifferRule(&rule) == B_OK);
4580 	CHK(type.SetSnifferRule(NULL) == B_OK);
4581 	CHK(type.GetSnifferRule(&rule) == B_ENTRY_NOT_FOUND);
4582 	CHK(type.SetSnifferRule(NULL) == B_ENTRY_NOT_FOUND);
4583 #endif
4584 
4585 	// bad args: uninstalled type
4586 	CHK(type.Delete() == B_OK);
4587 	CHK(type.GetSnifferRule(&rule) == B_ENTRY_NOT_FOUND);
4588 	CHK(type.SetSnifferRule("0.0 ('ABC')") == B_OK);
4589 #if TEST_R5
4590 	CHK(type.GetSnifferRule(&rule) == B_ENTRY_NOT_FOUND);
4591 #else
4592 	CHK(type.GetSnifferRule(&rule) == B_OK);
4593 #endif
4594 
4595 	// bad args: uninitialized BMimeType
4596 	type.Unset();
4597 	CHK(type.GetSnifferRule(&rule) != B_OK);
4598 	CHK(type.SetSnifferRule("0.0 ('ABC')") != B_OK);
4599 }
4600 
4601 // helper class for GuessMimeType() tests
4602 class SniffingTestFile {
4603 public:
SniffingTestFile(string name,string extensionType,string contentType,const void * data=NULL,int32 size=-1,string metaType="")4604 	SniffingTestFile(string name, string extensionType, string contentType,
4605 					 const void *data = NULL, int32 size = -1, string metaType = "")
4606 		: name(name)
4607 		, extensionType(extensionType)
4608 		, contentType(contentType)
4609 		, data(NULL)
4610 		, size(0)
4611 		, metaType(metaType)
4612 	{
4613 		// replace wildcard types
4614 		if (this->extensionType == "")
4615 			this->extensionType = "application/octet-stream";
4616 		if (this->contentType == "")
4617 			this->contentType = "application/octet-stream";
4618 		// copy data
4619 		if (data) {
4620 			if (size == -1)
4621 				this->size = strlen((const char*)data) + 1;
4622 			else
4623 				this->size = size;
4624 			this->data = new char[this->size];
4625 			memcpy(this->data, data, this->size);
4626 		}
4627 	}
4628 
~SniffingTestFile()4629 	~SniffingTestFile()
4630 	{
4631 		delete[] data;
4632 	}
4633 
Create()4634 	status_t Create()
4635 	{
4636 		BFile file(name.c_str(), B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
4637 		ssize_t error = file.InitCheck();
4638 		if (error == B_OK && data) {
4639 			ssize_t written = file.Write(data, size);
4640 			if (written < 0)
4641 				error = written;
4642 			else if (written != size)
4643 				error = B_ERROR;
4644 		}
4645 		if (!error && metaType.length() > 0) {
4646 			error = file.WriteAttr("META:TYPE", B_STRING_TYPE, 0, metaType.c_str(),
4647 				metaType.length()+1);
4648 			error = error == (ssize_t)(metaType.length()+1) ? B_OK : error;
4649 		}
4650 		return error;
4651 	}
4652 
Delete()4653 	status_t Delete()
4654 	{
4655 		return BEntry(name.c_str()).Remove();
4656 	}
4657 
4658 	string	name;
4659 	string	extensionType;
4660 	string	contentType;
4661 	char	*data;
4662 	int32	size;
4663 	string metaType;
4664 };
4665 
4666 // SniffingTest
4667 void
SniffingTest()4668 MimeTypeTest::SniffingTest()
4669 {
4670 	// tests:
4671 	// * GuessMimeType()
4672 
4673 	// install some test types with sniffer rules
4674 	{
4675 		BMimeType type;
4676 		CHK(type.SetTo(testType) == B_OK);
4677 		CHK(type.Install() == B_OK);
4678 		CHK(type.SetSnifferRule("0.5 [0:1] ('ABCD_EFGH' & 0xffffffff00ffffffff)")
4679 			== B_OK);
4680 		CHK(type.SetTo(testType1) == B_OK);
4681 		CHK(type.Install() == B_OK);
4682 		CHK(type.SetSnifferRule("0.4 ('ABCD')") == B_OK);
4683 		CHK(type.SetTo(testType2) == B_OK);
4684 		CHK(type.Install() == B_OK);
4685 #if TEST_R5
4686 		// This rule is invalid!
4687 		CHK(type.SetSnifferRule("0.4 [0] ('XYZ') | [0:5] ('CD  E')") == B_OK);
4688 #else
4689 //		CHK(type.SetSnifferRule("0.4 ([0] 'XYZ' | [0:5] 'CD  E')") == B_OK);
4690 #endif
4691 		CHK(type.SetTo(testType3) == B_OK);
4692 		CHK(type.Install() == B_OK);
4693 		CHK(type.SetSnifferRule("0.3 [0:8] ('ABCD' | 'EFGH')") == B_OK);
4694 		CHK(type.SetTo(testType4) == B_OK);
4695 		CHK(type.Install() == B_OK);
4696 		CHK(type.SetSnifferRule("0.2 [0:3] ('ABCD' | 'abcd')") == B_OK);
4697 		CHK(type.SetTo(testType5) == B_OK);
4698 		CHK(type.Install() == B_OK);
4699 		CHK(type.SetSnifferRule("0.2 ('LMNO' & 0xfffeffff)") == B_OK);
4700 	}
4701 
4702 	SniffingTestFile files[] = {
4703 		SniffingTestFile(string(testDir) + "/file1.cpp",
4704 						 "text/x-source-code", ""),
4705 		SniffingTestFile(string(testDir) + "/file2.gif",
4706 						 "image/gif", ""),
4707 		SniffingTestFile(string(testDir) + "/file3",
4708 						 "", "text/html",
4709 						 "<html>\n<body>\n</body></html>\n"),
4710 		SniffingTestFile(string(testDir) + "/file4.cpp",
4711 						 "text/x-source-code", "text/html",
4712 						 "<html>\n<body>\n</body></html>\n"),
4713 		SniffingTestFile(string(testDir) + "/file5", "", testType1, "ABCD"),
4714 		SniffingTestFile(string(testDir) + "/file6", "", testType3, " ABCD"),
4715 		SniffingTestFile(string(testDir) + "/file7", "", testType4, "abcd"),
4716 		SniffingTestFile(string(testDir) + "/file8", "", testType3,
4717 						 " ABCDEFGH"),
4718 		SniffingTestFile(string(testDir) + "/file9", "", testType,
4719 						 " ABCD EFGH"),
4720 //		SniffingTestFile(string(testDir) + "/file10", "", testType2,
4721 		SniffingTestFile(string(testDir) + "/file10", "", testType3,
4722 						 " ABCD  EFGH"),
4723 		SniffingTestFile(string(testDir) + "/file11", "", testType5,
4724 						 "LMNO"),
4725 		SniffingTestFile(string(testDir) + "/file12", "", testType5,
4726 						 "LLNO"),
4727 		SniffingTestFile(string(testDir) + "/file13", "", "",
4728 						 "LNNO"),
4729 		// meta mime test
4730 // bonefish: TODO: Now that content sniffing is enabled again, this doesn't
4731 // work properly anymore, since there are actually three types involved: The
4732 // extension type ("text/html"), the content type (also "text/html") and the
4733 // real type ("application/x-vnd.be-meta-mime") which is concluded from the
4734 // existence of the "META:TYPE" attribute rather than the extension or content.
4735 //#if !TEST_R5
4736 //		SniffingTestFile(string(testDir) + "/file14.html",
4737 //						 "text/html", "application/x-vnd.be-meta-mime",
4738 //						 "<html>\n<body>\n</body></html>\n", -1,
4739 //						 "fake-meta-mime-string"),
4740 //#endif	// !TEST_R5
4741 	};
4742 	int fileCount = sizeof(files) / sizeof(SniffingTestFile);
4743 	for (int32 i = 0; i < fileCount; i++) {
4744 		NextSubTest();
4745 		SniffingTestFile &file = files[i];
4746 		const char *filename = file.name.c_str();
4747 //printf("file: %s\n", filename);
4748 		const char *extensionType = file.extensionType.c_str();
4749 		const char *contentType = file.contentType.c_str();
4750 		const char *realType = contentType;
4751 		if (file.contentType == "application/octet-stream")
4752 			realType = extensionType;
4753 		// GuessMimeType(const char*,)
4754 		BMimeType type;
4755 		CHK(BMimeType::GuessMimeType(filename, &type) == B_OK);
4756 //printf("type: `%s', extensionType: `%s'\n", type.Type(), extensionType);
4757 		CHK(type == extensionType);
4758 		type.Unset();
4759 		// GuessMimeType(const void*, int32,)
4760 		if (file.data != NULL) {
4761 			CHK(BMimeType::GuessMimeType(file.data, file.size, &type) == B_OK);
4762 if (!(type == contentType))
4763 printf("type: %s, should be: %s\n", type.Type(), realType);
4764 			CHK(type == contentType);
4765 			type.Unset();
4766 		}
4767 		CHK(file.Create() == B_OK);
4768 		// set BEOS:TYPE to something confusing ;-)
4769 		BNode node;
4770 		CHK(node.SetTo(filename) == B_OK);
4771 		CHK(WriteStringAttr(node, "BEOS:TYPE", "application/x-person") == B_OK);
4772 		// GuessMimeType(const ref*,)
4773 		entry_ref ref;
4774 		CHK(get_ref_for_path(filename, &ref) == B_OK);
4775 		CHK(BMimeType::GuessMimeType(&ref, &type) == B_OK);
4776 if (!(type == realType))
4777 printf("type: %s, should be: %s (file == '%s')\n", type.Type(), realType, filename);
4778 		CHK(type == realType);
4779 		type.Unset();
4780 		CHK(file.Delete() == B_OK);
4781 	}
4782 
4783 	// GuessMimeType(const ref*,), invalid/abstract entry
4784 	{
4785 		NextSubTest();
4786 		string filename = string(testDir) + "/file100.cpp";
4787 		BMimeType type;
4788 		entry_ref ref;
4789 // invalid entry_ref: R5: Is fine! Haiku: no dice
4790 #if TEST_R5
4791 		CHK(BMimeType::GuessMimeType(&ref, &type) == B_OK);
4792 		CHK(type == "application/octet-stream");
4793 #else
4794 		CHK(BMimeType::GuessMimeType(&ref, &type) != B_OK);
4795 #endif
4796 		// abstract entry_ref
4797 		CHK(get_ref_for_path(filename.c_str(), &ref) == B_OK);
4798 		// R5: B_NAME_NOT_FOUND, Haiku:
4799 		CHK(BMimeType::GuessMimeType(&ref, &type) != B_OK);
4800 	}
4801 
4802 	// bad args
4803 	{
4804 		NextSubTest();
4805 		SniffingTestFile &file = files[0];
4806 		CHK(file.Create() == B_OK);
4807 		const char *filename = file.name.c_str();
4808 		entry_ref ref;
4809 		CHK(get_ref_for_path(filename, &ref) == B_OK);
4810 		BMimeType type;
4811 		// NULL BMimeType
4812 		CHK(BMimeType::GuessMimeType(filename, NULL) == B_BAD_VALUE);
4813 		CHK(BMimeType::GuessMimeType(file.data, file.size, NULL)
4814 			== B_BAD_VALUE);
4815 		CHK(BMimeType::GuessMimeType(&ref, NULL) == B_BAD_VALUE);
4816 		// NULL filename/ref/data
4817 		CHK(BMimeType::GuessMimeType((const char*)NULL, &type) == B_BAD_VALUE);
4818 		CHK(BMimeType::GuessMimeType(NULL, 10, &type) == B_BAD_VALUE);
4819 		CHK(BMimeType::GuessMimeType((const entry_ref*)NULL, &type)
4820 			== B_BAD_VALUE);
4821 		// NULL BMimeType and filename/ref/data
4822 		CHK(BMimeType::GuessMimeType((const char*)NULL, NULL) == B_BAD_VALUE);
4823 		CHK(BMimeType::GuessMimeType(NULL, 10, NULL) == B_BAD_VALUE);
4824 		CHK(BMimeType::GuessMimeType((const entry_ref*)NULL, NULL)
4825 			== B_BAD_VALUE);
4826 		CHK(file.Delete() == B_OK);
4827 	}
4828 }
4829 
4830 
4831 /* KEY:
4832    + == Tests implemented
4833    * == Function implemented
4834 */
4835 
4836 /* Ingo's functions:
4837 
4838 	// initialization
4839 +*	BMimeType();
4840 +*	BMimeType(const char *mimeType);
4841 (*	virtual ~BMimeType();)
4842 
4843 +*	status_t SetTo(const char *mimeType);
4844 +*	status_t SetType(const char *mimeType);
4845 +*	void Unset();
4846 +*	status_t InitCheck() const;
4847 
4848 	// string access
4849 +*	const char *Type() const;
4850 +*	bool IsValid() const;
4851 +*	static bool IsValid(const char *mimeType);
4852 +*	bool IsSupertypeOnly() const;
4853 +*	status_t GetSupertype(BMimeType *superType) const;
4854 +*	bool Contains(const BMimeType *type) const;
4855 +*	bool operator==(const BMimeType &type) const;
4856 +*	bool operator==(const char *type) const;
4857 
4858 	// MIME database monitoring
4859 +	static status_t StartWatching(BMessenger target);
4860 +	static status_t StopWatching(BMessenger target);
4861 
4862 	// C functions
4863 +	int update_mime_info(const char *path, int recursive, int synchronous,
4864 						 int force);
4865 +	status_t create_app_meta_mime(const char *path, int recursive,
4866 								  int synchronous, int force);
4867 +	status_t get_device_icon(const char *dev, void *icon, int32 size);
4868 
4869 	// sniffer rule manipulation
4870 +	status_t GetSnifferRule(BString *result) const;
4871 +	status_t SetSnifferRule(const char *);
4872 +	static status_t CheckSnifferRule(const char *rule, BString *parseError);
4873 
4874 	// sniffing
4875 +	status_t GuessMimeType(const entry_ref *file, BMimeType *result);
4876 +	static status_t GuessMimeType(const void *buffer, int32 length,
4877 								  BMimeType *result);
4878 +	static status_t GuessMimeType(const char *filename, BMimeType *result);
4879 */
4880 
4881 
4882 /* Tyler's functions:
4883 
4884 	// MIME database access
4885 +	status_t Install();
4886 +	status_t Delete();
4887 +	bool IsInstalled() const;
4888 +	status_t GetIcon(BBitmap *icon, icon_size size) const;
4889 +	status_t GetPreferredApp(char *signature, app_verb verb = B_OPEN) const;
4890 +	status_t GetAttrInfo(BMessage *info) const;
4891 +	status_t GetFileExtensions(BMessage *extensions) const;
4892 +	status_t GetShortDescription(char *description) const;
4893 +	status_t GetLongDescription(char *description) const;
4894 	status_t GetSupportingApps(BMessage *signatures) const;
4895 
4896 +	status_t SetIcon(const BBitmap *icon, icon_size size);
4897 +	status_t SetPreferredApp(const char *signature, app_verb verb = B_OPEN);
4898 +	status_t SetAttrInfo(const BMessage *info);
4899 +	status_t SetFileExtensions(const BMessage *extensions);
4900 +	status_t SetShortDescription(const char *description);
4901 +	status_t SetLongDescription(const char *description);
4902 
4903 +	static status_t GetInstalledSupertypes(BMessage *super_types);
4904 +	static status_t GetInstalledTypes(BMessage *types);
4905 +	static status_t GetInstalledTypes(const char *super_type,
4906 									  BMessage *subtypes);
4907 +	static status_t GetWildcardApps(BMessage *wild_ones);
4908 
4909 +	status_t GetAppHint(entry_ref *ref) const;
4910 +	status_t SetAppHint(const entry_ref *ref);
4911 
4912 +	status_t GetIconForType(const char *type, BBitmap *icon,
4913 							icon_size which) const;
4914 +	status_t SetIconForType(const char *type, const BBitmap *icon,
4915 							icon_size which);
4916 */
4917 
4918 
4919