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