#include <stdio.h>
#include <string>
#include <unistd.h>
#include <Application.h>
#include <Bitmap.h>
#include <Directory.h>
#include <Entry.h>
#include <File.h>
#include <fs_attr.h>
#include <fs_info.h>
#include <Node.h>
#include <NodeMonitor.h>
#include <Path.h>
#include <Resources.h>
#include <Roster.h>
#include <String.h>
#include <TypeConstants.h>
#include <Volume.h>
#include <VolumeRoster.h>
#include <cppunit/Test.h>
#include <cppunit/TestCaller.h>
#include <cppunit/TestSuite.h>
#include <TestApp.h>
#include <TestShell.h>
#include <TestUtils.h>
#include <cppunit/TestAssert.h>
#include "VolumeTest.h"
#include "../app/bmessenger/Helpers.h"
static const char *testDir = "/tmp/testDir";
static const char *testFile1 = "/tmp/testDir/file1";
static const char *testMountPoint = "/tmp/testDir/mount_point";
static
bool
icon_equal(const BBitmap *icon1, const BBitmap *icon2)
{
return (icon1->Bounds() == icon2->Bounds()
&& icon1->BitsLength() == icon2->BitsLength()
&& memcmp(icon1->Bits(), icon2->Bits(), icon1->BitsLength()) == 0);
}
CppUnit::Test*
VolumeTest::Suite() {
CppUnit::TestSuite *suite = new CppUnit::TestSuite();
typedef CppUnit::TestCaller<VolumeTest> TC;
suite->addTest( new TC("BVolume::Init Test1",
&VolumeTest::InitTest1) );
suite->addTest( new TC("BVolume::Init Test2",
&VolumeTest::InitTest2) );
suite->addTest( new TC("BVolume::Assignment Test",
&VolumeTest::AssignmentTest) );
suite->addTest( new TC("BVolume::Comparisson Test",
&VolumeTest::ComparissonTest) );
suite->addTest( new TC("BVolume::SetName Test",
&VolumeTest::SetNameTest) );
suite->addTest( new TC("BVolume::BadValues Test",
&VolumeTest::BadValuesTest) );
suite->addTest( new TC("BVolumeRoster::Iteration Test",
&VolumeTest::IterationTest) );
suite->addTest( new TC("BVolumeRoster::Watching Test",
&VolumeTest::WatchingTest) );
return suite;
}
void
VolumeTest::setUp()
{
BasicTest::setUp();
execCommand(
string("mkdir ") + testDir
);
createVolume(testFile1, testMountPoint, 1);
fApplication = new BTestApp("application/x-vnd.obos.volume-test");
if (fApplication->Init() != B_OK) {
fprintf(stderr, "Failed to initialize application.\n");
delete fApplication;
fApplication = NULL;
}
}
void
VolumeTest::tearDown()
{
if (fApplication) {
fApplication->Terminate();
delete fApplication;
fApplication = NULL;
}
deleteVolume(testFile1, testMountPoint);
execCommand(string("rm -rf ") + testDir);
BasicTest::tearDown();
}
static
void
CheckVolume(BVolume &volume, dev_t device, status_t error)
{
CHK(volume.InitCheck() == error);
if (error == B_OK) {
fs_info info;
CHK(fs_stat_dev(device, &info) == 0);
CHK(volume.Device() == device);
BDirectory rootDir;
CHK(volume.GetRootDirectory(&rootDir) == B_OK);
node_ref rootNode;
rootNode.device = device;
rootNode.node = info.root;
BDirectory actualRootDir(&rootNode);
CHK(rootDir == actualRootDir);
CHK(volume.Capacity() == info.total_blocks * info.block_size);
CHK(volume.FreeBytes() == info.free_blocks * info.block_size);
char name[B_FILE_NAME_LENGTH];
CHK(volume.GetName(name) == B_OK);
CHK(BString(name) == info.volume_name);
BBitmap miniIcon(BRect(0, 0, 15, 15), B_CMAP8);
BBitmap miniIcon2(BRect(0, 0, 15, 15), B_CMAP8);
status_t iconError = get_device_icon(info.device_name,
miniIcon2.Bits(), B_MINI_ICON);
CHK(volume.GetIcon(&miniIcon, B_MINI_ICON) == iconError);
if (iconError == B_OK)
CHK(icon_equal(&miniIcon, &miniIcon2));
BBitmap largeIcon(BRect(0, 0, 31, 31), B_CMAP8);
BBitmap largeIcon2(BRect(0, 0, 31, 31), B_CMAP8);
iconError = get_device_icon(info.device_name, largeIcon2.Bits(),
B_LARGE_ICON);
CHK(volume.GetIcon(&largeIcon, B_LARGE_ICON) == iconError);
if (iconError == B_OK)
CHK(icon_equal(&largeIcon, &largeIcon2));
CHK(volume.IsRemovable() == bool(info.flags & B_FS_IS_REMOVABLE));
CHK(volume.IsReadOnly() == bool(info.flags & B_FS_IS_READONLY));
CHK(volume.IsPersistent() == bool(info.flags & B_FS_IS_PERSISTENT));
CHK(volume.IsShared() == bool(info.flags & B_FS_IS_SHARED));
CHK(volume.KnowsMime() == bool(info.flags & B_FS_HAS_MIME));
CHK(volume.KnowsAttr() == bool(info.flags & B_FS_HAS_ATTR));
CHK(volume.KnowsQuery() == bool(info.flags & B_FS_HAS_QUERY));
} else {
CHK(volume.Device() == -1);
BDirectory rootDir;
CHK(volume.GetRootDirectory(&rootDir) == B_BAD_VALUE);
CHK(volume.Capacity() == B_BAD_VALUE);
CHK(volume.FreeBytes() == B_BAD_VALUE);
char name[B_FILE_NAME_LENGTH];
CHK(volume.GetName(name) == B_BAD_VALUE);
BBitmap miniIcon(BRect(0, 0, 15, 15), B_CMAP8);
CHK(volume.GetIcon(&miniIcon, B_MINI_ICON) == B_BAD_VALUE);
BBitmap largeIcon(BRect(0, 0, 31, 31), B_CMAP8);
CHK(volume.GetIcon(&largeIcon, B_LARGE_ICON) == B_BAD_VALUE);
CHK(volume.IsRemovable() == false);
CHK(volume.IsReadOnly() == false);
CHK(volume.IsPersistent() == false);
CHK(volume.IsShared() == false);
CHK(volume.KnowsMime() == false);
CHK(volume.KnowsAttr() == false);
CHK(volume.KnowsQuery() == false);
}
}
static
void
AssertNotBootVolume(const BVolume &volume)
{
CHK(volume.InitCheck() == B_OK);
dev_t bootDevice = dev_for_path("/boot");
CHK(bootDevice >= 0);
CHK(volume.Device() != bootDevice);
}
void
VolumeTest::InitTest1()
{
{
BVolume volume;
CheckVolume(volume, -1, B_NO_INIT);
}
const char *volumes[] = {
"/boot",
"/",
"/dev",
"/pipe",
"/unknown",
testMountPoint
};
int32 volumeCount = sizeof(volumes) / sizeof(const char*);
for (int32 i = 0; i < volumeCount; i++) {
NextSubTest();
const char *volumeRootDir = volumes[i];
dev_t device = dev_for_path(volumeRootDir);
BVolume volume(device);
CheckVolume(volume, device, (device >= 0 ? B_OK : B_BAD_VALUE));
}
NextSubTest();
{
BVolume volume(-2);
CHK(volume.InitCheck() == B_BAD_VALUE);
}
NextSubTest();
{
dev_t device = 213;
fs_info info;
while (fs_stat_dev(device, &info) == 0)
device++;
BVolume volume(device);
CHK(volume.InitCheck() == B_ENTRY_NOT_FOUND);
}
}
void
VolumeTest::InitTest2()
{
const char *volumes[] = {
"/boot",
"/",
"/dev",
"/pipe",
"/unknown",
testMountPoint
};
int32 volumeCount = sizeof(volumes) / sizeof(const char*);
BVolume volume1;
for (int32 i = 0; i < volumeCount; i++) {
NextSubTest();
const char *volumeRootDir = volumes[i];
dev_t device = dev_for_path(volumeRootDir);
status_t initError = (device >= 0 ? B_OK : B_BAD_VALUE);
CHK(volume1.SetTo(device) == initError);
CheckVolume(volume1, device, initError);
BVolume volume2;
CHK(volume2.SetTo(device) == initError);
CheckVolume(volume2, device, initError);
volume2.Unset();
CheckVolume(volume2, device, B_NO_INIT);
}
NextSubTest();
{
BVolume volume;
CHK(volume.SetTo(-2) == B_BAD_VALUE);
CHK(volume.InitCheck() == B_BAD_VALUE);
}
NextSubTest();
{
dev_t device = 213;
fs_info info;
while (fs_stat_dev(device, &info) == 0)
device++;
BVolume volume;
CHK(volume.SetTo(device) == B_ENTRY_NOT_FOUND);
CHK(volume.InitCheck() == B_ENTRY_NOT_FOUND);
}
}
void
VolumeTest::AssignmentTest()
{
const char *volumes[] = {
"/boot",
"/",
"/dev",
"/pipe",
"/unknown",
testMountPoint
};
int32 volumeCount = sizeof(volumes) / sizeof(const char*);
BVolume volume1;
for (int32 i = 0; i < volumeCount; i++) {
NextSubTest();
const char *volumeRootDir = volumes[i];
dev_t device = dev_for_path(volumeRootDir);
status_t initError = (device >= 0 ? B_OK : B_BAD_VALUE);
BVolume volume3(device);
CheckVolume(volume3, device, initError);
CHK(&(volume1 = volume3) == &volume1);
CheckVolume(volume1, device, initError);
BVolume volume2(volume3);
CheckVolume(volume2, device, initError);
}
}
void
VolumeTest::ComparissonTest()
{
const char *volumes[] = {
"/boot",
"/",
"/dev",
"/pipe",
"/unknown",
testMountPoint
};
int32 volumeCount = sizeof(volumes) / sizeof(const char*);
for (int32 i = 0; i < volumeCount; i++) {
NextSubTest();
const char *volumeRootDir = volumes[i];
dev_t device = dev_for_path(volumeRootDir);
status_t initError = (device >= 0 ? B_OK : B_BAD_VALUE);
BVolume volume(device);
CheckVolume(volume, device, initError);
for (int32 k = 0; k < volumeCount; k++) {
const char *volumeRootDir2 = volumes[k];
dev_t device2 = dev_for_path(volumeRootDir2);
status_t initError2 = (device2 >= 0 ? B_OK : B_BAD_VALUE);
BVolume volume2(device2);
CheckVolume(volume2, device2, initError2);
bool equal = (i == k
|| initError == initError2 && initError2 != B_OK);
CHK((volume == volume2) == equal);
CHK((volume != volume2) == !equal);
}
}
}
void
VolumeTest::SetNameTest()
{
dev_t device = dev_for_path(testMountPoint);
BVolume volume(device);
CheckVolume(volume, device, B_OK);
AssertNotBootVolume(volume);
NextSubTest();
const char *newName = "a new name";
CHK(volume.SetName(newName) == B_OK);
char name[B_FILE_NAME_LENGTH];
CHK(volume.GetName(name) == B_OK);
CHK(string(newName) == name);
CheckVolume(volume, device, B_OK);
NextSubTest();
const char *newName2 = "another name";
CHK(volume.SetName(newName2) == B_OK);
CHK(volume.GetName(name) == B_OK);
CHK(string(newName2) == name);
CheckVolume(volume, device, B_OK);
#ifndef TEST_R5
NextSubTest();
CHK(volume.GetName(NULL) == B_BAD_VALUE);
#endif
#ifndef TEST_R5
NextSubTest();
CHK(volume.SetName(NULL) == B_BAD_VALUE);
#endif
NextSubTest();
volume.Unset();
CHK(volume.SetName(newName) == B_BAD_VALUE);
}
void
VolumeTest::BadValuesTest()
{
BVolume volume(dev_for_path("/boot"));
CHK(volume.InitCheck() == B_OK);
#ifndef TEST_R5
NextSubTest();
CHK(volume.GetRootDirectory(NULL) == B_BAD_VALUE);
#endif
NextSubTest();
CHK(volume.GetIcon(NULL, B_MINI_ICON) == B_BAD_VALUE);
CHK(volume.GetIcon(NULL, B_LARGE_ICON) == B_BAD_VALUE);
#ifndef TEST_R5
NextSubTest();
BBitmap largeIcon(BRect(0, 0, 31, 31), B_CMAP8);
CHK(volume.GetIcon(&largeIcon, B_MINI_ICON) == B_BAD_VALUE);
BBitmap miniIcon(BRect(0, 0, 15, 15), B_CMAP8);
CHK(volume.GetIcon(&miniIcon, B_LARGE_ICON) == B_BAD_VALUE);
#endif
}
static
void
GetAllDevices(set<dev_t> &devices)
{
int32 cookie = 0;
dev_t device;
while ((device = next_dev(&cookie)) >= 0)
{
devices.insert(device);
}
}
void
VolumeTest::IterationTest()
{
NextSubTest();
BVolumeRoster roster;
BVolume volume;
CHK(roster.GetBootVolume(&volume) == B_OK);
dev_t device = dev_for_path("/boot");
CHK(device >= 0);
CheckVolume(volume, device, B_OK);
set<dev_t> allDevices;
GetAllDevices(allDevices);
int32 allDevicesCount = allDevices.size();
for (int32 i = 0; i <= allDevicesCount; i++) {
NextSubTest();
set<dev_t> devices(allDevices);
volume.Unset();
int32 checkCount = i;
while (--checkCount >= 0 && roster.GetNextVolume(&volume) == B_OK) {
device = volume.Device();
CHK(device >= 0);
CheckVolume(volume, device, B_OK);
CHK(devices.find(device) != devices.end());
devices.erase(device);
}
devices = allDevices;
roster.Rewind();
volume.Unset();
status_t error;
while ((error = roster.GetNextVolume(&volume)) == B_OK) {
device = volume.Device();
CHK(device >= 0);
CheckVolume(volume, device, B_OK);
CHK(devices.find(device) != devices.end());
devices.erase(device);
}
CHK(error == B_BAD_VALUE);
CHK(devices.empty());
roster.Rewind();
}
#ifndef TEST_R5
NextSubTest();
CHK(roster.GetNextVolume(NULL) == B_BAD_VALUE);
#endif
}
static
void
CheckWatchingMessage(bool mounted, dev_t expectedDevice, BTestHandler &handler,
node_ref nodeRef = node_ref())
{
snooze(100000);
BMessageQueue &queue = handler.Queue();
BMessage *_message = queue.NextMessage();
CHK(_message);
BMessage message(*_message);
delete _message;
if (mounted) {
int32 opcode;
dev_t device;
dev_t parentDevice;
ino_t directory;
CHK(message.FindInt32("opcode", &opcode) == B_OK);
CHK(message.FindInt32("new device", &device) == B_OK);
CHK(message.FindInt32("device", &parentDevice) == B_OK);
CHK(message.FindInt64("directory", &directory) == B_OK);
CHK(opcode == B_DEVICE_MOUNTED);
CHK(device == expectedDevice);
CHK(parentDevice == nodeRef.device);
CHK(directory == nodeRef.node);
} else {
int32 opcode;
dev_t device;
CHK(message.FindInt32("opcode", &opcode) == B_OK);
CHK(message.FindInt32("device", &device) == B_OK);
CHK(opcode == B_DEVICE_UNMOUNTED);
CHK(device == expectedDevice);
}
}
void
VolumeTest::WatchingTest()
{
NextSubTest();
BVolumeRoster roster;
CHK(!roster.Messenger().IsValid());
BMessenger target(&fApplication->Handler());
CHK(roster.StartWatching(target) == B_OK);
CHK(roster.Messenger() == target);
dev_t device = dev_for_path(testMountPoint);
CHK(device >= 0);
NextSubTest();
deleteVolume(testFile1, testMountPoint, false);
CHK(roster.Messenger() == target);
CheckWatchingMessage(false, device, fApplication->Handler());
node_ref nodeRef;
CHK(BDirectory(testMountPoint).GetNodeRef(&nodeRef) == B_OK);
NextSubTest();
createVolume(testFile1, testMountPoint, 1, false);
CHK(roster.Messenger() == target);
device = dev_for_path(testMountPoint);
CHK(device >= 0);
CheckWatchingMessage(true, device, fApplication->Handler(), nodeRef);
BTestHandler *handler2 = fApplication->CreateTestHandler();
BMessenger target2(handler2);
CHK(roster.StartWatching(target2) == B_OK);
CHK(roster.Messenger() == target2);
NextSubTest();
deleteVolume(testFile1, testMountPoint, false);
CHK(roster.Messenger() == target2);
CheckWatchingMessage(false, device, *handler2);
NextSubTest();
createVolume(testFile1, testMountPoint, 1, false);
CHK(roster.Messenger() == target2);
device = dev_for_path(testMountPoint);
CHK(device >= 0);
CheckWatchingMessage(true, device, *handler2, nodeRef);
NextSubTest();
roster.StopWatching();
CHK(!roster.Messenger().IsValid());
NextSubTest();
deleteVolume(testFile1, testMountPoint, false);
createVolume(testFile1, testMountPoint, 1, false);
snooze(100000);
CHK(fApplication->Handler().Queue().IsEmpty());
CHK(handler2->Queue().IsEmpty());
NextSubTest();
CHK(roster.StartWatching(BMessenger()) == B_ERROR);
}