xref: /haiku/src/tests/kits/storage/VolumeTest.cpp (revision cc868bc230be2ea503399f24feaa46ab6aa1d690)
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
icon_equal(const BBitmap * icon1,const BBitmap * icon2)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*
Suite()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
setUp()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
tearDown()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
CheckVolume(BVolume & volume,dev_t device,status_t error)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 // AssertNotBootVolume
195 static
196 void
AssertNotBootVolume(const BVolume & volume)197 AssertNotBootVolume(const BVolume &volume)
198 {
199 	CHK(volume.InitCheck() == B_OK);
200 	dev_t bootDevice = dev_for_path("/boot");
201 	CHK(bootDevice >= 0);
202 	CHK(volume.Device() != bootDevice);
203 }
204 
205 // InitTest1
206 void
InitTest1()207 VolumeTest::InitTest1()
208 {
209 	// 1. BVolume(void)
210 	{
211 		BVolume volume;
212 		CheckVolume(volume, -1, B_NO_INIT);
213 	}
214 	// 2. BVolume(dev_t dev)
215 	// volumes for testing
216 	const char *volumes[] = {
217 		"/boot",
218 		"/",
219 		"/dev",
220 		"/pipe",
221 		"/unknown",
222 		testMountPoint
223 	};
224 	int32 volumeCount = sizeof(volumes) / sizeof(const char*);
225 	for (int32 i = 0; i < volumeCount; i++) {
226 		NextSubTest();
227 		const char *volumeRootDir = volumes[i];
228 		dev_t device = dev_for_path(volumeRootDir);
229 		BVolume volume(device);
230 		CheckVolume(volume, device, (device >= 0 ? B_OK : B_BAD_VALUE));
231 	}
232 	// invalid device ID
233 	NextSubTest();
234 	{
235 		BVolume volume(-2);
236 		CHK(volume.InitCheck() == B_BAD_VALUE);
237 	}
238 	// invalid device ID
239 	NextSubTest();
240 	{
241 		dev_t device = 213;
242 		fs_info info;
243 		while (fs_stat_dev(device, &info) == 0)
244 			device++;
245 		BVolume volume(device);
246 		CHK(volume.InitCheck() == B_ENTRY_NOT_FOUND);
247 	}
248 }
249 
250 // InitTest2
251 void
InitTest2()252 VolumeTest::InitTest2()
253 {
254 	// volumes for testing
255 	const char *volumes[] = {
256 		"/boot",
257 		"/",
258 		"/dev",
259 		"/pipe",
260 		"/unknown",
261 		testMountPoint
262 	};
263 	int32 volumeCount = sizeof(volumes) / sizeof(const char*);
264 	BVolume volume1;
265 	for (int32 i = 0; i < volumeCount; i++) {
266 		NextSubTest();
267 		const char *volumeRootDir = volumes[i];
268 		dev_t device = dev_for_path(volumeRootDir);
269 		status_t initError = (device >= 0 ? B_OK : B_BAD_VALUE);
270 		// reinit already initialized volume
271 		CHK(volume1.SetTo(device) == initError);
272 		CheckVolume(volume1, device, initError);
273 		// init fresh volume
274 		BVolume volume2;
275 		CHK(volume2.SetTo(device) == initError);
276 		CheckVolume(volume2, device, initError);
277 		// uninit volume
278 		volume2.Unset();
279 		CheckVolume(volume2, device, B_NO_INIT);
280 	}
281 	// invalid device ID
282 	NextSubTest();
283 	{
284 		BVolume volume;
285 		CHK(volume.SetTo(-2) == B_BAD_VALUE);
286 		CHK(volume.InitCheck() == B_BAD_VALUE);
287 	}
288 	// invalid device ID
289 	NextSubTest();
290 	{
291 		dev_t device = 213;
292 		fs_info info;
293 		while (fs_stat_dev(device, &info) == 0)
294 			device++;
295 		BVolume volume;
296 		CHK(volume.SetTo(device) == B_ENTRY_NOT_FOUND);
297 		CHK(volume.InitCheck() == B_ENTRY_NOT_FOUND);
298 	}
299 }
300 
301 // AssignmentTest
302 void
AssignmentTest()303 VolumeTest::AssignmentTest()
304 {
305 	// volumes for testing
306 	const char *volumes[] = {
307 		"/boot",
308 		"/",
309 		"/dev",
310 		"/pipe",
311 		"/unknown",
312 		testMountPoint
313 	};
314 	int32 volumeCount = sizeof(volumes) / sizeof(const char*);
315 	BVolume volume1;
316 	for (int32 i = 0; i < volumeCount; i++) {
317 		NextSubTest();
318 		const char *volumeRootDir = volumes[i];
319 		dev_t device = dev_for_path(volumeRootDir);
320 		status_t initError = (device >= 0 ? B_OK : B_BAD_VALUE);
321 		BVolume volume3(device);
322 		CheckVolume(volume3, device, initError);
323 		// assignment operation
324 		CHK(&(volume1 = volume3) == &volume1);
325 		CheckVolume(volume1, device, initError);
326 		// copy constructor
327 		BVolume volume2(volume3);
328 		CheckVolume(volume2, device, initError);
329 	}
330 }
331 
332 // ComparissonTest
333 void
ComparissonTest()334 VolumeTest::ComparissonTest()
335 {
336 	// volumes for testing
337 	const char *volumes[] = {
338 		"/boot",
339 		"/",
340 		"/dev",
341 		"/pipe",
342 		"/unknown",
343 		testMountPoint
344 	};
345 	int32 volumeCount = sizeof(volumes) / sizeof(const char*);
346 	for (int32 i = 0; i < volumeCount; i++) {
347 		NextSubTest();
348 		const char *volumeRootDir = volumes[i];
349 		dev_t device = dev_for_path(volumeRootDir);
350 		status_t initError = (device >= 0 ? B_OK : B_BAD_VALUE);
351 		BVolume volume(device);
352 		CheckVolume(volume, device, initError);
353 		for (int32 k = 0; k < volumeCount; k++) {
354 			const char *volumeRootDir2 = volumes[k];
355 			dev_t device2 = dev_for_path(volumeRootDir2);
356 			status_t initError2 = (device2 >= 0 ? B_OK : B_BAD_VALUE);
357 			BVolume volume2(device2);
358 			CheckVolume(volume2, device2, initError2);
359 			bool equal = (i == k
360 						  || initError == initError2 && initError2 != B_OK);
361 			CHK((volume == volume2) == equal);
362 			CHK((volume != volume2) == !equal);
363 		}
364 	}
365 }
366 
367 // SetNameTest
368 void
SetNameTest()369 VolumeTest::SetNameTest()
370 {
371 	// status_t SetName(const char* name);
372 	dev_t device = dev_for_path(testMountPoint);
373 	BVolume volume(device);
374 	CheckVolume(volume, device, B_OK);
375 	AssertNotBootVolume(volume);
376 	// set a new name
377 	NextSubTest();
378 	const char *newName = "a new name";
379 	CHK(volume.SetName(newName) == B_OK);
380 	char name[B_FILE_NAME_LENGTH];
381 	CHK(volume.GetName(name) == B_OK);
382 	CHK(string(newName) == name);
383 	CheckVolume(volume, device, B_OK);
384 	// set another name
385 	NextSubTest();
386 	const char *newName2 = "another name";
387 	CHK(volume.SetName(newName2) == B_OK);
388 	CHK(volume.GetName(name) == B_OK);
389 	CHK(string(newName2) == name);
390 	CheckVolume(volume, device, B_OK);
391 	// GetName() with NULL buffer
392 // R5: crashes when passing a NULL pointer
393 #ifndef TEST_R5
394 	NextSubTest();
395 	CHK(volume.GetName(NULL) == B_BAD_VALUE);
396 #endif
397 	// bad name
398 // R5: crashes when passing a NULL pointer
399 #ifndef TEST_R5
400 	NextSubTest();
401 	CHK(volume.SetName(NULL) == B_BAD_VALUE);
402 #endif
403 	// uninitialized volume
404 	NextSubTest();
405 	volume.Unset();
406 	CHK(volume.SetName(newName) == B_BAD_VALUE);
407 }
408 
409 // BadValuesTest
410 void
BadValuesTest()411 VolumeTest::BadValuesTest()
412 {
413 	BVolume volume(dev_for_path("/boot"));
414 	CHK(volume.InitCheck() == B_OK);
415 	// NULL arguments
416 // R5: crashes, when passing a NULL BDirectory.
417 #ifndef TEST_R5
418 	NextSubTest();
419 	CHK(volume.GetRootDirectory(NULL) == B_BAD_VALUE);
420 #endif
421 	NextSubTest();
422 	CHK(volume.GetIcon(NULL, B_MINI_ICON) == B_BAD_VALUE);
423 	CHK(volume.GetIcon(NULL, B_LARGE_ICON) == B_BAD_VALUE);
424 	// incompatible icon formats
425 // R5: returns B_OK
426 #ifndef TEST_R5
427 	NextSubTest();
428 	// mini
429 	BBitmap largeIcon(BRect(0, 0, 31, 31), B_CMAP8);
430 	CHK(volume.GetIcon(&largeIcon, B_MINI_ICON) == B_BAD_VALUE);
431 	// large
432 	BBitmap miniIcon(BRect(0, 0, 15, 15), B_CMAP8);
433 	CHK(volume.GetIcon(&miniIcon, B_LARGE_ICON) == B_BAD_VALUE);
434 #endif
435 }
436 
437 
438 // BVolumeRoster tests
439 
440 //  GetAllDevices
441 static
442 void
GetAllDevices(set<dev_t> & devices)443 GetAllDevices(set<dev_t> &devices)
444 {
445 //printf("GetAllDevices()\n");
446 	int32 cookie = 0;
447 	dev_t device;
448 	while ((device = next_dev(&cookie)) >= 0)
449 {
450 //printf("  device: %ld\n", device);
451 //BVolume dVolume(device);
452 //char name[B_FILE_NAME_LENGTH];
453 //dVolume.GetName(name);
454 //BDirectory rootDir;
455 //dVolume.GetRootDirectory(&rootDir);
456 //BEntry rootEntry;
457 //rootDir.GetEntry(&rootEntry);
458 //BPath rootPath;
459 //rootEntry.GetPath(&rootPath);
460 //printf("  name: `%s', root: `%s'\n", name, rootPath.Path());
461 		devices.insert(device);
462 }
463 //printf("GetAllDevices() done\n");
464 }
465 
466 // IterationTest
467 void
IterationTest()468 VolumeTest::IterationTest()
469 {
470 	// status_t GetBootVolume(BVolume *volume)
471 	NextSubTest();
472 	BVolumeRoster roster;
473 	BVolume volume;
474 	CHK(roster.GetBootVolume(&volume) == B_OK);
475 	dev_t device = dev_for_path("/boot");
476 	CHK(device >= 0);
477 	CheckVolume(volume, device, B_OK);
478 
479 	// status_t GetNextVolume(BVolume *volume)
480 	// void Rewind()
481 	set<dev_t> allDevices;
482 	GetAllDevices(allDevices);
483 	int32 allDevicesCount = allDevices.size();
484 	for (int32 i = 0; i <= allDevicesCount; i++) {
485 		NextSubTest();
486 		// iterate through the first i devices
487 		set<dev_t> devices(allDevices);
488 		volume.Unset();
489 		int32 checkCount = i;
490 		while (--checkCount >= 0 && 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 		// rewind and iterate through all devices
498 		devices = allDevices;
499 		roster.Rewind();
500 		volume.Unset();
501 		status_t error;
502 		while ((error = roster.GetNextVolume(&volume)) == B_OK) {
503 			device = volume.Device();
504 			CHK(device >= 0);
505 			CheckVolume(volume, device, B_OK);
506 			CHK(devices.find(device) != devices.end());
507 			devices.erase(device);
508 		}
509 		CHK(error == B_BAD_VALUE);
510 		CHK(devices.empty());
511 		roster.Rewind();
512 	}
513 
514 	// bad argument
515 // R5: crashes when passing a NULL BVolume
516 #ifndef TEST_R5
517 	NextSubTest();
518 	CHK(roster.GetNextVolume(NULL) == B_BAD_VALUE);
519 #endif
520 }
521 
522 // CheckWatchingMessage
523 static
524 void
CheckWatchingMessage(bool mounted,dev_t expectedDevice,BTestHandler & handler,node_ref nodeRef=node_ref ())525 CheckWatchingMessage(bool mounted, dev_t expectedDevice, BTestHandler &handler,
526 					 node_ref nodeRef = node_ref())
527 {
528 	snooze(100000);
529 	// get the message
530 	BMessageQueue &queue = handler.Queue();
531 	BMessage *_message = queue.NextMessage();
532 	CHK(_message);
533 	BMessage message(*_message);
534 	delete _message;
535 	// check the message
536 	if (mounted) {
537 		// volume mounted
538 		int32 opcode;
539 		dev_t device;
540 		dev_t parentDevice;
541 		ino_t directory;
542 		CHK(message.FindInt32("opcode", &opcode) == B_OK);
543 		CHK(message.FindInt32("new device", &device) == B_OK);
544 		CHK(message.FindInt32("device", &parentDevice) == B_OK);
545 		CHK(message.FindInt64("directory", &directory) == B_OK);
546 		CHK(opcode == B_DEVICE_MOUNTED);
547 		CHK(device == expectedDevice);
548 		CHK(parentDevice == nodeRef.device);
549 		CHK(directory == nodeRef.node);
550 	} else {
551 		// volume unmounted
552 		int32 opcode;
553 		dev_t device;
554 		CHK(message.FindInt32("opcode", &opcode) == B_OK);
555 		CHK(message.FindInt32("device", &device) == B_OK);
556 		CHK(opcode == B_DEVICE_UNMOUNTED);
557 		CHK(device == expectedDevice);
558 	}
559 }
560 
561 // WatchingTest
562 void
WatchingTest()563 VolumeTest::WatchingTest()
564 {
565 	// status_t StartWatching(BMessenger msngr=be_app_messenger);
566 	// void StopWatching(void);
567 	// BMessenger Messenger(void) const;
568 
569 	// start watching
570 	NextSubTest();
571 	BVolumeRoster roster;
572 	CHK(!roster.Messenger().IsValid());
573 	BMessenger target(&fApplication->Handler());
574 	CHK(roster.StartWatching(target) == B_OK);
575 	CHK(roster.Messenger() == target);
576 	dev_t device = dev_for_path(testMountPoint);
577 	CHK(device >= 0);
578 	// unmount volume
579 	NextSubTest();
580 	deleteVolume(testFile1, testMountPoint, false);
581 	CHK(roster.Messenger() == target);
582 	CheckWatchingMessage(false, device, fApplication->Handler());
583 	// get the node_ref of the mount point
584 	node_ref nodeRef;
585 	CHK(BDirectory(testMountPoint).GetNodeRef(&nodeRef) == B_OK);
586 	// mount volume
587 	NextSubTest();
588 	createVolume(testFile1, testMountPoint, 1, false);
589 	CHK(roster.Messenger() == target);
590 	device = dev_for_path(testMountPoint);
591 	CHK(device >= 0);
592 	CheckWatchingMessage(true, device, fApplication->Handler(), nodeRef);
593 	// start watching with another target
594 	BTestHandler *handler2 = fApplication->CreateTestHandler();
595 	BMessenger target2(handler2);
596 	CHK(roster.StartWatching(target2) == B_OK);
597 	CHK(roster.Messenger() == target2);
598 	// unmount volume
599 	NextSubTest();
600 	deleteVolume(testFile1, testMountPoint, false);
601 	CHK(roster.Messenger() == target2);
602 	CheckWatchingMessage(false, device, *handler2);
603 	// mount volume
604 	NextSubTest();
605 	createVolume(testFile1, testMountPoint, 1, false);
606 	CHK(roster.Messenger() == target2);
607 	device = dev_for_path(testMountPoint);
608 	CHK(device >= 0);
609 	CheckWatchingMessage(true, device, *handler2, nodeRef);
610 	// stop watching
611 	NextSubTest();
612 	roster.StopWatching();
613 	CHK(!roster.Messenger().IsValid());
614 	// unmount, mount volume
615 	NextSubTest();
616 	deleteVolume(testFile1, testMountPoint, false);
617 	createVolume(testFile1, testMountPoint, 1, false);
618 	snooze(100000);
619 	CHK(fApplication->Handler().Queue().IsEmpty());
620 	CHK(handler2->Queue().IsEmpty());
621 
622 	// try start watching with a bad messenger
623 	NextSubTest();
624 	CHK(roster.StartWatching(BMessenger()) == B_ERROR);
625 }
626 
627