haiku/src/tests/kits/storage/QueryTest.cpp

1467 lines
40 KiB
C++
Raw Normal View History

// QueryTest.cpp
#include <ctype.h>
#include <fs_info.h>
#include <stdio.h>
#include <string>
#include <unistd.h>
#include "QueryTest.h"
#include <Application.h>
#include <Message.h>
#include <MessageQueue.h>
#include <Messenger.h>
#include <NodeMonitor.h>
#include <OS.h>
#include <Path.h>
#include <Query.h>
#include <String.h>
#include <Volume.h>
#include <TestApp.h>
#include <TestUtils.h>
// Query
class Query : public BQuery {
public:
#if TEST_R5
status_t PushValue(int32 value) { PushInt32(value); return B_OK; }
status_t PushValue(uint32 value) { PushUInt32(value); return B_OK; }
status_t PushValue(int64 value) { PushInt64(value); return B_OK; }
status_t PushValue(uint64 value) { PushUInt64(value); return B_OK; }
status_t PushValue(float value) { PushFloat(value); return B_OK; }
status_t PushValue(double value) { PushDouble(value); return B_OK; }
status_t PushValue(const BString value, bool caseInsensitive = false)
{
PushString(value.String(), caseInsensitive); return B_OK;
}
status_t PushAttr(const char *attribute)
{
BQuery::PushAttr(attribute);
return B_OK;
}
status_t PushOp(query_op op)
{
BQuery::PushOp(op);
return B_OK;
}
#else
status_t PushValue(int32 value) { return PushInt32(value); }
status_t PushValue(uint32 value) { return PushUInt32(value); }
status_t PushValue(int64 value) { return PushInt64(value); }
status_t PushValue(uint64 value) { return PushUInt64(value); }
status_t PushValue(float value) { return PushFloat(value); }
status_t PushValue(double value) { return PushDouble(value); }
status_t PushValue(const BString value, bool caseInsensitive = false)
{
return PushString(value.String(), caseInsensitive);
}
#endif
};
// PredicateNode
class PredicateNode {
public:
virtual ~PredicateNode() {}
virtual status_t push(Query &query) const = 0;
virtual BString toString() const = 0;
};
// ValueNode
template<typename ValueType>
class ValueNode : public PredicateNode {
public:
ValueNode(ValueType v) : value(v) {}
virtual ~ValueNode() {}
virtual status_t push(Query &query) const
{
return query.PushValue(value);
}
virtual BString toString() const
{
return BString() << value;
}
ValueType value;
};
// float specialization
BString
ValueNode<float>::toString() const
{
char buffer[32];
sprintf(buffer, "0x%08lx", *(int32*)&value);
return BString() << buffer;
}
// double specialization
BString
ValueNode<double>::toString() const
{
char buffer[32];
sprintf(buffer, "0x%016Lx", *(int64*)&value);
return BString() << buffer;
}
// StringNode
class StringNode : public PredicateNode {
public:
StringNode(BString v, bool caseInsensitive = false)
: value(v), caseInsensitive(caseInsensitive)
{
}
virtual ~StringNode() {}
virtual status_t push(Query &query) const
{
return query.PushValue(value, caseInsensitive);
}
virtual BString toString() const
{
BString escaped;
if (caseInsensitive) {
const char *str = value.String();
int32 len = value.Length();
for (int32 i = 0; i < len; i++) {
char c = str[i];
if (isalpha(c)) {
int lower = tolower(c);
int upper = toupper(c);
if (lower < 0 || upper < 0)
escaped << c;
else
escaped << "[" << (char)lower << (char)upper << "]";
} else
escaped << c;
}
} else
escaped = value;
escaped.CharacterEscape("\"\\'", '\\');
return BString("\"") << escaped << "\"";
}
BString value;
bool caseInsensitive;
};
// DateNode
class DateNode : public PredicateNode {
public:
DateNode(BString v) : value(v) {}
virtual ~DateNode() {}
virtual status_t push(Query &query) const
{
return query.PushDate(value.String());
}
virtual BString toString() const
{
BString escaped(value);
escaped.CharacterEscape("%\"\\'", '\\');
return BString("%") << escaped << "%";
}
BString value;
};
// AttributeNode
class AttributeNode : public PredicateNode {
public:
AttributeNode(BString v) : value(v) {}
virtual ~AttributeNode() {}
virtual status_t push(Query &query) const
{
return query.PushAttr(value.String());
}
virtual BString toString() const
{
return value;
}
BString value;
};
// short hands
typedef ValueNode<int32> Int32Node;
typedef ValueNode<uint32> UInt32Node;
typedef ValueNode<int64> Int64Node;
typedef ValueNode<uint64> UInt64Node;
typedef ValueNode<float> FloatNode;
typedef ValueNode<double> DoubleNode;
// ListNode
class ListNode : public PredicateNode {
public:
ListNode(PredicateNode *child1 = NULL, PredicateNode *child2 = NULL,
PredicateNode *child3 = NULL, PredicateNode *child4 = NULL,
PredicateNode *child5 = NULL, PredicateNode *child6 = NULL)
: children()
{
addChild(child1);
addChild(child2);
addChild(child3);
addChild(child4);
addChild(child5);
addChild(child6);
}
virtual ~ListNode()
{
for (int32 i = 0; PredicateNode *child = childAt(i); i++)
delete child;
}
virtual status_t push(Query &query) const
{
status_t error = B_OK;
for (int32 i = 0; PredicateNode *child = childAt(i); i++) {
error = child->push(query);
if (error != B_OK)
break;
}
return error;
}
virtual BString toString() const
{
return BString("INVALID");
}
ListNode &addChild(PredicateNode *child)
{
if (child)
children.AddItem(child);
return *this;
}
PredicateNode *childAt(int32 index) const
{
return (PredicateNode*)children.ItemAt(index);
}
BList children;
};
// OpNode
class OpNode : public ListNode {
public:
OpNode(query_op op, PredicateNode *left, PredicateNode *right = NULL)
: ListNode(left, right), op(op) {}
virtual ~OpNode() { }
virtual status_t push(Query &query) const
{
status_t error = ListNode::push(query);
if (error == B_OK)
error = query.PushOp(op);
return error;
}
virtual BString toString() const
{
PredicateNode *left = childAt(0);
PredicateNode *right = childAt(1);
if (!left)
return "INVALID ARGS";
BString result;
BString leftString = left->toString();
BString rightString;
if (right)
rightString = right->toString();
switch (op) {
case B_INVALID_OP:
result = "INVALID";
break;
case B_EQ:
result << "(" << leftString << "==" << rightString << ")";
break;
case B_GT:
result << "(" << leftString << ">" << rightString << ")";
break;
case B_GE:
result << "(" << leftString << ">=" << rightString << ")";
break;
case B_LT:
result << "(" << leftString << "<" << rightString << ")";
break;
case B_LE:
result << "(" << leftString << "<=" << rightString << ")";
break;
case B_NE:
result << "(" << leftString << "!=" << rightString << ")";
break;
case B_CONTAINS:
{
StringNode *strNode = dynamic_cast<StringNode*>(right);
if (strNode) {
rightString = StringNode(BString("*") << strNode->value
<< "*").toString();
}
result << "(" << leftString << "==" << rightString << ")";
break;
}
case B_BEGINS_WITH:
{
StringNode *strNode = dynamic_cast<StringNode*>(right);
if (strNode) {
rightString = StringNode(BString(strNode->value) << "*")
.toString();
}
result << "(" << leftString << "==" << rightString << ")";
break;
}
case B_ENDS_WITH:
{
StringNode *strNode = dynamic_cast<StringNode*>(right);
if (strNode) {
rightString = StringNode(BString("*") << strNode->value)
.toString();
}
result << "(" << leftString << "==" << rightString << ")";
break;
}
case B_AND:
result << "(" << leftString << "&&" << rightString << ")";
break;
case B_OR:
result << "(" << leftString << "||" << rightString << ")";
break;
case B_NOT:
result << "(" << "!" << leftString << ")";
break;
case _B_RESERVED_OP_:
result = "RESERVED";
break;
}
return result;
}
query_op op;
};
// QueryTestEntry
class QueryTestEntry {
public:
QueryTestEntry(string path, node_flavor kind,
const QueryTestEntry *linkTarget = NULL)
: path(path),
cpath(NULL),
kind(kind),
linkToPath(),
clinkToPath(NULL),
directory(-1),
node(-1),
name()
{
cpath = this->path.c_str();
if (linkTarget)
linkToPath = linkTarget->path;
clinkToPath = this->linkToPath.c_str();
}
string operator+(string leaf) const
{
return path + "/" + leaf;
}
string path;
const char *cpath;
node_flavor kind;
string linkToPath;
const char *clinkToPath;
ino_t directory;
ino_t node;
string name;
};
static const char *testVolumeImage = "/tmp/query-test-image";
static const char *testMountPoint = "/non-existing-mount-point";
// the test entry hierarchy:
// mountPoint
// + dir1
// + subdir11
// + subdir12
// + file11
// + file12
// + link11
// + dir2
// + subdir21
// + subdir22
// + subdir23
// + file21
// + file22
// + link21
// + dir3
// + subdir31
// + subdir32
// + file31
// + file32
// + link31
// + file1
// + file2
// + file3
// + link1
// + link2
// + link3
static QueryTestEntry mountPoint(testMountPoint, B_DIRECTORY_NODE);
static QueryTestEntry dir1(mountPoint + "dir1", B_DIRECTORY_NODE);
static QueryTestEntry subdir11(dir1 + "subdir11", B_DIRECTORY_NODE);
static QueryTestEntry subdir12(dir1 + "subdir12", B_DIRECTORY_NODE);
static QueryTestEntry file11(dir1 + "file11", B_FILE_NODE);
static QueryTestEntry file12(dir1 + "file12", B_FILE_NODE);
static QueryTestEntry link11(dir1 + "link11", B_SYMLINK_NODE, &file11);
static QueryTestEntry dir2(mountPoint + "dir2", B_DIRECTORY_NODE);
static QueryTestEntry subdir21(dir2 + "subdir21", B_DIRECTORY_NODE);
static QueryTestEntry subdir22(dir2 + "subdir22", B_DIRECTORY_NODE);
static QueryTestEntry subdir23(dir2 + "subdir23", B_DIRECTORY_NODE);
static QueryTestEntry file21(dir2 + "file21", B_FILE_NODE);
static QueryTestEntry file22(dir2 + "file22", B_FILE_NODE);
static QueryTestEntry link21(dir2 + "link21", B_SYMLINK_NODE, &file12);
static QueryTestEntry dir3(mountPoint + "dir3", B_DIRECTORY_NODE);
static QueryTestEntry subdir31(dir3 + "subdir31", B_DIRECTORY_NODE);
static QueryTestEntry subdir32(dir3 + "subdir32", B_DIRECTORY_NODE);
static QueryTestEntry file31(dir3 + "file31", B_FILE_NODE);
static QueryTestEntry file32(dir3 + "file32", B_FILE_NODE);
static QueryTestEntry link31(dir3 + "link31", B_SYMLINK_NODE, &file22);
static QueryTestEntry file1(mountPoint + "file1", B_FILE_NODE);
static QueryTestEntry file2(mountPoint + "file2", B_FILE_NODE);
static QueryTestEntry file3(mountPoint + "file3", B_FILE_NODE);
static QueryTestEntry link1(mountPoint + "link1", B_SYMLINK_NODE, &file1);
static QueryTestEntry link2(mountPoint + "link2", B_SYMLINK_NODE, &file2);
static QueryTestEntry link3(mountPoint + "link3", B_SYMLINK_NODE, &file3);
static QueryTestEntry *allTestEntries[] = {
&dir1, &subdir11, &subdir12, &file11, &file12, &link11,
&dir2, &subdir21, &subdir22, &subdir23, &file21, &file22, &link21,
&dir3, &subdir31, &subdir32, &file31, &file32, &link31,
&file1, &file2, &file3, &link1, &link2, &link3
};
static const int32 allTestEntryCount
= sizeof(allTestEntries) / sizeof(QueryTestEntry*);
// create_test_entries
void
create_test_entries(QueryTestEntry **testEntries, int32 count)
{
// create the command line
string cmdLine("true");
for (int32 i = 0; i < count; i++) {
const QueryTestEntry *entry = testEntries[i];
switch (entry->kind) {
case B_DIRECTORY_NODE:
cmdLine += " ; mkdir " + entry->path;
break;
case B_FILE_NODE:
cmdLine += " ; touch " + entry->path;
break;
case B_SYMLINK_NODE:
cmdLine += " ; ln -s " + entry->linkToPath + " " + entry->path;
break;
case B_ANY_NODE:
default:
printf("WARNING: invalid node kind\n");
break;
}
}
BasicTest::execCommand(cmdLine);
}
// delete_test_entries
void
delete_test_entries(QueryTestEntry **testEntries, int32 count)
{
// create the command line
string cmdLine("true");
for (int32 i = 0; i < count; i++) {
const QueryTestEntry *entry = testEntries[i];
switch (entry->kind) {
case B_DIRECTORY_NODE:
case B_FILE_NODE:
case B_SYMLINK_NODE:
cmdLine += " ; rm -rf " + entry->path;
break;
case B_ANY_NODE:
default:
printf("WARNING: invalid node kind\n");
break;
}
}
BasicTest::execCommand(cmdLine);
}
// QueryTest
// Suite
CppUnit::Test*
QueryTest::Suite() {
CppUnit::TestSuite *suite = new CppUnit::TestSuite();
typedef CppUnit::TestCaller<QueryTest> TC;
suite->addTest( new TC("BQuery::Predicate Test",
&QueryTest::PredicateTest) );
suite->addTest( new TC("BQuery::Parameter Test",
&QueryTest::ParameterTest) );
suite->addTest( new TC("BQuery::Fetch Test", &QueryTest::FetchTest) );
suite->addTest( new TC("BQuery::Live Test", &QueryTest::LiveTest) );
return suite;
}
// setUp
void
QueryTest::setUp()
{
BasicTest::setUp();
fApplication = new BTestApp("application/x-vnd.obos.query-test");
if (fApplication->Init() != B_OK) {
fprintf(stderr, "Failed to initialize application.\n");
delete fApplication;
fApplication = NULL;
}
fVolumeCreated = false;
}
// tearDown
void
QueryTest::tearDown()
{
BasicTest::tearDown();
if (fApplication) {
fApplication->Terminate();
delete fApplication;
fApplication = NULL;
}
if (fVolumeCreated) {
deleteVolume(testVolumeImage, testMountPoint);
fVolumeCreated = false;
}
}
// TestPredicate
static
void
TestPredicate(const PredicateNode &predicateNode, status_t pushResult = B_OK,
status_t getResult = B_OK)
{
BString predicateString = predicateNode.toString().String();
//printf("predicate: `%s'\n", predicateString.String());
// GetPredicate(BString *)
{
Query query;
// CPPUNIT_ASSERT( predicateNode.push(query) == pushResult );
status_t error = predicateNode.push(query);
if (error != pushResult) {
printf("predicate: `%s'\n", predicateString.String());
printf("error: %lx vs %lx\n", error, pushResult);
}
CPPUNIT_ASSERT( error == pushResult );
if (pushResult == B_OK) {
BString predicate;
// CPPUNIT_ASSERT( query.GetPredicate(&predicate) == getResult );
error = query.GetPredicate(&predicate);
if (error != getResult) {
printf("predicate: `%s'\n", predicateString.String());
printf("error: %lx vs %lx\n", error, getResult);
}
CPPUNIT_ASSERT( error == getResult );
if (getResult == B_OK) {
CPPUNIT_ASSERT( (int32)query.PredicateLength()
== predicateString.Length() + 1 );
CPPUNIT_ASSERT( predicateString == predicate );
}
}
}
// GetPredicate(char *, size_t)
{
Query query;
CPPUNIT_ASSERT( predicateNode.push(query) == pushResult );
if (pushResult == B_OK) {
char buffer[1024];
CPPUNIT_ASSERT( query.GetPredicate(buffer, sizeof(buffer))
== getResult );
if (getResult == B_OK)
CPPUNIT_ASSERT( predicateString == buffer );
}
}
// PredicateLength()
{
Query query;
CPPUNIT_ASSERT( predicateNode.push(query) == pushResult );
if (pushResult == B_OK) {
size_t expectedLength
= (getResult == B_OK ? predicateString.Length() + 1 : 0);
CPPUNIT_ASSERT( query.PredicateLength() == expectedLength );
}
}
// SetPredicate()
{
Query query;
CPPUNIT_ASSERT( query.SetPredicate(predicateString.String()) == B_OK );
CPPUNIT_ASSERT( (int32)query.PredicateLength()
== predicateString.Length() + 1 );
BString predicate;
CPPUNIT_ASSERT( query.GetPredicate(&predicate) == B_OK );
CPPUNIT_ASSERT( predicateString == predicate );
}
}
// TestOperator
static
void
TestOperator(query_op op)
{
// well formed
TestPredicate(OpNode(op,
new AttributeNode("attribute"),
new Int32Node(42)
));
TestPredicate(OpNode(op,
new AttributeNode("attribute"),
new StringNode("some string")
));
TestPredicate(OpNode(op,
new AttributeNode("attribute"),
new DateNode("22 May 2002")
));
// ill formed
TestPredicate(OpNode(op, new AttributeNode("attribute"), NULL), B_OK,
B_NO_INIT);
// R5: crashs when pushing B_CONTAINS/B_BEGINS/ENDS_WITH on an empty stack
#if TEST_R5
if (op < B_CONTAINS || op > B_ENDS_WITH)
#endif
TestPredicate(OpNode(op, NULL, NULL), B_OK, B_NO_INIT);
TestPredicate(OpNode(op,
new AttributeNode("attribute"),
new DateNode("22 May 2002")
).addChild(new Int32Node(42)), B_OK, B_NO_INIT);
}
// PredicateTest
void
QueryTest::PredicateTest()
{
// tests:
// * Push*()
// * Set/GetPredicate(), PredicateLength()
// empty predicate
NextSubTest();
char buffer[1024];
{
Query query;
BString predicate;
CPPUNIT_ASSERT( query.GetPredicate(&predicate) == B_NO_INIT );
}
{
Query query;
CPPUNIT_ASSERT( query.GetPredicate(buffer, sizeof(buffer))
== B_NO_INIT );
}
// one element predicates
NextSubTest();
TestPredicate(Int32Node(42));
TestPredicate(UInt32Node(42));
TestPredicate(Int64Node(42));
// R5: buggy PushUInt64() implementation.
#if !TEST_R5
TestPredicate(UInt64Node(42));
#endif
TestPredicate(FloatNode(42));
TestPredicate(DoubleNode(42));
TestPredicate(StringNode("some \" chars ' to \\ be ( escaped ) or "
"% not!"));
TestPredicate(StringNode("some \" chars ' to \\ be ( escaped ) or "
"% not!", true));
TestPredicate(DateNode("+15 min"));
TestPredicate(DateNode("22 May 2002"));
TestPredicate(DateNode("tomorrow"));
TestPredicate(DateNode("17:57"));
TestPredicate(DateNode("invalid date"), B_BAD_VALUE);
TestPredicate(AttributeNode("some attribute"));
// operators
NextSubTest();
TestOperator(B_EQ);
TestOperator(B_GT);
TestOperator(B_GE);
TestOperator(B_LT);
TestOperator(B_LE);
TestOperator(B_NE);
TestOperator(B_CONTAINS);
TestOperator(B_BEGINS_WITH);
TestOperator(B_ENDS_WITH);
TestOperator(B_AND);
TestOperator(B_OR);
{
// B_NOT
TestPredicate(OpNode(B_NOT, new AttributeNode("attribute")));
TestPredicate(OpNode(B_NOT, new Int32Node(42)));
TestPredicate(OpNode(B_NOT, new StringNode("some string")));
TestPredicate(OpNode(B_NOT, new StringNode("some string", true)));
TestPredicate(OpNode(B_NOT, new DateNode("22 May 2002")));
TestPredicate(OpNode(B_NOT, NULL), B_OK, B_NO_INIT);
}
// well formed, legal predicate
NextSubTest();
TestPredicate(OpNode(B_AND,
new OpNode(B_CONTAINS,
new AttributeNode("attribute"),
new StringNode("hello")
),
new OpNode(B_OR,
new OpNode(B_NOT,
new OpNode(B_EQ,
new AttributeNode("attribute2"),
new UInt32Node(7)
),
NULL
),
new OpNode(B_GE,
new AttributeNode("attribute3"),
new DateNode("20 May 2002")
)
)
));
// well formed, illegal predicate
NextSubTest();
TestPredicate(OpNode(B_EQ,
new StringNode("hello"),
new OpNode(B_LE,
new OpNode(B_NOT,
new Int32Node(17),
NULL
),
new DateNode("20 May 2002")
)
));
// ill formed predicates
// Some have already been tested in TestOperator, so we only test a few
// special ones.
NextSubTest();
TestPredicate(ListNode(new Int32Node(42), new StringNode("hello!")),
B_OK, B_NO_INIT);
TestPredicate(OpNode(B_EQ,
new StringNode("hello"),
new OpNode(B_NOT, NULL)
), B_OK, B_NO_INIT);
// precedence Push*() over SetPredicate()
NextSubTest();
{
Query query;
OpNode predicate1(B_CONTAINS,
new AttributeNode("attribute"),
new StringNode("hello")
);
StringNode predicate2("I'm the loser. :´-(");
CPPUNIT_ASSERT( predicate1.push(query) == B_OK );
CPPUNIT_ASSERT( query.SetPredicate(predicate2.toString().String())
== B_OK );
BString predicate;
CPPUNIT_ASSERT( query.GetPredicate(&predicate) == B_OK );
CPPUNIT_ASSERT( predicate == predicate1.toString() );
}
// GetPredicate() clears the stack
NextSubTest();
{
Query query;
OpNode predicate1(B_CONTAINS,
new AttributeNode("attribute"),
new StringNode("hello")
);
StringNode predicate2("I'm the winner. :-)");
CPPUNIT_ASSERT( predicate1.push(query) == B_OK );
BString predicate;
CPPUNIT_ASSERT( query.GetPredicate(&predicate) == B_OK );
CPPUNIT_ASSERT( predicate == predicate1.toString() );
CPPUNIT_ASSERT( query.SetPredicate(predicate2.toString().String())
== B_OK );
CPPUNIT_ASSERT( query.GetPredicate(&predicate) == B_OK );
CPPUNIT_ASSERT( predicate == predicate2.toString() );
}
// PredicateLength() clears the stack
NextSubTest();
{
Query query;
OpNode predicate1(B_CONTAINS,
new AttributeNode("attribute"),
new StringNode("hello")
);
StringNode predicate2("I'm the winner. :-)");
CPPUNIT_ASSERT( predicate1.push(query) == B_OK );
CPPUNIT_ASSERT( (int32)query.PredicateLength()
== predicate1.toString().Length() + 1 );
CPPUNIT_ASSERT( query.SetPredicate(predicate2.toString().String())
== B_OK );
BString predicate;
CPPUNIT_ASSERT( query.GetPredicate(&predicate) == B_OK );
CPPUNIT_ASSERT( predicate == predicate2.toString() );
}
// SetPredicate(), Push*() fail after Fetch()
NextSubTest();
{
Query query;
CPPUNIT_ASSERT( query.SetPredicate("name=\"ThisShouldNotExist\"")
== B_OK );
BVolume volume(dev_for_path("/boot"));
CPPUNIT_ASSERT( volume.InitCheck() == B_OK );
CPPUNIT_ASSERT( query.SetVolume(&volume) == B_OK );
CPPUNIT_ASSERT( query.Fetch() == B_OK );
CPPUNIT_ASSERT( query.SetPredicate("name=\"ThisShouldNotExistEither\"")
== B_NOT_ALLOWED );
// R5: Push*()ing a new predicate does work, though it doesn't make any sense
#if TEST_R5
CPPUNIT_ASSERT( query.PushDate("20 May 2002") == B_OK );
CPPUNIT_ASSERT( query.PushValue((int32)42) == B_OK );
CPPUNIT_ASSERT( query.PushValue((uint32)42) == B_OK );
CPPUNIT_ASSERT( query.PushValue((int64)42) == B_OK );
CPPUNIT_ASSERT( query.PushValue((uint64)42) == B_OK );
CPPUNIT_ASSERT( query.PushValue((float)42) == B_OK );
CPPUNIT_ASSERT( query.PushValue((double)42) == B_OK );
CPPUNIT_ASSERT( query.PushValue("hello") == B_OK );
CPPUNIT_ASSERT( query.PushAttr("attribute") == B_OK );
CPPUNIT_ASSERT( query.PushOp(B_EQ) == B_OK );
#else
CPPUNIT_ASSERT( query.PushDate("20 May 2002") == B_NOT_ALLOWED );
CPPUNIT_ASSERT( query.PushValue((int32)42) == B_NOT_ALLOWED );
CPPUNIT_ASSERT( query.PushValue((uint32)42) == B_NOT_ALLOWED );
CPPUNIT_ASSERT( query.PushValue((int64)42) == B_NOT_ALLOWED );
CPPUNIT_ASSERT( query.PushValue((uint64)42) == B_NOT_ALLOWED );
CPPUNIT_ASSERT( query.PushValue((float)42) == B_NOT_ALLOWED );
CPPUNIT_ASSERT( query.PushValue((double)42) == B_NOT_ALLOWED );
CPPUNIT_ASSERT( query.PushValue("hello") == B_NOT_ALLOWED );
CPPUNIT_ASSERT( query.PushAttr("attribute") == B_NOT_ALLOWED );
CPPUNIT_ASSERT( query.PushOp(B_EQ) == B_NOT_ALLOWED );
#endif
}
// SetPredicate(): bad args
// R5: crashes when passing NULL to Set/GetPredicate()
#if !TEST_R5
NextSubTest();
{
Query query;
CPPUNIT_ASSERT( query.SetPredicate(NULL) == B_BAD_VALUE );
CPPUNIT_ASSERT( query.SetPredicate("hello") == B_OK );
CPPUNIT_ASSERT( query.GetPredicate(NULL) == B_BAD_VALUE );
CPPUNIT_ASSERT( query.GetPredicate(NULL, 10) == B_BAD_VALUE );
}
#endif
}
// ParameterTest
void
QueryTest::ParameterTest()
{
// tests:
// * SetVolume, TargetDevice()
// * SetTarget(), IsLive()
// SetVolume(), TargetDevice()
// uninitialized BQuery
NextSubTest();
{
BQuery query;
CPPUNIT_ASSERT( query.TargetDevice() == B_ERROR );
}
// NULL volume
// R5: crashs when passing a NULL BVolume
#if !TEST_R5
NextSubTest();
{
BQuery query;
CPPUNIT_ASSERT( query.SetVolume(NULL) == B_BAD_VALUE );
CPPUNIT_ASSERT( query.TargetDevice() == B_ERROR );
}
#endif
// invalid volume
NextSubTest();
{
BQuery query;
BVolume volume(-2);
CPPUNIT_ASSERT( volume.InitCheck() == B_BAD_VALUE );
CPPUNIT_ASSERT( query.SetVolume(&volume) == B_OK );
CPPUNIT_ASSERT( query.TargetDevice() == B_ERROR );
}
// valid volume
NextSubTest();
{
BQuery query;
dev_t device = dev_for_path("/boot");
BVolume volume(device);
CPPUNIT_ASSERT( volume.InitCheck() == B_OK );
CPPUNIT_ASSERT( query.SetVolume(&volume) == B_OK );
CPPUNIT_ASSERT( query.TargetDevice() == device );
}
// SetTarget(), IsLive()
// uninitialized BQuery
NextSubTest();
{
BQuery query;
CPPUNIT_ASSERT( query.IsLive() == false );
}
// uninitialized BMessenger
NextSubTest();
{
BQuery query;
BMessenger messenger;
CPPUNIT_ASSERT( messenger.IsValid() == false );
CPPUNIT_ASSERT( query.SetTarget(messenger) == B_BAD_VALUE );
CPPUNIT_ASSERT( query.IsLive() == false );
}
// valid BMessenger
NextSubTest();
{
BQuery query;
BMessenger messenger(&fApplication->Handler());
CPPUNIT_ASSERT( messenger.IsValid() == true );
CPPUNIT_ASSERT( query.SetTarget(messenger) == B_OK );
CPPUNIT_ASSERT( query.IsLive() == true );
}
// SetVolume/Target() fail after Fetch()
NextSubTest();
{
Query query;
CPPUNIT_ASSERT( query.SetPredicate("name=\"ThisShouldNotExist\"")
== B_OK );
BVolume volume(dev_for_path("/boot"));
CPPUNIT_ASSERT( volume.InitCheck() == B_OK );
CPPUNIT_ASSERT( query.SetVolume(&volume) == B_OK );
CPPUNIT_ASSERT( query.Fetch() == B_OK );
CPPUNIT_ASSERT( query.SetVolume(&volume) == B_NOT_ALLOWED );
BMessenger messenger(&fApplication->Handler());
CPPUNIT_ASSERT( messenger.IsValid() == true );
CPPUNIT_ASSERT( query.SetTarget(messenger) == B_NOT_ALLOWED );
}
// Fetch() fails without a valid volume set
NextSubTest();
{
Query query;
CPPUNIT_ASSERT( query.SetPredicate("name=\"ThisShouldNotExist\"")
== B_OK );
CPPUNIT_ASSERT( query.Fetch() == B_NO_INIT );
}
}
// TestFetchPredicateInit
static
void
TestFetchPredicateInit(Query &query, TestSet &testSet, const char *mountPoint,
const char *predicate, QueryTestEntry **entries,
int32 entryCount)
{
// init the query
CPPUNIT_ASSERT( query.SetPredicate(predicate) == B_OK );
BVolume volume(dev_for_path(mountPoint));
CPPUNIT_ASSERT( volume.InitCheck() == B_OK );
CPPUNIT_ASSERT( query.SetVolume(&volume) == B_OK );
CPPUNIT_ASSERT( query.Fetch() == B_OK );
// init the test set
testSet.clear();
for (int32 i = 0; i < entryCount; i++)
testSet.add(entries[i]->path);
}
// TestFetchPredicate
static
void
TestFetchPredicate(const char *mountPoint, const char *predicate,
QueryTestEntry **entries, int32 entryCount)
{
// GetNextEntry()
{
Query query;
TestSet testSet;
TestFetchPredicateInit(query, testSet, mountPoint, predicate, entries,
entryCount);
BEntry entry;
while (query.GetNextEntry(&entry) == B_OK) {
CPPUNIT_ASSERT( query.Rewind() == B_ERROR );
CPPUNIT_ASSERT( query.CountEntries() == B_ERROR );
BPath path;
CPPUNIT_ASSERT( entry.InitCheck() == B_OK );
CPPUNIT_ASSERT( entry.GetPath(&path) == B_OK );
CPPUNIT_ASSERT( testSet.test(path.Path()) == true );
}
CPPUNIT_ASSERT( testSet.testDone() == true );
CPPUNIT_ASSERT( query.GetNextEntry(&entry) == B_ENTRY_NOT_FOUND );
}
// GetNextRef()
{
Query query;
TestSet testSet;
TestFetchPredicateInit(query, testSet, mountPoint, predicate, entries,
entryCount);
entry_ref ref;
while (query.GetNextRef(&ref) == B_OK) {
CPPUNIT_ASSERT( query.Rewind() == B_ERROR );
CPPUNIT_ASSERT( query.CountEntries() == B_ERROR );
BPath path(&ref);
CPPUNIT_ASSERT( path.InitCheck() == B_OK );
CPPUNIT_ASSERT( testSet.test(path.Path()) == true );
}
CPPUNIT_ASSERT( testSet.testDone() == true );
CPPUNIT_ASSERT( query.GetNextRef(&ref) == B_ENTRY_NOT_FOUND );
}
// GetNextDirents()
{
Query query;
TestSet testSet;
TestFetchPredicateInit(query, testSet, mountPoint, predicate, entries,
entryCount);
size_t bufSize = (sizeof(dirent) + B_FILE_NAME_LENGTH) * 10;
char buffer[bufSize];
dirent *ents = (dirent *)buffer;
while (query.GetNextDirents(ents, bufSize, 1) == 1) {
CPPUNIT_ASSERT( query.Rewind() == B_ERROR );
CPPUNIT_ASSERT( query.CountEntries() == B_ERROR );
entry_ref ref(ents->d_pdev, ents->d_pino, ents->d_name);
BPath path(&ref);
CPPUNIT_ASSERT( path.InitCheck() == B_OK );
CPPUNIT_ASSERT( testSet.test(path.Path()) == true );
}
CPPUNIT_ASSERT( testSet.testDone() == true );
CPPUNIT_ASSERT( query.GetNextDirents(ents, bufSize, 1) == 0 );
}
// interleaving use of the different methods
{
Query query;
TestSet testSet;
TestFetchPredicateInit(query, testSet, mountPoint, predicate, entries,
entryCount);
size_t bufSize = (sizeof(dirent) + B_FILE_NAME_LENGTH) * 10;
char buffer[bufSize];
dirent *ents = (dirent *)buffer;
entry_ref ref;
BEntry entry;
while (query.GetNextDirents(ents, bufSize, 1) == 1) {
CPPUNIT_ASSERT( query.Rewind() == B_ERROR );
CPPUNIT_ASSERT( query.CountEntries() == B_ERROR );
entry_ref entref(ents->d_pdev, ents->d_pino, ents->d_name);
BPath entpath(&entref);
CPPUNIT_ASSERT( entpath.InitCheck() == B_OK );
CPPUNIT_ASSERT( testSet.test(entpath.Path()) == true );
if (query.GetNextRef(&ref) == B_OK) {
BPath refpath(&ref);
CPPUNIT_ASSERT( refpath.InitCheck() == B_OK );
CPPUNIT_ASSERT( testSet.test(refpath.Path()) == true );
}
if (query.GetNextEntry(&entry) == B_OK) {
BPath path;
CPPUNIT_ASSERT( entry.InitCheck() == B_OK );
CPPUNIT_ASSERT( entry.GetPath(&path) == B_OK );
CPPUNIT_ASSERT( testSet.test(path.Path()) == true );
}
}
CPPUNIT_ASSERT( query.GetNextEntry(&entry) == B_ENTRY_NOT_FOUND );
CPPUNIT_ASSERT( query.GetNextRef(&ref) == B_ENTRY_NOT_FOUND );
CPPUNIT_ASSERT( query.GetNextDirents(ents, bufSize, 1) == 0 );
}
}
// FetchTest
void
QueryTest::FetchTest()
{
// tests:
// * Clear()/Fetch()
// * BEntryList interface
// Fetch()
// uninitialized BQuery
NextSubTest();
{
Query query;
CPPUNIT_ASSERT( query.Fetch() == B_NO_INIT );
}
// incompletely initialized BQuery (no predicate)
NextSubTest();
{
Query query;
BVolume volume(dev_for_path("/boot"));
CPPUNIT_ASSERT( volume.InitCheck() == B_OK );
CPPUNIT_ASSERT( query.SetVolume(&volume) == B_OK );
CPPUNIT_ASSERT( query.Fetch() == B_NO_INIT );
}
// incompletely initialized BQuery (no volume)
NextSubTest();
{
Query query;
CPPUNIT_ASSERT( query.SetPredicate("name=\"ThisShouldNotExist\"")
== B_OK );
CPPUNIT_ASSERT( query.Fetch() == B_NO_INIT );
}
// incompletely initialized BQuery (invalid predicate)
NextSubTest();
{
Query query;
CPPUNIT_ASSERT( query.SetPredicate("name=\"ThisShouldNotExist\"&&")
== B_OK );
BVolume volume(dev_for_path("/boot"));
CPPUNIT_ASSERT( volume.InitCheck() == B_OK );
CPPUNIT_ASSERT( query.SetVolume(&volume) == B_OK );
CPPUNIT_ASSERT( query.Fetch() == B_BAD_VALUE );
}
// initialized BQuery, Fetch() twice
NextSubTest();
{
Query query;
CPPUNIT_ASSERT( query.SetPredicate("name=\"ThisShouldNotExist\"")
== B_OK );
BVolume volume(dev_for_path("/boot"));
CPPUNIT_ASSERT( volume.InitCheck() == B_OK );
CPPUNIT_ASSERT( query.SetVolume(&volume) == B_OK );
CPPUNIT_ASSERT( query.Fetch() == B_OK );
CPPUNIT_ASSERT( query.Fetch() == B_NOT_ALLOWED );
}
// initialized BQuery, successful Fetch(), different predicates
createVolume(testVolumeImage, testMountPoint, 2);
fVolumeCreated = true;
create_test_entries(allTestEntries, allTestEntryCount);
// ... all files
NextSubTest();
{
QueryTestEntry *entries[] = {
&file11, &file12, &file21, &file22, &file31, &file32, &file1,
&file2, &file3
};
const int32 entryCount = sizeof(entries) / sizeof(QueryTestEntry*);
TestFetchPredicate(testMountPoint, "name=\"file*\"", entries,
entryCount);
}
// ... all entries containing a "l"
NextSubTest();
{
QueryTestEntry *entries[] = {
&file11, &file12, &link11, &file21, &file22, &link21, &file31,
&file32, &link31, &file1, &file2, &file3, &link1, &link2, &link3
};
const int32 entryCount = sizeof(entries) / sizeof(QueryTestEntry*);
TestFetchPredicate(testMountPoint, "name=\"*l*\"", entries,
entryCount);
}
// ... all entries ending on "2"
NextSubTest();
{
QueryTestEntry *entries[] = {
&subdir12, &file12, &dir2, &subdir22, &file22, &subdir32, &file32,
&file2, &link2
};
const int32 entryCount = sizeof(entries) / sizeof(QueryTestEntry*);
TestFetchPredicate(testMountPoint, "name=\"*2\"", entries,
entryCount);
}
// Clear()
// uninitialized BQuery
NextSubTest();
{
Query query;
CPPUNIT_ASSERT( query.Clear() == B_OK );
}
// initialized BQuery, Fetch(), Clear(), Fetch()
NextSubTest();
{
Query query;
CPPUNIT_ASSERT( query.SetPredicate("name=\"ThisShouldNotExist\"")
== B_OK );
BVolume volume(dev_for_path("/boot"));
CPPUNIT_ASSERT( volume.InitCheck() == B_OK );
CPPUNIT_ASSERT( query.SetVolume(&volume) == B_OK );
CPPUNIT_ASSERT( query.Fetch() == B_OK );
CPPUNIT_ASSERT( query.Clear() == B_OK );
CPPUNIT_ASSERT( query.Fetch() == B_NO_INIT );
}
// initialized BQuery, Fetch(), Clear(), re-init, Fetch()
NextSubTest();
{
Query query;
CPPUNIT_ASSERT( query.SetPredicate("name=\"ThisShouldNotExist\"")
== B_OK );
BVolume volume(dev_for_path("/boot"));
CPPUNIT_ASSERT( volume.InitCheck() == B_OK );
CPPUNIT_ASSERT( query.SetVolume(&volume) == B_OK );
CPPUNIT_ASSERT( query.Fetch() == B_OK );
CPPUNIT_ASSERT( query.Clear() == B_OK );
CPPUNIT_ASSERT( query.SetPredicate("name=\"ThisShouldNotExist\"")
== B_OK );
CPPUNIT_ASSERT( volume.SetTo(dev_for_path("/boot")) == B_OK );
CPPUNIT_ASSERT( query.SetVolume(&volume) == B_OK );
CPPUNIT_ASSERT( query.Fetch() == B_OK );
}
// BEntryList interface:
// empty queries
NextSubTest();
{
Query query;
CPPUNIT_ASSERT( query.SetPredicate("name=\"ThisShouldNotExist\"")
== B_OK );
BVolume volume(dev_for_path("/boot"));
CPPUNIT_ASSERT( volume.InitCheck() == B_OK );
CPPUNIT_ASSERT( query.SetVolume(&volume) == B_OK );
CPPUNIT_ASSERT( query.Fetch() == B_OK );
BEntry entry;
entry_ref ref;
size_t bufSize = (sizeof(dirent) + B_FILE_NAME_LENGTH) * 10;
char buffer[bufSize];
dirent *ents = (dirent *)buffer;
CPPUNIT_ASSERT( query.GetNextEntry(&entry) == B_ENTRY_NOT_FOUND );
CPPUNIT_ASSERT( query.GetNextRef(&ref) == B_ENTRY_NOT_FOUND );
CPPUNIT_ASSERT( query.GetNextDirents(ents, bufSize, 1) == 0 );
}
// uninitialized queries
NextSubTest();
{
Query query;
BEntry entry;
entry_ref ref;
size_t bufSize = (sizeof(dirent) + B_FILE_NAME_LENGTH) * 10;
char buffer[bufSize];
dirent *ents = (dirent *)buffer;
CPPUNIT_ASSERT( query.GetNextEntry(&entry) == B_FILE_ERROR );
CPPUNIT_ASSERT( query.GetNextRef(&ref) == B_FILE_ERROR );
CPPUNIT_ASSERT( query.GetNextDirents(ents, bufSize, 1)
== B_FILE_ERROR );
}
// bad args
NextSubTest();
{
Query query;
CPPUNIT_ASSERT( query.SetPredicate("name=\"ThisShouldNotExist\"")
== B_OK );
BVolume volume(dev_for_path("/boot"));
CPPUNIT_ASSERT( volume.InitCheck() == B_OK );
CPPUNIT_ASSERT( query.SetVolume(&volume) == B_OK );
CPPUNIT_ASSERT( query.Fetch() == B_OK );
size_t bufSize = (sizeof(dirent) + B_FILE_NAME_LENGTH) * 10;
// R5: crashs when passing a NULL BEntry or entry_ref
#if !TEST_R5
CPPUNIT_ASSERT( query.GetNextEntry(NULL) == B_BAD_VALUE );
CPPUNIT_ASSERT( query.GetNextRef(NULL) == B_BAD_VALUE );
#endif
CPPUNIT_ASSERT( equals(query.GetNextDirents(NULL, bufSize, 1),
B_BAD_ADDRESS, B_BAD_VALUE) );
}
}
// AddLiveEntries
void
QueryTest::AddLiveEntries(QueryTestEntry **entries, int32 entryCount,
QueryTestEntry **queryEntries, int32 queryEntryCount)
{
create_test_entries(entries, entryCount);
for (int32 i = 0; i < entryCount; i++) {
QueryTestEntry *entry = entries[i];
BNode node(entry->cpath);
CPPUNIT_ASSERT( node.InitCheck() == B_OK );
node_ref nref;
CPPUNIT_ASSERT( node.GetNodeRef(&nref) == B_OK );
entry->node = nref.node;
entry_ref ref;
CPPUNIT_ASSERT( get_ref_for_path(entry->cpath, &ref) == B_OK );
entry->directory = ref.directory;
entry->name = ref.name;
}
CheckUpdateMessages(B_ENTRY_CREATED, queryEntries, queryEntryCount);
}
// RemoveLiveEntries
void
QueryTest::RemoveLiveEntries(QueryTestEntry **entries, int32 entryCount,
QueryTestEntry **queryEntries,
int32 queryEntryCount)
{
delete_test_entries(entries, entryCount);
CheckUpdateMessages(B_ENTRY_REMOVED, queryEntries, queryEntryCount);
for (int32 i = 0; i < entryCount; i++) {
QueryTestEntry *entry = entries[i];
entry->directory = -1;
entry->node = -1;
entry->name = "";
}
}
// CheckUpdateMessages
void
QueryTest::CheckUpdateMessages(uint32 opcode, QueryTestEntry **entries,
int32 entryCount)
{
// wait for the messages
snooze(100000);
if (fApplication) {
BMessageQueue &queue = fApplication->Handler().Queue();
CPPUNIT_ASSERT( queue.Lock() );
try {
int32 entryNum = 0;
while (BMessage *_message = queue.NextMessage()) {
BMessage message(*_message);
delete _message;
CPPUNIT_ASSERT( entryNum < entryCount );
QueryTestEntry *entry = entries[entryNum];
CPPUNIT_ASSERT( message.what == B_QUERY_UPDATE );
uint32 msgOpcode;
CPPUNIT_ASSERT( message.FindInt32("opcode", (int32*)&msgOpcode)
== B_OK );
CPPUNIT_ASSERT( msgOpcode == opcode );
dev_t device;
CPPUNIT_ASSERT( message.FindInt32("device", &device)
== B_OK );
CPPUNIT_ASSERT( device == dev_for_path(testMountPoint) );
ino_t directory;
CPPUNIT_ASSERT( message.FindInt64("directory", &directory)
== B_OK );
CPPUNIT_ASSERT( directory == entry->directory );
ino_t node;
CPPUNIT_ASSERT( message.FindInt64("node", &node)
== B_OK );
CPPUNIT_ASSERT( node == entry->node );
if (opcode == B_ENTRY_CREATED) {
const char *name;
CPPUNIT_ASSERT( message.FindString("name", &name)
== B_OK );
CPPUNIT_ASSERT( entry->name == name );
}
entryNum++;
}
CPPUNIT_ASSERT( entryNum == entryCount );
} catch (CppUnit::Exception exception) {
queue.Unlock();
throw exception;
}
queue.Unlock();
}
}
// LiveTest
void
QueryTest::LiveTest()
{
// tests:
// * live queries
CPPUNIT_ASSERT( fApplication != NULL );
createVolume(testVolumeImage, testMountPoint, 2);
fVolumeCreated = true;
create_test_entries(allTestEntries, allTestEntryCount);
BMessenger target(&fApplication->Handler());
// empty query, add some files, remove some files
NextSubTest();
{
Query query;
CPPUNIT_ASSERT( query.SetPredicate("name=\"*Argh\"")
== B_OK );
BVolume volume(dev_for_path(testMountPoint));
CPPUNIT_ASSERT( volume.InitCheck() == B_OK );
CPPUNIT_ASSERT( query.SetVolume(&volume) == B_OK );
CPPUNIT_ASSERT( query.SetTarget(target) == B_OK );
CPPUNIT_ASSERT( query.Fetch() == B_OK );
BEntry entry;
CPPUNIT_ASSERT( query.GetNextEntry(&entry) == B_ENTRY_NOT_FOUND );
// the test entries
QueryTestEntry testDir1(dir1 + "testDirArgh", B_DIRECTORY_NODE);
QueryTestEntry testDir2(dir1 + "testDir2", B_DIRECTORY_NODE);
QueryTestEntry testFile1(subdir21 + "testFileArgh", B_FILE_NODE);
QueryTestEntry testFile2(subdir21 + "testFile2", B_FILE_NODE);
QueryTestEntry testLink1(subdir32 + "testLinkArgh", B_SYMLINK_NODE,
&file11);
QueryTestEntry testLink2(subdir32 + "testLink2", B_SYMLINK_NODE,
&file11);
QueryTestEntry *entries[] = {
&testDir1, &testDir2, &testFile1, &testFile2,
&testLink1, &testLink2
};
int32 entryCount = sizeof(entries) / sizeof(QueryTestEntry*);
QueryTestEntry *queryEntries[] = {
&testDir1, &testFile1, &testLink1
};
int32 queryEntryCount = sizeof(queryEntries) / sizeof(QueryTestEntry*);
AddLiveEntries(entries, entryCount, queryEntries, queryEntryCount);
RemoveLiveEntries(entries, entryCount, queryEntries, queryEntryCount);
}
// non-empty query, add some files, remove some files
NextSubTest();
{
Query query;
TestSet testSet;
CPPUNIT_ASSERT( query.SetTarget(target) == B_OK );
QueryTestEntry *initialEntries[] = {
&file11, &file12, &file21, &file22, &file31, &file32, &file1,
&file2, &file3
};
int32 initialEntryCount
= sizeof(initialEntries) / sizeof(QueryTestEntry*);
TestFetchPredicateInit(query, testSet, testMountPoint,
"name=\"*ile*\"", initialEntries,
initialEntryCount);
BEntry entry;
while (query.GetNextEntry(&entry) == B_OK) {
BPath path;
CPPUNIT_ASSERT( entry.InitCheck() == B_OK );
CPPUNIT_ASSERT( entry.GetPath(&path) == B_OK );
CPPUNIT_ASSERT( testSet.test(path.Path()) == true );
}
CPPUNIT_ASSERT( testSet.testDone() == true );
CPPUNIT_ASSERT( query.GetNextEntry(&entry) == B_ENTRY_NOT_FOUND );
// the test entries
QueryTestEntry testDir1(dir1 + "testDir1", B_DIRECTORY_NODE);
QueryTestEntry testDir2(dir1 + "testDir2", B_DIRECTORY_NODE);
QueryTestEntry testFile1(subdir21 + "testFile1", B_FILE_NODE);
QueryTestEntry testFile2(subdir21 + "testFile2", B_FILE_NODE);
QueryTestEntry testLink1(subdir32 + "testLink1", B_SYMLINK_NODE,
&file11);
QueryTestEntry testLink2(subdir32 + "testLink2", B_SYMLINK_NODE,
&file11);
QueryTestEntry testFile3(subdir32 + "testFile3", B_FILE_NODE);
QueryTestEntry *entries[] = {
&testDir1, &testDir2, &testFile1, &testFile2,
&testLink1, &testLink2, &testFile3
};
int32 entryCount = sizeof(entries) / sizeof(QueryTestEntry*);
QueryTestEntry *queryEntries[] = {
&testFile1, &testFile2, &testFile3
};
int32 queryEntryCount = sizeof(queryEntries) / sizeof(QueryTestEntry*);
AddLiveEntries(entries, entryCount, queryEntries, queryEntryCount);
RemoveLiveEntries(entries, entryCount, queryEntries, queryEntryCount);
}
}