xref: /haiku/src/tests/kits/storage/QueryTest.cpp (revision 1f8b3fdb7e10d481850fd91618e7a8b5b57bcace)
1 // QueryTest.cpp
2 
3 #include <ctype.h>
4 #include <fs_info.h>
5 #include <stdio.h>
6 #include <string>
7 #include <unistd.h>
8 
9 #include "QueryTest.h"
10 
11 #include <Application.h>
12 #include <Message.h>
13 #include <MessageQueue.h>
14 #include <Messenger.h>
15 #include <NodeMonitor.h>
16 #include <ObjectList.h>
17 #include <OS.h>
18 #include <Path.h>
19 #include <Query.h>
20 #include <String.h>
21 #include <Volume.h>
22 #include <TestApp.h>
23 #include <TestUtils.h>
24 
25 // Query
26 
27 class Query : public BQuery {
28 public:
29 #if TEST_R5
PushValue(int32 value)30 	status_t PushValue(int32 value)			{ PushInt32(value); return B_OK; }
PushValue(uint32 value)31 	status_t PushValue(uint32 value)		{ PushUInt32(value); return B_OK; }
PushValue(int64 value)32 	status_t PushValue(int64 value)			{ PushInt64(value); return B_OK; }
PushValue(uint64 value)33 	status_t PushValue(uint64 value)		{ PushUInt64(value); return B_OK; }
PushValue(float value)34 	status_t PushValue(float value)			{ PushFloat(value); return B_OK; }
PushValue(double value)35 	status_t PushValue(double value)		{ PushDouble(value); return B_OK; }
PushValue(const BString value,bool caseInsensitive=false)36 	status_t PushValue(const BString value, bool caseInsensitive = false)
37 	{
38 		PushString(value.String(), caseInsensitive); return B_OK;
39 	}
PushAttr(const char * attribute)40 	status_t PushAttr(const char *attribute)
41 	{
42 		BQuery::PushAttr(attribute);
43 		return B_OK;
44 	}
PushOp(query_op op)45 	status_t PushOp(query_op op)
46 	{
47 		BQuery::PushOp(op);
48 		return B_OK;
49 	}
50 #else
51 	status_t PushValue(int32 value)			{ return PushInt32(value); }
52 	status_t PushValue(uint32 value)		{ return PushUInt32(value); }
53 	status_t PushValue(int64 value)			{ return PushInt64(value); }
54 	status_t PushValue(uint64 value)		{ return PushUInt64(value); }
55 	status_t PushValue(float value)			{ return PushFloat(value); }
56 	status_t PushValue(double value)		{ return PushDouble(value); }
57 	status_t PushValue(const BString value, bool caseInsensitive = false)
58 	{
59 		return PushString(value.String(), caseInsensitive);
60 	}
61 #endif
62 };
63 
64 
65 // PredicateNode
66 
67 class PredicateNode {
68 public:
~PredicateNode()69 	virtual ~PredicateNode() {}
70 
71 	virtual status_t push(Query &query) const = 0;
72 	virtual BString toString() const = 0;
73 };
74 
75 
76 // ValueNode
77 
78 template<typename ValueType>
79 class ValueNode : public PredicateNode {
80 public:
ValueNode(ValueType v)81 	ValueNode(ValueType v) : value(v) {}
82 
~ValueNode()83 	virtual ~ValueNode() {}
84 
push(Query & query) const85 	virtual status_t push(Query &query) const
86 	{
87 		return query.PushValue(value);
88 	}
89 
toString() const90 	virtual BString toString() const
91 	{
92 		return BString() << value;
93 	}
94 
95 	ValueType value;
96 };
97 
98 // float specialization
99 template<>
100 BString
toString() const101 ValueNode<float>::toString() const
102 {
103 	char buffer[32];
104 	sprintf(buffer, "0x%08lx", *(int32*)&value);
105 	return BString() << buffer;
106 }
107 
108 // double specialization
109 template<>
110 BString
toString() const111 ValueNode<double>::toString() const
112 {
113 	char buffer[32];
114 	sprintf(buffer, "0x%016Lx", *(int64*)&value);
115 	return BString() << buffer;
116 }
117 
118 // StringNode
119 
120 class StringNode : public PredicateNode {
121 public:
StringNode(BString v,bool caseInsensitive=false)122 	StringNode(BString v, bool caseInsensitive = false)
123 		: value(v), caseInsensitive(caseInsensitive)
124 	{
125 	}
126 
~StringNode()127 	virtual ~StringNode() {}
128 
push(Query & query) const129 	virtual status_t push(Query &query) const
130 	{
131 		return query.PushValue(value, caseInsensitive);
132 	}
133 
toString() const134 	virtual BString toString() const
135 	{
136 		BString escaped;
137 		if (caseInsensitive) {
138 			const char *str = value.String();
139 			int32 len = value.Length();
140 			for (int32 i = 0; i < len; i++) {
141 				char c = str[i];
142 				if (isalpha(c)) {
143 					int lower = tolower(c);
144 					int upper = toupper(c);
145 					if (lower < 0 || upper < 0)
146 						escaped << c;
147 					else
148 						escaped << "[" << (char)lower << (char)upper << "]";
149 				} else
150 					escaped << c;
151 			}
152 		} else
153 			escaped = value;
154 		escaped.CharacterEscape("\"\\'", '\\');
155 		return BString("\"") << escaped << "\"";
156 	}
157 
158 	BString value;
159 	bool	caseInsensitive;
160 };
161 
162 
163 // DateNode
164 
165 class DateNode : public PredicateNode {
166 public:
DateNode(BString v)167 	DateNode(BString v) : value(v) {}
168 
~DateNode()169 	virtual ~DateNode() {}
170 
push(Query & query) const171 	virtual status_t push(Query &query) const
172 	{
173 		return query.PushDate(value.String());
174 	}
175 
toString() const176 	virtual BString toString() const
177 	{
178 		BString escaped(value);
179 		escaped.CharacterEscape("%\"\\'", '\\');
180 		return BString("%") << escaped << "%";
181 	}
182 
183 	BString value;
184 };
185 
186 
187 // AttributeNode
188 
189 class AttributeNode : public PredicateNode {
190 public:
AttributeNode(BString v)191 	AttributeNode(BString v) : value(v) {}
192 
~AttributeNode()193 	virtual ~AttributeNode() {}
194 
push(Query & query) const195 	virtual status_t push(Query &query) const
196 	{
197 		return query.PushAttr(value.String());
198 	}
199 
toString() const200 	virtual BString toString() const
201 	{
202 		return value;
203 	}
204 
205 	BString value;
206 };
207 
208 
209 // short hands
210 typedef ValueNode<int32>	Int32Node;
211 typedef ValueNode<uint32>	UInt32Node;
212 typedef ValueNode<int64>	Int64Node;
213 typedef ValueNode<uint64>	UInt64Node;
214 typedef ValueNode<float>	FloatNode;
215 typedef ValueNode<double>	DoubleNode;
216 
217 
218 // ListNode
219 
220 class ListNode : public PredicateNode {
221 public:
ListNode(PredicateNode * child1=NULL,PredicateNode * child2=NULL,PredicateNode * child3=NULL,PredicateNode * child4=NULL,PredicateNode * child5=NULL,PredicateNode * child6=NULL)222 	ListNode(PredicateNode *child1 = NULL, PredicateNode *child2 = NULL,
223 			 PredicateNode *child3 = NULL, PredicateNode *child4 = NULL,
224 			 PredicateNode *child5 = NULL, PredicateNode *child6 = NULL)
225 		: children()
226 	{
227 		addChild(child1);
228 		addChild(child2);
229 		addChild(child3);
230 		addChild(child4);
231 		addChild(child5);
232 		addChild(child6);
233 	}
234 
~ListNode()235 	virtual ~ListNode()
236 	{
237 		for (int32 i = 0; PredicateNode *child = childAt(i); i++)
238 			delete child;
239 	}
240 
push(Query & query) const241 	virtual status_t push(Query &query) const
242 	{
243 		status_t error = B_OK;
244 		for (int32 i = 0; PredicateNode *child = childAt(i); i++) {
245 			error = child->push(query);
246 			if (error != B_OK)
247 				break;
248 		}
249 		return error;
250 	}
251 
toString() const252 	virtual BString toString() const
253 	{
254 		return BString("INVALID");
255 	}
256 
addChild(PredicateNode * child)257 	ListNode &addChild(PredicateNode *child)
258 	{
259 		if (child)
260 			children.AddItem(child);
261 		return *this;
262 	}
263 
childAt(int32 index) const264 	PredicateNode *childAt(int32 index) const
265 	{
266 		return (PredicateNode*)children.ItemAt(index);
267 	}
268 
269 	BObjectList<PredicateNode> children;
270 };
271 
272 // OpNode
273 
274 class OpNode : public ListNode {
275 public:
OpNode(query_op op,PredicateNode * left,PredicateNode * right=NULL)276 	OpNode(query_op op, PredicateNode *left, PredicateNode *right = NULL)
277 		: ListNode(left, right), op(op) {}
278 
~OpNode()279 	virtual ~OpNode() { }
280 
push(Query & query) const281 	virtual status_t push(Query &query) const
282 	{
283 		status_t error = ListNode::push(query);
284 		if (error == B_OK)
285 			error = query.PushOp(op);
286 		return error;
287 	}
288 
toString() const289 	virtual BString toString() const
290 	{
291 		PredicateNode *left = childAt(0);
292 		PredicateNode *right = childAt(1);
293 		if (!left)
294 			return "INVALID ARGS";
295 		BString result;
296 		BString leftString = left->toString();
297 		BString rightString;
298 		if (right)
299 			rightString = right->toString();
300 		switch (op) {
301 			case B_INVALID_OP:
302 				result = "INVALID";
303 				break;
304 			case B_EQ:
305 				result << "(" << leftString << "==" << rightString << ")";
306 				break;
307 			case B_GT:
308 				result << "(" << leftString << ">" << rightString << ")";
309 				break;
310 			case B_GE:
311 				result << "(" << leftString << ">=" << rightString << ")";
312 				break;
313 			case B_LT:
314 				result << "(" << leftString << "<" << rightString << ")";
315 				break;
316 			case B_LE:
317 				result << "(" << leftString << "<=" << rightString << ")";
318 				break;
319 			case B_NE:
320 				result << "(" << leftString << "!=" << rightString << ")";
321 				break;
322 			case B_CONTAINS:
323 			{
324 				StringNode *strNode = dynamic_cast<StringNode*>(right);
325 				if (strNode) {
326 					rightString = StringNode(BString("*") << strNode->value
327 											 << "*").toString();
328 				}
329 				result << "(" << leftString << "==" << rightString << ")";
330 				break;
331 			}
332 			case B_BEGINS_WITH:
333 			{
334 				StringNode *strNode = dynamic_cast<StringNode*>(right);
335 				if (strNode) {
336 					rightString = StringNode(BString(strNode->value) << "*")
337 											 .toString();
338 				}
339 				result << "(" << leftString << "==" << rightString << ")";
340 				break;
341 			}
342 			case B_ENDS_WITH:
343 			{
344 				StringNode *strNode = dynamic_cast<StringNode*>(right);
345 				if (strNode) {
346 					rightString = StringNode(BString("*") << strNode->value)
347 											 .toString();
348 				}
349 				result << "(" << leftString << "==" << rightString << ")";
350 				break;
351 			}
352 			case B_AND:
353 				result << "(" << leftString << "&&" << rightString << ")";
354 				break;
355 			case B_OR:
356 				result << "(" << leftString << "||" << rightString << ")";
357 				break;
358 			case B_NOT:
359 				result << "(" << "!" << leftString << ")";
360 				break;
361 			case _B_RESERVED_OP_:
362 				result = "RESERVED";
363 				break;
364 		}
365 		return result;
366 	}
367 
368 	query_op op;
369 };
370 
371 
372 // QueryTestEntry
373 class QueryTestEntry {
374 public:
QueryTestEntry(string path,node_flavor kind,const QueryTestEntry * linkTarget=NULL)375 	QueryTestEntry(string path, node_flavor kind,
376 				   const QueryTestEntry *linkTarget = NULL)
377 		: path(path),
378 		  cpath(NULL),
379 		  kind(kind),
380 		  linkToPath(),
381 		  clinkToPath(NULL),
382 		  directory(-1),
383 		  node(-1),
384 		  name()
385 	{
386 		cpath = this->path.c_str();
387 		if (linkTarget)
388 			linkToPath = linkTarget->path;
389 		clinkToPath = this->linkToPath.c_str();
390 	}
391 
operator +(string leaf) const392 	string operator+(string leaf) const
393 	{
394 		return path + "/" + leaf;
395 	}
396 
397 	string		path;
398 	const char	*cpath;
399 	node_flavor	kind;
400 	string		linkToPath;
401 	const char	*clinkToPath;
402 	ino_t		directory;
403 	ino_t		node;
404 	string		name;
405 };
406 
407 static const char *testVolumeImage	= "/tmp/query-test-image";
408 static const char *testMountPoint	= "/non-existing-mount-point";
409 
410 // the test entry hierarchy:
411 // mountPoint
412 // + dir1
413 //   + subdir11
414 //   + subdir12
415 //   + file11
416 //   + file12
417 //   + link11
418 // + dir2
419 //   + subdir21
420 //   + subdir22
421 //   + subdir23
422 //   + file21
423 //   + file22
424 //   + link21
425 // + dir3
426 //   + subdir31
427 //   + subdir32
428 //   + file31
429 //   + file32
430 //   + link31
431 // + file1
432 // + file2
433 // + file3
434 // + link1
435 // + link2
436 // + link3
437 static QueryTestEntry mountPoint(testMountPoint, B_DIRECTORY_NODE);
438 static QueryTestEntry dir1(mountPoint + "dir1", B_DIRECTORY_NODE);
439 static QueryTestEntry subdir11(dir1 + "subdir11", B_DIRECTORY_NODE);
440 static QueryTestEntry subdir12(dir1 + "subdir12", B_DIRECTORY_NODE);
441 static QueryTestEntry file11(dir1 + "file11", B_FILE_NODE);
442 static QueryTestEntry file12(dir1 + "file12", B_FILE_NODE);
443 static QueryTestEntry link11(dir1 + "link11", B_SYMLINK_NODE, &file11);
444 static QueryTestEntry dir2(mountPoint + "dir2", B_DIRECTORY_NODE);
445 static QueryTestEntry subdir21(dir2 + "subdir21", B_DIRECTORY_NODE);
446 static QueryTestEntry subdir22(dir2 + "subdir22", B_DIRECTORY_NODE);
447 static QueryTestEntry subdir23(dir2 + "subdir23", B_DIRECTORY_NODE);
448 static QueryTestEntry file21(dir2 + "file21", B_FILE_NODE);
449 static QueryTestEntry file22(dir2 + "file22", B_FILE_NODE);
450 static QueryTestEntry link21(dir2 + "link21", B_SYMLINK_NODE, &file12);
451 static QueryTestEntry dir3(mountPoint + "dir3", B_DIRECTORY_NODE);
452 static QueryTestEntry subdir31(dir3 + "subdir31", B_DIRECTORY_NODE);
453 static QueryTestEntry subdir32(dir3 + "subdir32", B_DIRECTORY_NODE);
454 static QueryTestEntry file31(dir3 + "file31", B_FILE_NODE);
455 static QueryTestEntry file32(dir3 + "file32", B_FILE_NODE);
456 static QueryTestEntry link31(dir3 + "link31", B_SYMLINK_NODE, &file22);
457 static QueryTestEntry file1(mountPoint + "file1", B_FILE_NODE);
458 static QueryTestEntry file2(mountPoint + "file2", B_FILE_NODE);
459 static QueryTestEntry file3(mountPoint + "file3", B_FILE_NODE);
460 static QueryTestEntry link1(mountPoint + "link1", B_SYMLINK_NODE, &file1);
461 static QueryTestEntry link2(mountPoint + "link2", B_SYMLINK_NODE, &file2);
462 static QueryTestEntry link3(mountPoint + "link3", B_SYMLINK_NODE, &file3);
463 
464 static QueryTestEntry *allTestEntries[] = {
465 	&dir1, &subdir11, &subdir12, &file11, &file12, &link11,
466 	&dir2, &subdir21, &subdir22, &subdir23, &file21, &file22, &link21,
467 	&dir3, &subdir31, &subdir32, &file31, &file32, &link31,
468 	&file1, &file2, &file3, &link1, &link2, &link3
469 };
470 static const int32 allTestEntryCount
471 	= sizeof(allTestEntries) / sizeof(QueryTestEntry*);
472 
473 // create_test_entries
474 void
create_test_entries(QueryTestEntry ** testEntries,int32 count)475 create_test_entries(QueryTestEntry **testEntries, int32 count)
476 {
477 	// create the command line
478 	string cmdLine("true");
479 	for (int32 i = 0; i < count; i++) {
480 		const QueryTestEntry *entry = testEntries[i];
481 		switch (entry->kind) {
482 			case B_DIRECTORY_NODE:
483 				cmdLine += " ; mkdir " + entry->path;
484 				break;
485 			case B_FILE_NODE:
486 				cmdLine += " ; touch " + entry->path;
487 				break;
488 			case B_SYMLINK_NODE:
489 				cmdLine += " ; ln -s " + entry->linkToPath + " " + entry->path;
490 				break;
491 			case B_ANY_NODE:
492 			default:
493 				printf("WARNING: invalid node kind\n");
494 				break;
495 		}
496 	}
497 	BasicTest::execCommand(cmdLine);
498 }
499 
500 // delete_test_entries
501 void
delete_test_entries(QueryTestEntry ** testEntries,int32 count)502 delete_test_entries(QueryTestEntry **testEntries, int32 count)
503 {
504 	// create the command line
505 	string cmdLine("true");
506 	for (int32 i = 0; i < count; i++) {
507 		const QueryTestEntry *entry = testEntries[i];
508 		switch (entry->kind) {
509 			case B_DIRECTORY_NODE:
510 			case B_FILE_NODE:
511 			case B_SYMLINK_NODE:
512 				cmdLine += " ; rm -rf " + entry->path;
513 				break;
514 			case B_ANY_NODE:
515 			default:
516 				printf("WARNING: invalid node kind\n");
517 				break;
518 		}
519 	}
520 	BasicTest::execCommand(cmdLine);
521 }
522 
523 
524 
525 
526 // QueryTest
527 
528 // Suite
529 CppUnit::Test*
Suite()530 QueryTest::Suite() {
531 	CppUnit::TestSuite *suite = new CppUnit::TestSuite();
532 	typedef CppUnit::TestCaller<QueryTest> TC;
533 
534 	suite->addTest( new TC("BQuery::Predicate Test",
535 						   &QueryTest::PredicateTest) );
536 	suite->addTest( new TC("BQuery::Parameter Test",
537 						   &QueryTest::ParameterTest) );
538 	suite->addTest( new TC("BQuery::Fetch Test", &QueryTest::FetchTest) );
539 	suite->addTest( new TC("BQuery::Live Test", &QueryTest::LiveTest) );
540 
541 	return suite;
542 }
543 
544 // setUp
545 void
setUp()546 QueryTest::setUp()
547 {
548 	BasicTest::setUp();
549 	fApplication = new BTestApp("application/x-vnd.obos.query-test");
550 	if (fApplication->Init() != B_OK) {
551 		fprintf(stderr, "Failed to initialize application.\n");
552 		delete fApplication;
553 		fApplication = NULL;
554 	}
555 	fVolumeCreated = false;
556 }
557 
558 // tearDown
559 void
tearDown()560 QueryTest::tearDown()
561 {
562 	BasicTest::tearDown();
563 	if (fApplication) {
564 		fApplication->Terminate();
565 		delete fApplication;
566 		fApplication = NULL;
567 	}
568 	if (fVolumeCreated) {
569 		deleteVolume(testVolumeImage, testMountPoint);
570 		fVolumeCreated = false;
571 	}
572 }
573 
574 // TestPredicate
575 static
576 void
TestPredicate(const PredicateNode & predicateNode,status_t pushResult=B_OK,status_t getResult=B_OK)577 TestPredicate(const PredicateNode &predicateNode, status_t pushResult = B_OK,
578 			  status_t getResult = B_OK)
579 {
580 	BString predicateString = predicateNode.toString().String();
581 //printf("predicate: `%s'\n", predicateString.String());
582 	// GetPredicate(BString *)
583 	{
584 		Query query;
585 //		CPPUNIT_ASSERT( predicateNode.push(query) == pushResult );
586 status_t error = predicateNode.push(query);
587 if (error != pushResult) {
588 printf("predicate: `%s'\n", predicateString.String());
589 printf("error: %lx vs %lx\n", error, pushResult);
590 }
591 CPPUNIT_ASSERT( error == pushResult );
592 		if (pushResult == B_OK) {
593 			BString predicate;
594 //			CPPUNIT_ASSERT( query.GetPredicate(&predicate) == getResult );
595 error = query.GetPredicate(&predicate);
596 if (error != getResult) {
597 printf("predicate: `%s'\n", predicateString.String());
598 printf("error: %lx vs %lx\n", error, getResult);
599 }
600 CPPUNIT_ASSERT( error == getResult );
601 			if (getResult == B_OK) {
602 				CPPUNIT_ASSERT( (int32)query.PredicateLength()
603 								== predicateString.Length() + 1 );
604 				CPPUNIT_ASSERT( predicateString == predicate );
605 			}
606 		}
607 	}
608 	// GetPredicate(char *, size_t)
609 	{
610 		Query query;
611 		CPPUNIT_ASSERT( predicateNode.push(query) == pushResult );
612 		if (pushResult == B_OK) {
613 			char buffer[1024];
614 			CPPUNIT_ASSERT( query.GetPredicate(buffer, sizeof(buffer))
615 							== getResult );
616 			if (getResult == B_OK)
617 				CPPUNIT_ASSERT( predicateString == buffer );
618 		}
619 	}
620 	// PredicateLength()
621 	{
622 		Query query;
623 		CPPUNIT_ASSERT( predicateNode.push(query) == pushResult );
624 		if (pushResult == B_OK) {
625 			size_t expectedLength
626 				= (getResult == B_OK ? predicateString.Length() + 1 : 0);
627 			CPPUNIT_ASSERT( query.PredicateLength() == expectedLength );
628 		}
629 	}
630 	// SetPredicate()
631 	{
632 		Query query;
633 		CPPUNIT_ASSERT( query.SetPredicate(predicateString.String()) == B_OK );
634 		CPPUNIT_ASSERT( (int32)query.PredicateLength()
635 						== predicateString.Length() + 1 );
636 		BString predicate;
637 		CPPUNIT_ASSERT( query.GetPredicate(&predicate) == B_OK );
638 		CPPUNIT_ASSERT( predicateString == predicate );
639 	}
640 }
641 
642 // TestOperator
643 static
644 void
TestOperator(query_op op)645 TestOperator(query_op op)
646 {
647 	// well formed
648 	TestPredicate(OpNode(op,
649 		new AttributeNode("attribute"),
650 		new Int32Node(42)
651 	));
652 	TestPredicate(OpNode(op,
653 		new AttributeNode("attribute"),
654 		new StringNode("some string")
655 	));
656 	TestPredicate(OpNode(op,
657 		new AttributeNode("attribute"),
658 		new DateNode("22 May 2002")
659 	));
660 	// ill formed
661 	TestPredicate(OpNode(op, new AttributeNode("attribute"), NULL), B_OK,
662 				  B_NO_INIT);
663 // R5: crashs when pushing B_CONTAINS/B_BEGINS/ENDS_WITH on an empty stack
664 #if TEST_R5
665 if (op < B_CONTAINS || op > B_ENDS_WITH)
666 #endif
667 	TestPredicate(OpNode(op, NULL, NULL), B_OK, B_NO_INIT);
668 	TestPredicate(OpNode(op,
669 		new AttributeNode("attribute"),
670 		new DateNode("22 May 2002")
671 	).addChild(new Int32Node(42)), B_OK, B_NO_INIT);
672 }
673 
674 // PredicateTest
675 void
PredicateTest()676 QueryTest::PredicateTest()
677 {
678 	// tests:
679 	// * Push*()
680 	// * Set/GetPredicate(), PredicateLength()
681 	// empty predicate
682 	NextSubTest();
683 	char buffer[1024];
684 	{
685 		Query query;
686 		BString predicate;
687 		CPPUNIT_ASSERT( query.GetPredicate(&predicate) == B_NO_INIT );
688 	}
689 	{
690 		Query query;
691 		CPPUNIT_ASSERT( query.GetPredicate(buffer, sizeof(buffer))
692 						== B_NO_INIT );
693 	}
694 	// one element predicates
695 	NextSubTest();
696 	TestPredicate(Int32Node(42));
697 	TestPredicate(UInt32Node(42));
698 	TestPredicate(Int64Node(42));
699 // R5: buggy PushUInt64() implementation.
700 #if !TEST_R5
701 	TestPredicate(UInt64Node(42));
702 #endif
703 	TestPredicate(FloatNode(42));
704 	TestPredicate(DoubleNode(42));
705 	TestPredicate(StringNode("some \" chars ' to \\ be ( escaped ) or "
706 							 "% not!"));
707 	TestPredicate(StringNode("some \" chars ' to \\ be ( escaped ) or "
708 							 "% not!", true));
709 	TestPredicate(DateNode("+15 min"));
710 	TestPredicate(DateNode("22 May 2002"));
711 	TestPredicate(DateNode("tomorrow"));
712 	TestPredicate(DateNode("17:57"));
713 	TestPredicate(DateNode("invalid date"), B_BAD_VALUE);
714 	TestPredicate(AttributeNode("some attribute"));
715 	// operators
716 	NextSubTest();
717 	TestOperator(B_EQ);
718 	TestOperator(B_GT);
719 	TestOperator(B_GE);
720 	TestOperator(B_LT);
721 	TestOperator(B_LE);
722 	TestOperator(B_NE);
723 	TestOperator(B_CONTAINS);
724 	TestOperator(B_BEGINS_WITH);
725 	TestOperator(B_ENDS_WITH);
726 	TestOperator(B_AND);
727 	TestOperator(B_OR);
728 	{
729 		// B_NOT
730 		TestPredicate(OpNode(B_NOT, new AttributeNode("attribute")));
731 		TestPredicate(OpNode(B_NOT, new Int32Node(42)));
732 		TestPredicate(OpNode(B_NOT, new StringNode("some string")));
733 		TestPredicate(OpNode(B_NOT, new StringNode("some string", true)));
734 		TestPredicate(OpNode(B_NOT, new DateNode("22 May 2002")));
735 		TestPredicate(OpNode(B_NOT, NULL), B_OK, B_NO_INIT);
736 	}
737 	// well formed, legal predicate
738 	NextSubTest();
739 	TestPredicate(OpNode(B_AND,
740 		new OpNode(B_CONTAINS,
741 			new AttributeNode("attribute"),
742 			new StringNode("hello")
743 		),
744 		new OpNode(B_OR,
745 			new OpNode(B_NOT,
746 				new OpNode(B_EQ,
747 					new AttributeNode("attribute2"),
748 					new UInt32Node(7)
749 				),
750 				NULL
751 			),
752 			new OpNode(B_GE,
753 				new AttributeNode("attribute3"),
754 				new DateNode("20 May 2002")
755 			)
756 		)
757 	));
758 	// well formed, illegal predicate
759 	NextSubTest();
760 	TestPredicate(OpNode(B_EQ,
761 		new StringNode("hello"),
762 		new OpNode(B_LE,
763 			new OpNode(B_NOT,
764 				new Int32Node(17),
765 				NULL
766 			),
767 			new DateNode("20 May 2002")
768 		)
769 	));
770 	// ill formed predicates
771 	// Some have already been tested in TestOperator, so we only test a few
772 	// special ones.
773 	NextSubTest();
774 	TestPredicate(ListNode(new Int32Node(42), new StringNode("hello!")),
775 				  B_OK, B_NO_INIT);
776 	TestPredicate(OpNode(B_EQ,
777 		new StringNode("hello"),
778 		new OpNode(B_NOT, NULL)
779 	), B_OK, B_NO_INIT);
780 	// precedence Push*() over SetPredicate()
781 	NextSubTest();
782 	{
783 		Query query;
784 		OpNode predicate1(B_CONTAINS,
785 			new AttributeNode("attribute"),
786 			new StringNode("hello")
787 		);
788 		StringNode predicate2("I'm the loser. :´-(");
789 		CPPUNIT_ASSERT( predicate1.push(query) == B_OK );
790 		CPPUNIT_ASSERT( query.SetPredicate(predicate2.toString().String())
791 						== B_OK );
792 		BString predicate;
793 		CPPUNIT_ASSERT( query.GetPredicate(&predicate) == B_OK );
794 		CPPUNIT_ASSERT( predicate == predicate1.toString() );
795 	}
796 	// GetPredicate() clears the stack
797 	NextSubTest();
798 	{
799 		Query query;
800 		OpNode predicate1(B_CONTAINS,
801 			new AttributeNode("attribute"),
802 			new StringNode("hello")
803 		);
804 		StringNode predicate2("I'm the winner. :-)");
805 		CPPUNIT_ASSERT( predicate1.push(query) == B_OK );
806 		BString predicate;
807 		CPPUNIT_ASSERT( query.GetPredicate(&predicate) == B_OK );
808 		CPPUNIT_ASSERT( predicate == predicate1.toString() );
809 		CPPUNIT_ASSERT( query.SetPredicate(predicate2.toString().String())
810 						== B_OK );
811 		CPPUNIT_ASSERT( query.GetPredicate(&predicate) == B_OK );
812 		CPPUNIT_ASSERT( predicate == predicate2.toString() );
813 	}
814 	// PredicateLength() clears the stack
815 	NextSubTest();
816 	{
817 		Query query;
818 		OpNode predicate1(B_CONTAINS,
819 			new AttributeNode("attribute"),
820 			new StringNode("hello")
821 		);
822 		StringNode predicate2("I'm the winner. :-)");
823 		CPPUNIT_ASSERT( predicate1.push(query) == B_OK );
824 		CPPUNIT_ASSERT( (int32)query.PredicateLength()
825 						== predicate1.toString().Length() + 1 );
826 		CPPUNIT_ASSERT( query.SetPredicate(predicate2.toString().String())
827 						== B_OK );
828 		BString predicate;
829 		CPPUNIT_ASSERT( query.GetPredicate(&predicate) == B_OK );
830 		CPPUNIT_ASSERT( predicate == predicate2.toString() );
831 	}
832 	// SetPredicate(), Push*() fail after Fetch()
833 	NextSubTest();
834 	{
835 		Query query;
836 		CPPUNIT_ASSERT( query.SetPredicate("name=\"ThisShouldNotExist\"")
837 						== B_OK );
838 		BVolume volume(dev_for_path("/boot"));
839 		CPPUNIT_ASSERT( volume.InitCheck() == B_OK );
840 		CPPUNIT_ASSERT( query.SetVolume(&volume) == B_OK );
841 		CPPUNIT_ASSERT( query.Fetch() == B_OK );
842 		CPPUNIT_ASSERT( query.SetPredicate("name=\"ThisShouldNotExistEither\"")
843 						== B_NOT_ALLOWED );
844 // R5: Push*()ing a new predicate does work, though it doesn't make any sense
845 #if TEST_R5
846 		CPPUNIT_ASSERT( query.PushDate("20 May 2002") == B_OK );
847 		CPPUNIT_ASSERT( query.PushValue((int32)42) == B_OK );
848 		CPPUNIT_ASSERT( query.PushValue((uint32)42) == B_OK );
849 		CPPUNIT_ASSERT( query.PushValue((int64)42) == B_OK );
850 		CPPUNIT_ASSERT( query.PushValue((uint64)42) == B_OK );
851 		CPPUNIT_ASSERT( query.PushValue((float)42) == B_OK );
852 		CPPUNIT_ASSERT( query.PushValue((double)42) == B_OK );
853 		CPPUNIT_ASSERT( query.PushValue("hello") == B_OK );
854 		CPPUNIT_ASSERT( query.PushAttr("attribute") == B_OK );
855 		CPPUNIT_ASSERT( query.PushOp(B_EQ) == B_OK );
856 #else
857 		CPPUNIT_ASSERT( query.PushDate("20 May 2002") == B_NOT_ALLOWED );
858 		CPPUNIT_ASSERT( query.PushValue((int32)42) == B_NOT_ALLOWED );
859 		CPPUNIT_ASSERT( query.PushValue((uint32)42) == B_NOT_ALLOWED );
860 		CPPUNIT_ASSERT( query.PushValue((int64)42) == B_NOT_ALLOWED );
861 		CPPUNIT_ASSERT( query.PushValue((uint64)42) == B_NOT_ALLOWED );
862 		CPPUNIT_ASSERT( query.PushValue((float)42) == B_NOT_ALLOWED );
863 		CPPUNIT_ASSERT( query.PushValue((double)42) == B_NOT_ALLOWED );
864 		CPPUNIT_ASSERT( query.PushValue("hello") == B_NOT_ALLOWED );
865 		CPPUNIT_ASSERT( query.PushAttr("attribute") == B_NOT_ALLOWED );
866 		CPPUNIT_ASSERT( query.PushOp(B_EQ) == B_NOT_ALLOWED );
867 #endif
868 	}
869 	// SetPredicate(): bad args
870 // R5: crashes when passing NULL to Set/GetPredicate()
871 #if !TEST_R5
872 	NextSubTest();
873 	{
874 		Query query;
875 		CPPUNIT_ASSERT( query.SetPredicate(NULL) == B_BAD_VALUE );
876 		CPPUNIT_ASSERT( query.SetPredicate("hello") == B_OK );
877 		CPPUNIT_ASSERT( query.GetPredicate(NULL) == B_BAD_VALUE );
878 		CPPUNIT_ASSERT( query.GetPredicate(NULL, 10) == B_BAD_VALUE );
879 	}
880 #endif
881 }
882 
883 // ParameterTest
884 void
ParameterTest()885 QueryTest::ParameterTest()
886 {
887 	// tests:
888 	// * SetVolume, TargetDevice()
889 	// * SetTarget(), IsLive()
890 
891 	// SetVolume(), TargetDevice()
892 	// uninitialized BQuery
893 	NextSubTest();
894 	{
895 		BQuery query;
896 		CPPUNIT_ASSERT( query.TargetDevice() == B_ERROR );
897 	}
898 	// NULL volume
899 // R5: crashs when passing a NULL BVolume
900 #if !TEST_R5
901 	NextSubTest();
902 	{
903 		BQuery query;
904 		CPPUNIT_ASSERT( query.SetVolume(NULL) == B_BAD_VALUE );
905 		CPPUNIT_ASSERT( query.TargetDevice() == B_ERROR );
906 	}
907 #endif
908 	// invalid volume
909 	NextSubTest();
910 	{
911 		BQuery query;
912 		BVolume volume(-2);
913 		CPPUNIT_ASSERT( volume.InitCheck() == B_BAD_VALUE );
914 		CPPUNIT_ASSERT( query.SetVolume(&volume) == B_OK );
915 		CPPUNIT_ASSERT( query.TargetDevice() == B_ERROR );
916 	}
917 	// valid volume
918 	NextSubTest();
919 	{
920 		BQuery query;
921 		dev_t device = dev_for_path("/boot");
922 		BVolume volume(device);
923 		CPPUNIT_ASSERT( volume.InitCheck() == B_OK );
924 		CPPUNIT_ASSERT( query.SetVolume(&volume) == B_OK );
925 		CPPUNIT_ASSERT( query.TargetDevice() == device );
926 	}
927 
928 	// SetTarget(), IsLive()
929 	// uninitialized BQuery
930 	NextSubTest();
931 	{
932 		BQuery query;
933 		CPPUNIT_ASSERT( query.IsLive() == false );
934 	}
935 	// uninitialized BMessenger
936 	NextSubTest();
937 	{
938 		BQuery query;
939 		BMessenger messenger;
940 		CPPUNIT_ASSERT( messenger.IsValid() == false );
941 		CPPUNIT_ASSERT( query.SetTarget(messenger) == B_BAD_VALUE );
942 		CPPUNIT_ASSERT( query.IsLive() == false );
943 	}
944 	// valid BMessenger
945 	NextSubTest();
946 	{
947 		BQuery query;
948 		BMessenger messenger(&fApplication->Handler());
949 		CPPUNIT_ASSERT( messenger.IsValid() == true );
950 		CPPUNIT_ASSERT( query.SetTarget(messenger) == B_OK );
951 		CPPUNIT_ASSERT( query.IsLive() == true );
952 	}
953 
954 	// SetVolume/Target() fail after Fetch()
955 	NextSubTest();
956 	{
957 		Query query;
958 		CPPUNIT_ASSERT( query.SetPredicate("name=\"ThisShouldNotExist\"")
959 						== B_OK );
960 		BVolume volume(dev_for_path("/boot"));
961 		CPPUNIT_ASSERT( volume.InitCheck() == B_OK );
962 		CPPUNIT_ASSERT( query.SetVolume(&volume) == B_OK );
963 		CPPUNIT_ASSERT( query.Fetch() == B_OK );
964 		CPPUNIT_ASSERT( query.SetVolume(&volume) == B_NOT_ALLOWED );
965 		BMessenger messenger(&fApplication->Handler());
966 		CPPUNIT_ASSERT( messenger.IsValid() == true );
967 		CPPUNIT_ASSERT( query.SetTarget(messenger) == B_NOT_ALLOWED );
968 	}
969 
970 	// Fetch() fails without a valid volume set
971 	NextSubTest();
972 	{
973 		Query query;
974 		CPPUNIT_ASSERT( query.SetPredicate("name=\"ThisShouldNotExist\"")
975 						== B_OK );
976 		CPPUNIT_ASSERT( query.Fetch() == B_NO_INIT );
977 	}
978 }
979 
980 // TestFetchPredicateInit
981 static
982 void
TestFetchPredicateInit(Query & query,TestSet & testSet,const char * mountPoint,const char * predicate,QueryTestEntry ** entries,int32 entryCount)983 TestFetchPredicateInit(Query &query, TestSet &testSet, const char *mountPoint,
984 					   const char *predicate, QueryTestEntry **entries,
985 					   int32 entryCount)
986 {
987 	// init the query
988 	CPPUNIT_ASSERT( query.SetPredicate(predicate) == B_OK );
989 	BVolume volume(dev_for_path(mountPoint));
990 	CPPUNIT_ASSERT( volume.InitCheck() == B_OK );
991 	CPPUNIT_ASSERT( query.SetVolume(&volume) == B_OK );
992 	CPPUNIT_ASSERT( query.Fetch() == B_OK );
993 	// init the test set
994 	testSet.clear();
995 	for (int32 i = 0; i < entryCount; i++)
996 		testSet.add(entries[i]->path);
997 }
998 
999 
1000 // TestFetchPredicate
1001 static
1002 void
TestFetchPredicate(const char * mountPoint,const char * predicate,QueryTestEntry ** entries,int32 entryCount)1003 TestFetchPredicate(const char *mountPoint, const char *predicate,
1004 				   QueryTestEntry **entries, int32 entryCount)
1005 {
1006 	// GetNextEntry()
1007 	{
1008 		Query query;
1009 		TestSet testSet;
1010 		TestFetchPredicateInit(query, testSet, mountPoint, predicate, entries,
1011 							   entryCount);
1012 		BEntry entry;
1013 		while (query.GetNextEntry(&entry) == B_OK) {
1014 // Haiku supports rewinding queries, R5 does not.
1015 #ifdef TEST_R5
1016 			CPPUNIT_ASSERT( query.Rewind() == B_ERROR );
1017 #endif
1018 			CPPUNIT_ASSERT( query.CountEntries() == B_ERROR );
1019 			BPath path;
1020 			CPPUNIT_ASSERT( entry.InitCheck() == B_OK );
1021 			CPPUNIT_ASSERT( entry.GetPath(&path) == B_OK );
1022 			CPPUNIT_ASSERT( testSet.test(path.Path()) == true );
1023 		}
1024 		CPPUNIT_ASSERT( testSet.testDone() == true );
1025 		CPPUNIT_ASSERT( query.GetNextEntry(&entry) == B_ENTRY_NOT_FOUND );
1026 	}
1027 	// GetNextRef()
1028 	{
1029 		Query query;
1030 		TestSet testSet;
1031 		TestFetchPredicateInit(query, testSet, mountPoint, predicate, entries,
1032 							   entryCount);
1033 		entry_ref ref;
1034 		while (query.GetNextRef(&ref) == B_OK) {
1035 // Haiku supports rewinding queries, R5 does not.
1036 #ifdef TEST_R5
1037 			CPPUNIT_ASSERT( query.Rewind() == B_ERROR );
1038 #endif
1039 			CPPUNIT_ASSERT( query.CountEntries() == B_ERROR );
1040 			BPath path(&ref);
1041 			CPPUNIT_ASSERT( path.InitCheck() == B_OK );
1042 			CPPUNIT_ASSERT( testSet.test(path.Path()) == true );
1043 		}
1044 		CPPUNIT_ASSERT( testSet.testDone() == true );
1045 		CPPUNIT_ASSERT( query.GetNextRef(&ref) == B_ENTRY_NOT_FOUND );
1046 	}
1047 	// GetNextDirents()
1048 	{
1049 		Query query;
1050 		TestSet testSet;
1051 		TestFetchPredicateInit(query, testSet, mountPoint, predicate, entries,
1052 							   entryCount);
1053 		size_t bufSize = (sizeof(dirent) + B_FILE_NAME_LENGTH) * 10;
1054 		char buffer[bufSize];
1055 		dirent *ents = (dirent *)buffer;
1056 		while (query.GetNextDirents(ents, bufSize, 1) == 1) {
1057 // Haiku supports rewinding queries, R5 does not.
1058 #ifdef TEST_R5
1059 			CPPUNIT_ASSERT( query.Rewind() == B_ERROR );
1060 #endif
1061 			CPPUNIT_ASSERT( query.CountEntries() == B_ERROR );
1062 			entry_ref ref(ents->d_pdev, ents->d_pino, ents->d_name);
1063 			BPath path(&ref);
1064 			CPPUNIT_ASSERT( path.InitCheck() == B_OK );
1065 			CPPUNIT_ASSERT( testSet.test(path.Path()) == true );
1066 		}
1067 		CPPUNIT_ASSERT( testSet.testDone() == true );
1068 		CPPUNIT_ASSERT( query.GetNextDirents(ents, bufSize, 1) == 0 );
1069 	}
1070 	// interleaving use of the different methods
1071 	{
1072 		Query query;
1073 		TestSet testSet;
1074 		TestFetchPredicateInit(query, testSet, mountPoint, predicate, entries,
1075 							   entryCount);
1076 		size_t bufSize = (sizeof(dirent) + B_FILE_NAME_LENGTH) * 10;
1077 		char buffer[bufSize];
1078 		dirent *ents = (dirent *)buffer;
1079 		entry_ref ref;
1080 		BEntry entry;
1081 		while (query.GetNextDirents(ents, bufSize, 1) == 1) {
1082 // Haiku supports rewinding queries, R5 does not.
1083 #ifdef TEST_R5
1084 			CPPUNIT_ASSERT( query.Rewind() == B_ERROR );
1085 #endif
1086 			CPPUNIT_ASSERT( query.CountEntries() == B_ERROR );
1087 			entry_ref entref(ents->d_pdev, ents->d_pino, ents->d_name);
1088 			BPath entpath(&entref);
1089 			CPPUNIT_ASSERT( entpath.InitCheck() == B_OK );
1090 			CPPUNIT_ASSERT( testSet.test(entpath.Path()) == true );
1091 			if (query.GetNextRef(&ref) == B_OK) {
1092 				BPath refpath(&ref);
1093 				CPPUNIT_ASSERT( refpath.InitCheck() == B_OK );
1094 				CPPUNIT_ASSERT( testSet.test(refpath.Path()) == true );
1095 			}
1096 			if (query.GetNextEntry(&entry) == B_OK) {
1097 				BPath path;
1098 				CPPUNIT_ASSERT( entry.InitCheck() == B_OK );
1099 				CPPUNIT_ASSERT( entry.GetPath(&path) == B_OK );
1100 				CPPUNIT_ASSERT( testSet.test(path.Path()) == true );
1101 			}
1102 		}
1103 		CPPUNIT_ASSERT( query.GetNextEntry(&entry) == B_ENTRY_NOT_FOUND );
1104 		CPPUNIT_ASSERT( query.GetNextRef(&ref) == B_ENTRY_NOT_FOUND );
1105 		CPPUNIT_ASSERT( query.GetNextDirents(ents, bufSize, 1) == 0 );
1106 	}
1107 }
1108 
1109 // FetchTest
1110 void
FetchTest()1111 QueryTest::FetchTest()
1112 {
1113 	// tests:
1114 	// * Clear()/Fetch()
1115 	// * BEntryList interface
1116 
1117 	// Fetch()
1118 	// uninitialized BQuery
1119 	NextSubTest();
1120 	{
1121 		Query query;
1122 		CPPUNIT_ASSERT( query.Fetch() == B_NO_INIT );
1123 	}
1124 	// incompletely initialized BQuery (no predicate)
1125 	NextSubTest();
1126 	{
1127 		Query query;
1128 		BVolume volume(dev_for_path("/boot"));
1129 		CPPUNIT_ASSERT( volume.InitCheck() == B_OK );
1130 		CPPUNIT_ASSERT( query.SetVolume(&volume) == B_OK );
1131 		CPPUNIT_ASSERT( query.Fetch() == B_NO_INIT );
1132 	}
1133 	// incompletely initialized BQuery (no volume)
1134 	NextSubTest();
1135 	{
1136 		Query query;
1137 		CPPUNIT_ASSERT( query.SetPredicate("name=\"ThisShouldNotExist\"")
1138 						== B_OK );
1139 		CPPUNIT_ASSERT( query.Fetch() == B_NO_INIT );
1140 	}
1141 	// incompletely initialized BQuery (invalid predicate)
1142 	NextSubTest();
1143 	{
1144 		Query query;
1145 		CPPUNIT_ASSERT( query.SetPredicate("name=\"ThisShouldNotExist\"&&")
1146 						== B_OK );
1147 		BVolume volume(dev_for_path("/boot"));
1148 		CPPUNIT_ASSERT( volume.InitCheck() == B_OK );
1149 		CPPUNIT_ASSERT( query.SetVolume(&volume) == B_OK );
1150 		CPPUNIT_ASSERT( query.Fetch() == B_BAD_VALUE );
1151 	}
1152 	// initialized BQuery, Fetch() twice
1153 	NextSubTest();
1154 	{
1155 		Query query;
1156 		CPPUNIT_ASSERT( query.SetPredicate("name=\"ThisShouldNotExist\"")
1157 						== B_OK );
1158 		BVolume volume(dev_for_path("/boot"));
1159 		CPPUNIT_ASSERT( volume.InitCheck() == B_OK );
1160 		CPPUNIT_ASSERT( query.SetVolume(&volume) == B_OK );
1161 		CPPUNIT_ASSERT( query.Fetch() == B_OK );
1162 		CPPUNIT_ASSERT( query.Fetch() == B_NOT_ALLOWED );
1163 	}
1164 	// initialized BQuery, successful Fetch(), different predicates
1165 	createVolume(testVolumeImage, testMountPoint, 2);
1166 	fVolumeCreated = true;
1167 	create_test_entries(allTestEntries, allTestEntryCount);
1168 	// ... all files
1169 	NextSubTest();
1170 	{
1171 		QueryTestEntry *entries[] = {
1172 			&file11, &file12, &file21, &file22, &file31, &file32, &file1,
1173 			&file2, &file3
1174 		};
1175 		const int32 entryCount = sizeof(entries) / sizeof(QueryTestEntry*);
1176 		TestFetchPredicate(testMountPoint, "name=\"file*\"", entries,
1177 						   entryCount);
1178 	}
1179 	// ... all entries containing a "l"
1180 	NextSubTest();
1181 	{
1182 		QueryTestEntry *entries[] = {
1183 			&file11, &file12, &link11, &file21, &file22, &link21, &file31,
1184 			&file32, &link31, &file1, &file2, &file3, &link1, &link2, &link3
1185 		};
1186 		const int32 entryCount = sizeof(entries) / sizeof(QueryTestEntry*);
1187 		TestFetchPredicate(testMountPoint, "name=\"*l*\"", entries,
1188 						   entryCount);
1189 	}
1190 	// ... all entries ending on "2"
1191 	NextSubTest();
1192 	{
1193 		QueryTestEntry *entries[] = {
1194 			&subdir12, &file12, &dir2, &subdir22, &file22, &subdir32, &file32,
1195 			&file2, &link2
1196 		};
1197 		const int32 entryCount = sizeof(entries) / sizeof(QueryTestEntry*);
1198 		TestFetchPredicate(testMountPoint, "name=\"*2\"", entries,
1199 						   entryCount);
1200 	}
1201 
1202 	// Clear()
1203 	// uninitialized BQuery
1204 	NextSubTest();
1205 	{
1206 		Query query;
1207 		CPPUNIT_ASSERT( query.Clear() == B_OK );
1208 	}
1209 	// initialized BQuery, Fetch(), Clear(), Fetch()
1210 	NextSubTest();
1211 	{
1212 		Query query;
1213 		CPPUNIT_ASSERT( query.SetPredicate("name=\"ThisShouldNotExist\"")
1214 						== B_OK );
1215 		BVolume volume(dev_for_path("/boot"));
1216 		CPPUNIT_ASSERT( volume.InitCheck() == B_OK );
1217 		CPPUNIT_ASSERT( query.SetVolume(&volume) == B_OK );
1218 		CPPUNIT_ASSERT( query.Fetch() == B_OK );
1219 		CPPUNIT_ASSERT( query.Clear() == B_OK );
1220 		CPPUNIT_ASSERT( query.Fetch() == B_NO_INIT );
1221 	}
1222 	// initialized BQuery, Fetch(), Clear(), re-init, Fetch()
1223 	NextSubTest();
1224 	{
1225 		Query query;
1226 		CPPUNIT_ASSERT( query.SetPredicate("name=\"ThisShouldNotExist\"")
1227 						== B_OK );
1228 		BVolume volume(dev_for_path("/boot"));
1229 		CPPUNIT_ASSERT( volume.InitCheck() == B_OK );
1230 		CPPUNIT_ASSERT( query.SetVolume(&volume) == B_OK );
1231 		CPPUNIT_ASSERT( query.Fetch() == B_OK );
1232 		CPPUNIT_ASSERT( query.Clear() == B_OK );
1233 		CPPUNIT_ASSERT( query.SetPredicate("name=\"ThisShouldNotExist\"")
1234 						== B_OK );
1235 		CPPUNIT_ASSERT( volume.SetTo(dev_for_path("/boot")) == B_OK );
1236 		CPPUNIT_ASSERT( query.SetVolume(&volume) == B_OK );
1237 		CPPUNIT_ASSERT( query.Fetch() == B_OK );
1238 	}
1239 
1240 	// BEntryList interface:
1241 	// empty queries
1242 	NextSubTest();
1243 	{
1244 		Query query;
1245 		CPPUNIT_ASSERT( query.SetPredicate("name=\"ThisShouldNotExist\"")
1246 						== B_OK );
1247 		BVolume volume(dev_for_path("/boot"));
1248 		CPPUNIT_ASSERT( volume.InitCheck() == B_OK );
1249 		CPPUNIT_ASSERT( query.SetVolume(&volume) == B_OK );
1250 		CPPUNIT_ASSERT( query.Fetch() == B_OK );
1251 		BEntry entry;
1252 		entry_ref ref;
1253 		size_t bufSize = (sizeof(dirent) + B_FILE_NAME_LENGTH) * 10;
1254 		char buffer[bufSize];
1255 		dirent *ents = (dirent *)buffer;
1256 		CPPUNIT_ASSERT( query.GetNextEntry(&entry) == B_ENTRY_NOT_FOUND );
1257 		CPPUNIT_ASSERT( query.GetNextRef(&ref) == B_ENTRY_NOT_FOUND );
1258 		CPPUNIT_ASSERT( query.GetNextDirents(ents, bufSize, 1) == 0 );
1259 	}
1260 	// uninitialized queries
1261 	NextSubTest();
1262 	{
1263 		Query query;
1264 		BEntry entry;
1265 		entry_ref ref;
1266 		size_t bufSize = (sizeof(dirent) + B_FILE_NAME_LENGTH) * 10;
1267 		char buffer[bufSize];
1268 		dirent *ents = (dirent *)buffer;
1269 		CPPUNIT_ASSERT( query.GetNextEntry(&entry) == B_FILE_ERROR );
1270 		CPPUNIT_ASSERT( query.GetNextRef(&ref) == B_FILE_ERROR );
1271 		CPPUNIT_ASSERT( query.GetNextDirents(ents, bufSize, 1)
1272 						== B_FILE_ERROR );
1273 	}
1274 	// bad args
1275 	NextSubTest();
1276 	{
1277 		Query query;
1278 		CPPUNIT_ASSERT( query.SetPredicate("name=\"ThisShouldNotExist\"")
1279 						== B_OK );
1280 		BVolume volume(dev_for_path("/boot"));
1281 		CPPUNIT_ASSERT( volume.InitCheck() == B_OK );
1282 		CPPUNIT_ASSERT( query.SetVolume(&volume) == B_OK );
1283 		CPPUNIT_ASSERT( query.Fetch() == B_OK );
1284 		size_t bufSize = (sizeof(dirent) + B_FILE_NAME_LENGTH) * 10;
1285 // R5: crashs when passing a NULL BEntry or entry_ref
1286 #if !TEST_R5
1287 		CPPUNIT_ASSERT( query.GetNextEntry(NULL) == B_BAD_VALUE );
1288 		CPPUNIT_ASSERT( query.GetNextRef(NULL) == B_BAD_VALUE );
1289 #endif
1290 		CPPUNIT_ASSERT( equals(query.GetNextDirents(NULL, bufSize, 1),
1291 							   B_BAD_ADDRESS, B_BAD_VALUE) );
1292 	}
1293 }
1294 
1295 // AddLiveEntries
1296 void
AddLiveEntries(QueryTestEntry ** entries,int32 entryCount,QueryTestEntry ** queryEntries,int32 queryEntryCount)1297 QueryTest::AddLiveEntries(QueryTestEntry **entries, int32 entryCount,
1298 						  QueryTestEntry **queryEntries, int32 queryEntryCount)
1299 {
1300 	create_test_entries(entries, entryCount);
1301 	for (int32 i = 0; i < entryCount; i++) {
1302 		QueryTestEntry *entry = entries[i];
1303 		BNode node(entry->cpath);
1304 		CPPUNIT_ASSERT( node.InitCheck() == B_OK );
1305 		node_ref nref;
1306 		CPPUNIT_ASSERT( node.GetNodeRef(&nref) == B_OK );
1307 		entry->node = nref.node;
1308 		entry_ref ref;
1309 		CPPUNIT_ASSERT( get_ref_for_path(entry->cpath, &ref) == B_OK );
1310 		entry->directory = ref.directory;
1311 		entry->name = ref.name;
1312 	}
1313 	CheckUpdateMessages(B_ENTRY_CREATED, queryEntries, queryEntryCount);
1314 }
1315 
1316 // RemoveLiveEntries
1317 void
RemoveLiveEntries(QueryTestEntry ** entries,int32 entryCount,QueryTestEntry ** queryEntries,int32 queryEntryCount)1318 QueryTest::RemoveLiveEntries(QueryTestEntry **entries, int32 entryCount,
1319 							 QueryTestEntry **queryEntries,
1320 							 int32 queryEntryCount)
1321 {
1322 	delete_test_entries(entries, entryCount);
1323 	CheckUpdateMessages(B_ENTRY_REMOVED, queryEntries, queryEntryCount);
1324 	for (int32 i = 0; i < entryCount; i++) {
1325 		QueryTestEntry *entry = entries[i];
1326 		entry->directory = -1;
1327 		entry->node = -1;
1328 		entry->name = "";
1329 	}
1330 }
1331 
1332 // CheckUpdateMessages
1333 void
CheckUpdateMessages(uint32 opcode,QueryTestEntry ** entries,int32 entryCount)1334 QueryTest::CheckUpdateMessages(uint32 opcode, QueryTestEntry **entries,
1335 							   int32 entryCount)
1336 {
1337 
1338 	// wait for the messages
1339 	snooze(100000);
1340 	if (fApplication) {
1341 		BMessageQueue &queue = fApplication->Handler().Queue();
1342 		CPPUNIT_ASSERT( queue.Lock() );
1343 		try {
1344 			int32 entryNum = 0;
1345 			while (BMessage *_message = queue.NextMessage()) {
1346 				BMessage message(*_message);
1347 				delete _message;
1348 				CPPUNIT_ASSERT( entryNum < entryCount );
1349 				QueryTestEntry *entry = entries[entryNum];
1350 				CPPUNIT_ASSERT( message.what == B_QUERY_UPDATE );
1351 				uint32 msgOpcode;
1352 				CPPUNIT_ASSERT( message.FindInt32("opcode", (int32*)&msgOpcode)
1353 								== B_OK );
1354 				CPPUNIT_ASSERT( msgOpcode == opcode );
1355 				dev_t device;
1356 				CPPUNIT_ASSERT( message.FindInt32("device", &device)
1357 								== B_OK );
1358 				CPPUNIT_ASSERT( device == dev_for_path(testMountPoint) );
1359 				ino_t directory;
1360 				CPPUNIT_ASSERT( message.FindInt64("directory", &directory)
1361 								== B_OK );
1362 				CPPUNIT_ASSERT( directory == entry->directory );
1363 				ino_t node;
1364 				CPPUNIT_ASSERT( message.FindInt64("node", &node)
1365 								== B_OK );
1366 				CPPUNIT_ASSERT( node == entry->node );
1367 				if (opcode == B_ENTRY_CREATED) {
1368 					const char *name;
1369 					CPPUNIT_ASSERT( message.FindString("name", &name)
1370 									== B_OK );
1371 					CPPUNIT_ASSERT( entry->name == name );
1372 				}
1373 				entryNum++;
1374 			}
1375 			CPPUNIT_ASSERT( entryNum == entryCount );
1376 		} catch (CppUnit::Exception exception) {
1377 			queue.Unlock();
1378 			throw exception;
1379 		}
1380 		queue.Unlock();
1381 	}
1382 }
1383 
1384 // LiveTest
1385 void
LiveTest()1386 QueryTest::LiveTest()
1387 {
1388 	// tests:
1389 	// * live queries
1390 	CPPUNIT_ASSERT( fApplication != NULL );
1391 	createVolume(testVolumeImage, testMountPoint, 2);
1392 	fVolumeCreated = true;
1393 	create_test_entries(allTestEntries, allTestEntryCount);
1394 	BMessenger target(&fApplication->Handler());
1395 
1396 	// empty query, add some files, remove some files
1397 	NextSubTest();
1398 	{
1399 		Query query;
1400 		CPPUNIT_ASSERT( query.SetPredicate("name=\"*Argh\"")
1401 						== B_OK );
1402 		BVolume volume(dev_for_path(testMountPoint));
1403 		CPPUNIT_ASSERT( volume.InitCheck() == B_OK );
1404 		CPPUNIT_ASSERT( query.SetVolume(&volume) == B_OK );
1405 		CPPUNIT_ASSERT( query.SetTarget(target) == B_OK );
1406 		CPPUNIT_ASSERT( query.Fetch() == B_OK );
1407 		BEntry entry;
1408 		CPPUNIT_ASSERT( query.GetNextEntry(&entry) == B_ENTRY_NOT_FOUND );
1409 		// the test entries
1410 		QueryTestEntry testDir1(dir1 + "testDirArgh", B_DIRECTORY_NODE);
1411 		QueryTestEntry testDir2(dir1 + "testDir2", B_DIRECTORY_NODE);
1412 		QueryTestEntry testFile1(subdir21 + "testFileArgh", B_FILE_NODE);
1413 		QueryTestEntry testFile2(subdir21 + "testFile2", B_FILE_NODE);
1414 		QueryTestEntry testLink1(subdir32 + "testLinkArgh", B_SYMLINK_NODE,
1415 								 &file11);
1416 		QueryTestEntry testLink2(subdir32 + "testLink2", B_SYMLINK_NODE,
1417 								 &file11);
1418 		QueryTestEntry *entries[] = {
1419 			&testDir1, &testDir2, &testFile1, &testFile2,
1420 			&testLink1, &testLink2
1421 		};
1422 		int32 entryCount = sizeof(entries) / sizeof(QueryTestEntry*);
1423 		QueryTestEntry *queryEntries[] = {
1424 			&testDir1, &testFile1, &testLink1
1425 		};
1426 		int32 queryEntryCount = sizeof(queryEntries) / sizeof(QueryTestEntry*);
1427 		AddLiveEntries(entries, entryCount, queryEntries, queryEntryCount);
1428 		RemoveLiveEntries(entries, entryCount, queryEntries, queryEntryCount);
1429 	}
1430 	// non-empty query, add some files, remove some files
1431 	NextSubTest();
1432 	{
1433 		Query query;
1434 		TestSet testSet;
1435 		CPPUNIT_ASSERT( query.SetTarget(target) == B_OK );
1436 		QueryTestEntry *initialEntries[] = {
1437 			&file11, &file12, &file21, &file22, &file31, &file32, &file1,
1438 			&file2, &file3
1439 		};
1440 		int32 initialEntryCount
1441 			= sizeof(initialEntries) / sizeof(QueryTestEntry*);
1442 		TestFetchPredicateInit(query, testSet, testMountPoint,
1443 							   "name=\"*ile*\"", initialEntries,
1444 							   initialEntryCount);
1445 		BEntry entry;
1446 		while (query.GetNextEntry(&entry) == B_OK) {
1447 			BPath path;
1448 			CPPUNIT_ASSERT( entry.InitCheck() == B_OK );
1449 			CPPUNIT_ASSERT( entry.GetPath(&path) == B_OK );
1450 			CPPUNIT_ASSERT( testSet.test(path.Path()) == true );
1451 		}
1452 		CPPUNIT_ASSERT( testSet.testDone() == true );
1453 		CPPUNIT_ASSERT( query.GetNextEntry(&entry) == B_ENTRY_NOT_FOUND );
1454 		// the test entries
1455 		QueryTestEntry testDir1(dir1 + "testDir1", B_DIRECTORY_NODE);
1456 		QueryTestEntry testDir2(dir1 + "testDir2", B_DIRECTORY_NODE);
1457 		QueryTestEntry testFile1(subdir21 + "testFile1", B_FILE_NODE);
1458 		QueryTestEntry testFile2(subdir21 + "testFile2", B_FILE_NODE);
1459 		QueryTestEntry testLink1(subdir32 + "testLink1", B_SYMLINK_NODE,
1460 								 &file11);
1461 		QueryTestEntry testLink2(subdir32 + "testLink2", B_SYMLINK_NODE,
1462 								 &file11);
1463 		QueryTestEntry testFile3(subdir32 + "testFile3", B_FILE_NODE);
1464 		QueryTestEntry *entries[] = {
1465 			&testDir1, &testDir2, &testFile1, &testFile2,
1466 			&testLink1, &testLink2, &testFile3
1467 		};
1468 		int32 entryCount = sizeof(entries) / sizeof(QueryTestEntry*);
1469 		QueryTestEntry *queryEntries[] = {
1470 			&testFile1, &testFile2, &testFile3
1471 		};
1472 		int32 queryEntryCount = sizeof(queryEntries) / sizeof(QueryTestEntry*);
1473 		AddLiveEntries(entries, entryCount, queryEntries, queryEntryCount);
1474 		RemoveLiveEntries(entries, entryCount, queryEntries, queryEntryCount);
1475 	}
1476 }
1477 
1478 
1479 
1480 
1481 
1482