xref: /haiku/src/tests/kits/storage/VolumeTest.cpp (revision 06b932a49d65e82cdfa7d28a04f48eef6de9ea49)
1 // VolumeTest.cpp
2 
3 #include <stdio.h>
4 #include <string>
5 #include <unistd.h>
6 
7 #include <Application.h>
8 #include <Bitmap.h>
9 #include <Directory.h>
10 #include <Entry.h>
11 #include <File.h>
12 #include <fs_attr.h>
13 #include <fs_info.h>
14 #include <Node.h>
15 #include <NodeMonitor.h>
16 #include <Path.h>
17 #include <Resources.h>
18 #include <Roster.h>
19 #include <String.h>
20 #include <TypeConstants.h>
21 #include <Volume.h>
22 #include <VolumeRoster.h>
23 
24 #include <cppunit/Test.h>
25 #include <cppunit/TestCaller.h>
26 #include <cppunit/TestSuite.h>
27 #include <TestApp.h>
28 #include <TestShell.h>
29 #include <TestUtils.h>
30 #include <cppunit/TestAssert.h>
31 
32 #include "VolumeTest.h"
33 #include "../app/bmessenger/Helpers.h"
34 
35 // test dirs/files/types
36 static const char *testDir			= "/tmp/testDir";
37 static const char *testFile1		= "/tmp/testDir/file1";
38 static const char *testMountPoint	= "/tmp/testDir/mount_point";
39 
40 // icon_equal
41 static
42 bool
43 icon_equal(const BBitmap *icon1, const BBitmap *icon2)
44 {
45 	return (icon1->Bounds() == icon2->Bounds()
46 			&& icon1->BitsLength() == icon2->BitsLength()
47 			&& memcmp(icon1->Bits(), icon2->Bits(), icon1->BitsLength()) == 0);
48 }
49 
50 // Suite
51 CppUnit::Test*
52 VolumeTest::Suite() {
53 	CppUnit::TestSuite *suite = new CppUnit::TestSuite();
54 	typedef CppUnit::TestCaller<VolumeTest> TC;
55 
56 	suite->addTest( new TC("BVolume::Init Test1",
57 						   &VolumeTest::InitTest1) );
58 	suite->addTest( new TC("BVolume::Init Test2",
59 						   &VolumeTest::InitTest2) );
60 	suite->addTest( new TC("BVolume::Assignment Test",
61 						   &VolumeTest::AssignmentTest) );
62 	suite->addTest( new TC("BVolume::Comparisson Test",
63 						   &VolumeTest::ComparissonTest) );
64 	suite->addTest( new TC("BVolume::SetName Test",
65 						   &VolumeTest::SetNameTest) );
66 	suite->addTest( new TC("BVolume::BadValues Test",
67 						   &VolumeTest::BadValuesTest) );
68 	suite->addTest( new TC("BVolumeRoster::Iteration Test",
69 						   &VolumeTest::IterationTest) );
70 	suite->addTest( new TC("BVolumeRoster::Watching Test",
71 						   &VolumeTest::WatchingTest) );
72 
73 	return suite;
74 }
75 
76 // setUp
77 void
78 VolumeTest::setUp()
79 {
80 	BasicTest::setUp();
81 	// create test dir and files
82 	execCommand(
83 		string("mkdir ") + testDir
84 	);
85 	// create and mount image
86 	createVolume(testFile1, testMountPoint, 1);
87 	// create app
88 	fApplication = new BTestApp("application/x-vnd.obos.volume-test");
89 	if (fApplication->Init() != B_OK) {
90 		fprintf(stderr, "Failed to initialize application.\n");
91 		delete fApplication;
92 		fApplication = NULL;
93 	}
94 }
95 
96 // tearDown
97 void
98 VolumeTest::tearDown()
99 {
100 	// delete the application
101 	if (fApplication) {
102 		fApplication->Terminate();
103 		delete fApplication;
104 		fApplication = NULL;
105 	}
106 	// unmount and delete image
107 	deleteVolume(testFile1, testMountPoint);
108 	// delete the test dir
109 	execCommand(string("rm -rf ") + testDir);
110 
111 	BasicTest::tearDown();
112 }
113 
114 // CheckVolume
115 static
116 void
117 CheckVolume(BVolume &volume, dev_t device, status_t error)
118 {
119 	CHK(volume.InitCheck() == error);
120 	if (error == B_OK) {
121 		fs_info info;
122 		CHK(fs_stat_dev(device, &info) == 0);
123 		// device
124 		CHK(volume.Device() == device);
125 		// root dir
126 		BDirectory rootDir;
127 		CHK(volume.GetRootDirectory(&rootDir) == B_OK);
128 		node_ref rootNode;
129 		rootNode.device = device;
130 		rootNode.node = info.root;
131 		BDirectory actualRootDir(&rootNode);
132 		CHK(rootDir == actualRootDir);
133 		// capacity, free bytes
134 		CHK(volume.Capacity() == info.total_blocks * info.block_size);
135 		CHK(volume.FreeBytes() == info.free_blocks * info.block_size);
136 		// name
137 		char name[B_FILE_NAME_LENGTH];
138 		CHK(volume.GetName(name) == B_OK);
139 		CHK(BString(name) == info.volume_name);
140 		// icons
141 		// mini
142 		BBitmap miniIcon(BRect(0, 0, 15, 15), B_CMAP8);
143 		BBitmap miniIcon2(BRect(0, 0, 15, 15), B_CMAP8);
144 		status_t iconError = get_device_icon(info.device_name,
145 											 miniIcon2.Bits(), B_MINI_ICON);
146 		CHK(volume.GetIcon(&miniIcon, B_MINI_ICON) == iconError);
147 		if (iconError == B_OK)
148 			CHK(icon_equal(&miniIcon, &miniIcon2));
149 		// large
150 		BBitmap largeIcon(BRect(0, 0, 31, 31), B_CMAP8);
151 		BBitmap largeIcon2(BRect(0, 0, 31, 31), B_CMAP8);
152 		iconError = get_device_icon(info.device_name, largeIcon2.Bits(),
153 									B_LARGE_ICON);
154 		CHK(volume.GetIcon(&largeIcon, B_LARGE_ICON) == iconError);
155 		if (iconError == B_OK)
156 			CHK(icon_equal(&largeIcon, &largeIcon2));
157 		// flags
158 		CHK(volume.IsRemovable() == bool(info.flags & B_FS_IS_REMOVABLE));
159 		CHK(volume.IsReadOnly() == bool(info.flags & B_FS_IS_READONLY));
160 		CHK(volume.IsPersistent() == bool(info.flags & B_FS_IS_PERSISTENT));
161 		CHK(volume.IsShared() == bool(info.flags & B_FS_IS_SHARED));
162 		CHK(volume.KnowsMime() == bool(info.flags & B_FS_HAS_MIME));
163 		CHK(volume.KnowsAttr() == bool(info.flags & B_FS_HAS_ATTR));
164 		CHK(volume.KnowsQuery() == bool(info.flags & B_FS_HAS_QUERY));
165 	} else {
166 		CHK(volume.Device() == -1);
167 		// root dir
168 		BDirectory rootDir;
169 		CHK(volume.GetRootDirectory(&rootDir) == B_BAD_VALUE);
170 		// capacity, free bytes
171 		CHK(volume.Capacity() == B_BAD_VALUE);
172 		CHK(volume.FreeBytes() == B_BAD_VALUE);
173 		// name
174 		char name[B_FILE_NAME_LENGTH];
175 		CHK(volume.GetName(name) == B_BAD_VALUE);
176 		// icons
177 		// mini
178 		BBitmap miniIcon(BRect(0, 0, 15, 15), B_CMAP8);
179 		CHK(volume.GetIcon(&miniIcon, B_MINI_ICON) == B_BAD_VALUE);
180 		// large
181 		BBitmap largeIcon(BRect(0, 0, 31, 31), B_CMAP8);
182 		CHK(volume.GetIcon(&largeIcon, B_LARGE_ICON) == B_BAD_VALUE);
183 		// flags
184 		CHK(volume.IsRemovable() == false);
185 		CHK(volume.IsReadOnly() == false);
186 		CHK(volume.IsPersistent() == false);
187 		CHK(volume.IsShared() == false);
188 		CHK(volume.KnowsMime() == false);
189 		CHK(volume.KnowsAttr() == false);
190 		CHK(volume.KnowsQuery() == false);
191 	}
192 }
193 
194 // InitTest1
195 void
196 VolumeTest::InitTest1()
197 {
198 	// 1. BVolume(void)
199 	{
200 		BVolume volume;
201 		CheckVolume(volume, -1, B_NO_INIT);
202 	}
203 	// 2. BVolume(dev_t dev)
204 	// volumes for testing
205 	const char *volumes[] = {
206 		"/boot",
207 		"/",
208 		"/dev",
209 		"/pipe",
210 		"/unknown",
211 		testMountPoint
212 	};
213 	int32 volumeCount = sizeof(volumes) / sizeof(const char*);
214 	for (int32 i = 0; i < volumeCount; i++) {
215 		NextSubTest();
216 		const char *volumeRootDir = volumes[i];
217 		dev_t device = dev_for_path(volumeRootDir);
218 		BVolume volume(device);
219 		CheckVolume(volume, device, (device >= 0 ? B_OK : B_BAD_VALUE));
220 	}
221 	// invalid device ID
222 	NextSubTest();
223 	{
224 		BVolume volume(-2);
225 		CHK(volume.InitCheck() == B_BAD_VALUE);
226 	}
227 	// invalid device ID
228 	NextSubTest();
229 	{
230 		dev_t device = 213;
231 		fs_info info;
232 		while (fs_stat_dev(device, &info) == 0)
233 			device++;
234 		BVolume volume(device);
235 		CHK(volume.InitCheck() == B_ENTRY_NOT_FOUND);
236 	}
237 }
238 
239 // InitTest2
240 void
241 VolumeTest::InitTest2()
242 {
243 	// volumes for testing
244 	const char *volumes[] = {
245 		"/boot",
246 		"/",
247 		"/dev",
248 		"/pipe",
249 		"/unknown",
250 		testMountPoint
251 	};
252 	int32 volumeCount = sizeof(volumes) / sizeof(const char*);
253 	BVolume volume1;
254 	for (int32 i = 0; i < volumeCount; i++) {
255 		NextSubTest();
256 		const char *volumeRootDir = volumes[i];
257 		dev_t device = dev_for_path(volumeRootDir);
258 		status_t initError = (device >= 0 ? B_OK : B_BAD_VALUE);
259 		// reinit already initialized volume
260 		CHK(volume1.SetTo(device) == initError);
261 		CheckVolume(volume1, device, initError);
262 		// init fresh volume
263 		BVolume volume2;
264 		CHK(volume2.SetTo(device) == initError);
265 		CheckVolume(volume2, device, initError);
266 		// uninit volume
267 		volume2.Unset();
268 		CheckVolume(volume2, device, B_NO_INIT);
269 	}
270 	// invalid device ID
271 	NextSubTest();
272 	{
273 		BVolume volume;
274 		CHK(volume.SetTo(-2) == B_BAD_VALUE);
275 		CHK(volume.InitCheck() == B_BAD_VALUE);
276 	}
277 	// invalid device ID
278 	NextSubTest();
279 	{
280 		dev_t device = 213;
281 		fs_info info;
282 		while (fs_stat_dev(device, &info) == 0)
283 			device++;
284 		BVolume volume;
285 		CHK(volume.SetTo(device) == B_ENTRY_NOT_FOUND);
286 		CHK(volume.InitCheck() == B_ENTRY_NOT_FOUND);
287 	}
288 }
289 
290 // AssignmentTest
291 void
292 VolumeTest::AssignmentTest()
293 {
294 	// volumes for testing
295 	const char *volumes[] = {
296 		"/boot",
297 		"/",
298 		"/dev",
299 		"/pipe",
300 		"/unknown",
301 		testMountPoint
302 	};
303 	int32 volumeCount = sizeof(volumes) / sizeof(const char*);
304 	BVolume volume1;
305 	for (int32 i = 0; i < volumeCount; i++) {
306 		NextSubTest();
307 		const char *volumeRootDir = volumes[i];
308 		dev_t device = dev_for_path(volumeRootDir);
309 		status_t initError = (device >= 0 ? B_OK : B_BAD_VALUE);
310 		BVolume volume3(device);
311 		CheckVolume(volume3, device, initError);
312 		// assignment operation
313 		CHK(&(volume1 = volume3) == &volume1);
314 		CheckVolume(volume1, device, initError);
315 		// copy constructor
316 		BVolume volume2(volume3);
317 		CheckVolume(volume2, device, initError);
318 	}
319 }
320 
321 // ComparissonTest
322 void
323 VolumeTest::ComparissonTest()
324 {
325 	// volumes for testing
326 	const char *volumes[] = {
327 		"/boot",
328 		"/",
329 		"/dev",
330 		"/pipe",
331 		"/unknown",
332 		testMountPoint
333 	};
334 	int32 volumeCount = sizeof(volumes) / sizeof(const char*);
335 	for (int32 i = 0; i < volumeCount; i++) {
336 		NextSubTest();
337 		const char *volumeRootDir = volumes[i];
338 		dev_t device = dev_for_path(volumeRootDir);
339 		status_t initError = (device >= 0 ? B_OK : B_BAD_VALUE);
340 		BVolume volume(device);
341 		CheckVolume(volume, device, initError);
342 		for (int32 k = 0; k < volumeCount; k++) {
343 			const char *volumeRootDir2 = volumes[k];
344 			dev_t device2 = dev_for_path(volumeRootDir2);
345 			status_t initError2 = (device2 >= 0 ? B_OK : B_BAD_VALUE);
346 			BVolume volume2(device2);
347 			CheckVolume(volume2, device2, initError2);
348 			bool equal = (i == k
349 						  || initError == initError2 && initError2 != B_OK);
350 			CHK((volume == volume2) == equal);
351 			CHK((volume != volume2) == !equal);
352 		}
353 	}
354 }
355 
356 // SetNameTest
357 void
358 VolumeTest::SetNameTest()
359 {
360 	// status_t SetName(const char* name);
361 	dev_t device = dev_for_path(testMountPoint);
362 	BVolume volume(device);
363 	CheckVolume(volume, device, B_OK);
364 	// set a new name
365 	NextSubTest();
366 	const char *newName = "a new name";
367 	CHK(volume.SetName(newName) == B_OK);
368 	char name[B_FILE_NAME_LENGTH];
369 	CHK(volume.GetName(name) == B_OK);
370 	CHK(string(newName) == name);
371 	CheckVolume(volume, device, B_OK);
372 	// set another name
373 	NextSubTest();
374 	const char *newName2 = "another name";
375 	CHK(volume.SetName(newName2) == B_OK);
376 	CHK(volume.GetName(name) == B_OK);
377 	CHK(string(newName2) == name);
378 	CheckVolume(volume, device, B_OK);
379 	// GetName() with NULL buffer
380 // R5: crashes when passing a NULL pointer
381 #ifndef TEST_R5
382 	NextSubTest();
383 	CHK(volume.GetName(NULL) == B_BAD_VALUE);
384 #endif
385 	// bad name
386 // R5: crashes when passing a NULL pointer
387 #ifndef TEST_R5
388 	NextSubTest();
389 	CHK(volume.SetName(NULL) == B_BAD_VALUE);
390 #endif
391 	// uninitialized volume
392 	NextSubTest();
393 	volume.Unset();
394 	CHK(volume.SetName(newName) == B_BAD_VALUE);
395 }
396 
397 // BadValuesTest
398 void
399 VolumeTest::BadValuesTest()
400 {
401 	BVolume volume(dev_for_path("/boot"));
402 	CHK(volume.InitCheck() == B_OK);
403 	// NULL arguments
404 // R5: crashes, when passing a NULL BDirectory.
405 #ifndef TEST_R5
406 	NextSubTest();
407 	CHK(volume.GetRootDirectory(NULL) == B_BAD_VALUE);
408 #endif
409 	NextSubTest();
410 	CHK(volume.GetIcon(NULL, B_MINI_ICON) == B_BAD_VALUE);
411 	CHK(volume.GetIcon(NULL, B_LARGE_ICON) == B_BAD_VALUE);
412 	// incompatible icon formats
413 // R5: returns B_OK
414 #ifndef TEST_R5
415 	NextSubTest();
416 	// mini
417 	BBitmap largeIcon(BRect(0, 0, 31, 31), B_CMAP8);
418 	CHK(volume.GetIcon(&largeIcon, B_MINI_ICON) == B_BAD_VALUE);
419 	// large
420 	BBitmap miniIcon(BRect(0, 0, 15, 15), B_CMAP8);
421 	CHK(volume.GetIcon(&miniIcon, B_LARGE_ICON) == B_BAD_VALUE);
422 #endif
423 }
424 
425 
426 // BVolumeRoster tests
427 
428 //  GetAllDevices
429 static
430 void
431 GetAllDevices(set<dev_t> &devices)
432 {
433 //printf("GetAllDevices()\n");
434 	int32 cookie = 0;
435 	dev_t device;
436 	while ((device = next_dev(&cookie)) >= 0)
437 {
438 //printf("  device: %ld\n", device);
439 //BVolume dVolume(device);
440 //char name[B_FILE_NAME_LENGTH];
441 //dVolume.GetName(name);
442 //BDirectory rootDir;
443 //dVolume.GetRootDirectory(&rootDir);
444 //BEntry rootEntry;
445 //rootDir.GetEntry(&rootEntry);
446 //BPath rootPath;
447 //rootEntry.GetPath(&rootPath);
448 //printf("  name: `%s', root: `%s'\n", name, rootPath.Path());
449 		devices.insert(device);
450 }
451 //printf("GetAllDevices() done\n");
452 }
453 
454 // IterationTest
455 void
456 VolumeTest::IterationTest()
457 {
458 	// status_t GetBootVolume(BVolume *volume)
459 	NextSubTest();
460 	BVolumeRoster roster;
461 	BVolume volume;
462 	CHK(roster.GetBootVolume(&volume) == B_OK);
463 	dev_t device = dev_for_path("/boot");
464 	CHK(device >= 0);
465 	CheckVolume(volume, device, B_OK);
466 
467 	// status_t GetNextVolume(BVolume *volume)
468 	// void Rewind()
469 	set<dev_t> allDevices;
470 	GetAllDevices(allDevices);
471 	int32 allDevicesCount = allDevices.size();
472 	for (int32 i = 0; i <= allDevicesCount; i++) {
473 		NextSubTest();
474 		// iterate through the first i devices
475 		set<dev_t> devices(allDevices);
476 		volume.Unset();
477 		int32 checkCount = i;
478 		while (--checkCount >= 0 && roster.GetNextVolume(&volume) == B_OK) {
479 			device = volume.Device();
480 			CHK(device >= 0);
481 			CheckVolume(volume, device, B_OK);
482 			CHK(devices.find(device) != devices.end());
483 			devices.erase(device);
484 		}
485 		// rewind and iterate through all devices
486 		devices = allDevices;
487 		roster.Rewind();
488 		volume.Unset();
489 		status_t error;
490 		while ((error = roster.GetNextVolume(&volume)) == B_OK) {
491 			device = volume.Device();
492 			CHK(device >= 0);
493 			CheckVolume(volume, device, B_OK);
494 			CHK(devices.find(device) != devices.end());
495 			devices.erase(device);
496 		}
497 		CHK(error == B_BAD_VALUE);
498 		CHK(devices.empty());
499 		roster.Rewind();
500 	}
501 
502 	// bad argument
503 // R5: crashes when passing a NULL BVolume
504 #ifndef TEST_R5
505 	NextSubTest();
506 	CHK(roster.GetNextVolume(NULL) == B_BAD_VALUE);
507 #endif
508 }
509 
510 // CheckWatchingMessage
511 static
512 void
513 CheckWatchingMessage(bool mounted, dev_t expectedDevice, BTestHandler &handler,
514 					 node_ref nodeRef = node_ref())
515 {
516 	snooze(100000);
517 	// get the message
518 	BMessageQueue &queue = handler.Queue();
519 	BMessage *_message = queue.NextMessage();
520 	CHK(_message);
521 	BMessage message(*_message);
522 	delete _message;
523 	// check the message
524 	if (mounted) {
525 		// volume mounted
526 		int32 opcode;
527 		dev_t device;
528 		dev_t parentDevice;
529 		ino_t directory;
530 		CHK(message.FindInt32("opcode", &opcode) == B_OK);
531 		CHK(message.FindInt32("new device", &device) == B_OK);
532 		CHK(message.FindInt32("device", &parentDevice) == B_OK);
533 		CHK(message.FindInt64("directory", &directory) == B_OK);
534 		CHK(opcode == B_DEVICE_MOUNTED);
535 		CHK(device == expectedDevice);
536 		CHK(parentDevice == nodeRef.device);
537 		CHK(directory == nodeRef.node);
538 	} else {
539 		// volume unmounted
540 		int32 opcode;
541 		dev_t device;
542 		CHK(message.FindInt32("opcode", &opcode) == B_OK);
543 		CHK(message.FindInt32("device", &device) == B_OK);
544 		CHK(opcode == B_DEVICE_UNMOUNTED);
545 		CHK(device == expectedDevice);
546 	}
547 }
548 
549 // WatchingTest
550 void
551 VolumeTest::WatchingTest()
552 {
553 	// status_t StartWatching(BMessenger msngr=be_app_messenger);
554 	// void StopWatching(void);
555 	// BMessenger Messenger(void) const;
556 
557 	// start watching
558 	NextSubTest();
559 	BVolumeRoster roster;
560 	CHK(!roster.Messenger().IsValid());
561 	BMessenger target(&fApplication->Handler());
562 	CHK(roster.StartWatching(target) == B_OK);
563 	CHK(roster.Messenger() == target);
564 	dev_t device = dev_for_path(testMountPoint);
565 	CHK(device >= 0);
566 	// unmount volume
567 	NextSubTest();
568 	deleteVolume(testFile1, testMountPoint, false);
569 	CHK(roster.Messenger() == target);
570 	CheckWatchingMessage(false, device, fApplication->Handler());
571 	// get the node_ref of the mount point
572 	node_ref nodeRef;
573 	CHK(BDirectory(testMountPoint).GetNodeRef(&nodeRef) == B_OK);
574 	// mount volume
575 	NextSubTest();
576 	createVolume(testFile1, testMountPoint, 1, false);
577 	CHK(roster.Messenger() == target);
578 	device = dev_for_path(testMountPoint);
579 	CHK(device >= 0);
580 	CheckWatchingMessage(true, device, fApplication->Handler(), nodeRef);
581 	// start watching with another target
582 	BTestHandler *handler2 = fApplication->CreateTestHandler();
583 	BMessenger target2(handler2);
584 	CHK(roster.StartWatching(target2) == B_OK);
585 	CHK(roster.Messenger() == target2);
586 	// unmount volume
587 	NextSubTest();
588 	deleteVolume(testFile1, testMountPoint, false);
589 	CHK(roster.Messenger() == target2);
590 	CheckWatchingMessage(false, device, *handler2);
591 	// mount volume
592 	NextSubTest();
593 	createVolume(testFile1, testMountPoint, 1, false);
594 	CHK(roster.Messenger() == target2);
595 	device = dev_for_path(testMountPoint);
596 	CHK(device >= 0);
597 	CheckWatchingMessage(true, device, *handler2, nodeRef);
598 	// stop watching
599 	NextSubTest();
600 	roster.StopWatching();
601 	CHK(!roster.Messenger().IsValid());
602 	// unmount, mount volume
603 	NextSubTest();
604 	deleteVolume(testFile1, testMountPoint, false);
605 	createVolume(testFile1, testMountPoint, 1, false);
606 	snooze(100000);
607 	CHK(fApplication->Handler().Queue().IsEmpty());
608 	CHK(handler2->Queue().IsEmpty());
609 
610 	// try start watching with a bad messenger
611 	NextSubTest();
612 	CHK(roster.StartWatching(BMessenger()) == B_ERROR);
613 }
614 
615