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