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