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
36static const char *testDir			= "/tmp/testDir";
37static const char *testFile1		= "/tmp/testDir/file1";
38static const char *testMountPoint	= "/tmp/testDir/mount_point";
39
40// icon_equal
41static
42bool
43icon_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
51CppUnit::Test*
52VolumeTest::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
77void
78VolumeTest::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
97void
98VolumeTest::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
115static
116void
117CheckVolume(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
195static
196void
197AssertNotBootVolume(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
206void
207VolumeTest::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
251void
252VolumeTest::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
302void
303VolumeTest::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
333void
334VolumeTest::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
368void
369VolumeTest::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
410void
411VolumeTest::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
441static
442void
443GetAllDevices(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
467void
468VolumeTest::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
523static
524void
525CheckWatchingMessage(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
562void
563VolumeTest::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