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
37static const char *testDir				= "/tmp/mimeTestDir";
38static const char *R5DatabaseDir		= "/boot/home/config/settings/beos_mime";
39#if TEST_R5
40static std::string mimeDatabaseDir		= R5DatabaseDir;
41#else
42static std::string mimeDatabaseDir		= BPrivate::Storage::Mime::kDatabaseDir;
43#endif
44
45// MIME Test Types
46// testType and testTypeApp are Delete()d after each test.
47static const char *testType				= "text/x-vnd.obos-Storage-Kit-Test";
48static const char *testType1			= "text/x-vnd.obos-Storage-Kit-Test1";
49static const char *testType2			= "text/x-vnd.obos-Storage-Kit-Test2";
50static const char *testType3			= "text/x-vnd.obos-Storage-Kit-Test3";
51static const char *testType4			= "text/x-vnd.obos-Storage-Kit-Test4";
52static const char *testType5			= "text/x-vnd.obos-Storage-Kit-Test5";
53static const char *testTypeApp			= "application/StorageKit-Test";
54static const char *testTypeApp1			=  "application/"
55										   "x-vnd.obos.mime.test.test1";
56static const char *testTypeApp2			=  "application/"
57										   "x-vnd.obos.mime.test.test2";
58static const char *testTypeApp3			=  "application/"
59										   "x-vnd.obos.mime.test.test3";
60static const char *testTypeInvalid		= "text/Are spaces valid?";
61static const char *testTypeSuperValid	= "valid-but-fake-supertype";
62static const char *testTypeSuperInvalid	= "?????";
63
64// Real MIME types
65static const char *wildcardType			= "application/octet-stream";
66static const char *applicationSupertype	= "application";
67
68// Application Paths
69static const char *testApp				= "/boot/beos/apps/SoundRecorder";
70static const char *testApp2				= "/boot/beos/apps/CDPlayer";
71static const char *fakeTestApp			= "/__this_isn't_likely_to_exist__";
72
73// BMessage field names
74static const char *applicationsField		= "applications";
75static const char *typeField				= "type";
76static const char *typesField				= "types";
77static const char *fileExtField				= "extensions";
78static const char *attrInfoField_Name		= "attr:name";
79static const char *attrInfoField_PublicName	= "attr:public_name";
80static const char *attrInfoField_Type		= "attr:type";
81static const char *attrInfoField_Viewable	= "attr:viewable";
82static const char *attrInfoField_Editable	= "attr:editable";
83
84// Descriptions
85static const char *testDescr			= "Just a test, nothing more :-)";
86static const char *testDescr2			= "Another amazing test string";
87static 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
94static const char *testSig				= "application/x-vnd.obos.mime-type-test";
95static const char *testSig2				= "application/x-vnd.obos.mime-type-test-2";
96static 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
103static bool operator==(BBitmap &bmp1, BBitmap &bmp2);
104static bool operator!=(BBitmap &bmp1, BBitmap &bmp2);
105static bool operator==(BMessage &msg1, BMessage &msg2);
106static bool operator!=(BMessage &msg1, BMessage &msg2);
107static 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");
113static void to_lower(const char *str, std::string &result);
114static std::string to_lower(const char *str);
115static void remove_type(const char *type, const char *databaseDir = mimeDatabaseDir.c_str());
116static bool type_exists(const char *type, const char *databaseDir = mimeDatabaseDir.c_str());
117class ContainerAdapter;
118class SetAdapter;
119class QueueAdapter;
120void 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
126class MimeTypeTestSuite : public CppUnit::TestSuite {
127public:
128	MimeTypeTestSuite() : CppUnit::TestSuite(), fMimeDirExisted(false) {}
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
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
160	virtual void run( CppUnit::TestResult *result )
161	{
162		setUp();
163		CppUnit::TestSuite::run(result);
164		tearDown();
165	}
166private:
167	bool fMimeDirExisted;
168};
169
170// Suite
171CppUnit::Test*
172MimeTypeTest::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.
232bool
233operator==(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
266bool
267operator!=(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).
274bool
275operator==(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
328bool
329operator!=(BMessage &msg1, BMessage &msg2) {
330	return !(msg1 == msg2);
331}
332
333// Fills the bitmap data with the given character
334void
335fill_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
346void
347fill_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
364dump_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
394status_t
395reduce_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
426class IconHelper {
427public:
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
440	virtual ~IconHelper() {}
441
442	// Returns the proper bitmap bounds for the given icon size
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
448	BRect BitmapBounds() {
449		return BitmapBounds(size);
450	}
451
452	// Used to call the appropriate GetIcon[ForType] function
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
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
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
471	BBitmap* TempBitmap() {
472		return &bmpTemp;
473	}
474
475	BBitmap* Bitmap1() {
476		return &bmp1;
477	}
478
479	BBitmap* Bitmap2() {
480		return &bmp2;
481	}
482
483	icon_size Size() {
484		return size;
485	}
486
487protected:
488	BBitmap bmp1;
489	BBitmap bmp2;
490	BBitmap bmpTemp;
491	icon_size size;
492};
493
494class IconForTypeHelper : public IconHelper {
495public:
496	IconForTypeHelper(const char *fileType, icon_size which)
497		: IconHelper(which), fileType(fileType) {}
498	virtual ~IconForTypeHelper() {}
499	virtual status_t GetIcon(BMimeType &mime, BBitmap *icon) {
500		return mime.GetIconForType(fileType.c_str(), icon, size);
501	}
502	virtual status_t SetIcon(BMimeType &mime, BBitmap *icon) {
503		return mime.SetIconForType(fileType.c_str(), icon, size);
504	}
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	}
512protected:
513	std::string fileType;
514};
515
516// Adapter classes used by FillWithMimeTypes() to facilitate
517// addition of strings to containers with varying interfaces
518class ContainerAdapter {
519public:
520	virtual void Add(std::string value) = 0;
521};
522
523class SetAdapter : public ContainerAdapter {
524public:
525	SetAdapter(std::set<std::string> &set)
526		: fSet(set) { }
527	virtual void Add(std::string value) {
528		fSet.insert(value);
529	}
530protected:
531	std::set<std::string> &fSet;
532};
533
534class QueueAdapter : public ContainerAdapter {
535public:
536	QueueAdapter(std::queue<std::string> &queue)
537		: fQueue(queue) { }
538	virtual void Add(std::string value) {
539		fQueue.push(value);
540	}
541protected:
542	std::queue<std::string> &fQueue;
543};
544
545
546// setUp
547void
548MimeTypeTest::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
568void
569MimeTypeTest::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
602dump_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
618void
619MimeTypeTest::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
790void
791MimeTypeTest::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
1112void
1113MimeTypeTest::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
1324void
1325MimeTypeTest::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
1522void
1523MimeTypeTest::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
1576void
1577MimeTypeTest::LargeIconTest() {
1578	IconHelper helper(B_LARGE_ICON);
1579	IconTest(helper);
1580}
1581
1582void
1583MimeTypeTest::MiniIconTest() {
1584	IconHelper helper(B_MINI_ICON);
1585	IconTest(helper);
1586}
1587
1588void
1589MimeTypeTest::LargeIconForTypeTest() {
1590	IconForTypeHelper helper(testType, B_LARGE_ICON);
1591	IconForTypeTest(helper);
1592}
1593
1594void
1595MimeTypeTest::MiniIconForTypeTest() {
1596	IconForTypeHelper helper(testType, B_MINI_ICON);
1597	IconForTypeTest(helper);
1598}
1599
1600bool 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
1615void
1616MimeTypeTest::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
1789void
1790MimeTypeTest::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
1852void
1853MimeTypeTest::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
1865void
1866MimeTypeTest::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
1877void
1878MimeTypeTest::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
2008void
2009MimeTypeTest::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.
2154void
2155to_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
2162std::string
2163to_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
2172void
2173remove_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
2191bool
2192type_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
2206void
2207MimeTypeTest::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
2248void 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
2277bool
2278types_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
2299bool
2300is_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
2333void
2334MimeTypeTest::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
2527void
2528MimeTypeTest::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
2721void
2722MimeTypeTest::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
2746static
2747void
2748init_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
2769void
2770MimeTypeTest::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
2958void
2959MimeTypeTest::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
3123class NotificationMessage {
3124public:
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
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
3138	NotificationMessage(int32 which, string type, bool largeIcon)
3139		: which(which), type(type), hasExtraType(false), extraType(),
3140		  hasLargeIcon(true), largeIcon(largeIcon)
3141	{
3142	}
3143
3144	NotificationMessage(int32 which, string type)
3145		: which(which), type(type), hasExtraType(false), extraType(),
3146		  hasLargeIcon(false), largeIcon(false)
3147	{
3148	}
3149
3150public:
3151	int32	which;
3152	string	type;
3153	bool	hasExtraType;
3154	string	extraType;
3155	bool	hasLargeIcon;
3156	bool	largeIcon;
3157};
3158
3159// FillAttrInfo
3160static
3161void
3162FillAttrInfo(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
3184void
3185MimeTypeTest::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
3428void
3429MimeTypeTest::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
3488class MimeInfoTestFile {
3489public:
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
3507	~MimeInfoTestFile()
3508	{
3509		delete[] data;
3510	}
3511
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
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
3538void
3539MimeTypeTest::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
3709static
3710status_t
3711WriteStringAttr(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
3719const uint32 MINI_ICON_TYPE = 'MICN';
3720const uint32 LARGE_ICON_TYPE = 'ICON';
3721
3722// helper class for create_app_meta_mime() tests
3723class AppMimeTestFile {
3724public:
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
3739	~AppMimeTestFile()
3740	{
3741		SetMiniIcon(NULL);
3742		SetLargeIcon(NULL);
3743	}
3744
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
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
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
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
3871static
3872void
3873CheckAppMetaMime(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
3911void
3912MimeTypeTest::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
4166static
4167void
4168CheckIconData(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
4188void
4189MimeTypeTest::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
4239void
4240MimeTypeTest::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) {
4524if (error != B_OK)
4525printf("\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
4602class SniffingTestFile {
4603public:
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
4629	~SniffingTestFile()
4630	{
4631		delete[] data;
4632	}
4633
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
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
4667void
4668MimeTypeTest::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);
4762if (!(type == contentType))
4763printf("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);
4776if (!(type == realType))
4777printf("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