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