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