Fawkes API  Fawkes Development Version
robot_memory_test.cpp
1 /***************************************************************************
2  * robot_memory_test.cpp - Test for the RobotMemory and their test class
3  *
4  *
5  * Created: 3:11:53 PM 2016
6  * Copyright 2016 Frederik Zwilling
7  ****************************************************************************/
8 
9 /* This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU Library General Public License for more details.
18  *
19  * Read the full text in the LICENSE.GPL file in the doc directory.
20  */
21 
22 #include "robot_memory_test.h"
23 
24 #include <interfaces/Position3DInterface.h>
25 
26 #include <algorithm>
27 #include <list>
28 #include <math.h>
29 
30 using namespace fawkes;
31 using namespace mongo;
32 
33 //init static variable
36 
37 /**
38  * Setup for each test
39  */
40 void
42 {
45 }
46 
47 TEST_F(RobotMemoryTest, TestsWorking)
48 {
49  ASSERT_EQ(1, 3 - 2);
50 }
51 
52 TEST_F(RobotMemoryTest, AspectAvailable)
53 {
54  ASSERT_FALSE(robot_memory == NULL);
55 }
56 
57 TEST_F(RobotMemoryTest, QueryResultEmpty)
58 {
59  ASSERT_TRUE(robot_memory->insert("{'insert':'something to have the namespace'}"));
60  QResCursor qres = robot_memory->query("{somekey:'should_not_exist'}");
61  ASSERT_FALSE(qres->more());
62 }
63 
64 TEST_F(RobotMemoryTest, StoreAndQuery)
65 {
66  ASSERT_TRUE(robot_memory->insert("{'testkey':'value'}"));
67  QResCursor qres = robot_memory->query("{'testkey':'value'}");
68  ASSERT_TRUE(qres->more());
69  ASSERT_TRUE(contains_pairs(qres->next(), fromjson("{'testkey':'value'}")));
70 }
71 
72 TEST_F(RobotMemoryTest, StoreAndQueryOtherCollection)
73 {
74  ASSERT_TRUE(robot_memory->insert("{'testkey':'value'}", "robmem.othercollection"));
75  QResCursor qres = robot_memory->query("{'testkey':'value'}", "robmem.othercollection");
76  ASSERT_TRUE(qres->more());
77  ASSERT_TRUE(contains_pairs(qres->next(), fromjson("{'testkey':'value'}")));
78 }
79 
80 TEST_F(RobotMemoryTest, StoreUpdateQuery)
81 {
82  ASSERT_TRUE(robot_memory->insert("{'inserting':'something',as:0.5}"));
83  ASSERT_TRUE(robot_memory->update("{'inserting':'something',as:0.5}",
84  "{'updated':'something',as:3.0,extra:true}"));
85  QResCursor qres = robot_memory->query("{'updated':'something'}");
86  ASSERT_TRUE(qres->more());
87  ASSERT_TRUE(contains_pairs(qres->next(), fromjson("{'updated':'something',as:3.0,extra:true}")));
88 }
89 
90 TEST_F(RobotMemoryTest, StoreRemoveQuery)
91 {
92  ASSERT_TRUE(robot_memory->insert("{to_be:'removed'}"));
93  ASSERT_TRUE(robot_memory->remove("{to_be:'removed'}"));
94  QResCursor qres = robot_memory->query("{to_be:'removed'}");
95  ASSERT_FALSE(qres->more());
96 }
97 
98 TEST_F(RobotMemoryTest, Upsert)
99 {
100  ASSERT_TRUE(
101  robot_memory->update("{upsert:'not existing'}", "{upsert:'should not exist'}", "", false));
102  QResCursor qres = robot_memory->query("{upsert:'should not exist'}");
103  ASSERT_FALSE(qres->more());
104  ASSERT_TRUE(robot_memory->update("{upsert:'not existing'}", "{upsert:'should exist'}", "", true));
105  qres = robot_memory->query("{upsert:'should exist'}");
106  ASSERT_TRUE(qres->more());
107 }
108 
109 TEST_F(RobotMemoryTest, QueryInvalid)
110 {
111  ASSERT_THROW(robot_memory->query("{key-:+'not existing'}"), mongo::DBException);
112 }
113 
114 TEST_F(RobotMemoryTest, InsertInvalidCaught)
115 {
116  ASSERT_THROW(robot_memory->insert("{'testkey'::'value'}"), mongo::DBException);
117  ASSERT_THROW(robot_memory->insert("warbagarbl"), mongo::DBException);
118 }
119 
120 TEST_F(RobotMemoryTest, UpdateInvalidCaught)
121 {
122  ASSERT_THROW(robot_memory->update("{'testkey':'good'}", "{'bad':1.2.3}"), mongo::DBException);
123  ASSERT_THROW(robot_memory->update("{([})]", "{'key':4}"), mongo::DBException);
124 }
125 
126 TEST_F(RobotMemoryTest, RemoveInvalidCaught)
127 {
128  ASSERT_THROW(robot_memory->remove("{___:4.56!}"), mongo::DBException);
129  ASSERT_THROW(robot_memory->remove("{([})]"), mongo::DBException);
130 }
131 
132 TEST_F(RobotMemoryTest, AggregationSumQuery)
133 {
134  ASSERT_TRUE(robot_memory->insert("{'agg':'summand',value:0.5}"));
135  ASSERT_TRUE(robot_memory->insert("{'agg':'summand',value:0.7}"));
136  ASSERT_TRUE(robot_memory->insert("{'agg':'not-summand',value:0.9}"));
137 
138  std::vector<BSONObj> pipeline;
139  pipeline.push_back(fromjson("{'$match': {'agg':'summand'}}"));
140  pipeline.push_back(fromjson("{'$group': {'_id': null, 'total': {'$sum': '$value'}}}"));
141  BSONObj res = robot_memory->aggregate(pipeline);
142  ASSERT_TRUE(contains_pairs(res.getField("result").Array()[0].Obj(), fromjson("{'total':1.2}")));
143 }
144 
145 TEST_F(RobotMemoryTest, JavaScriptQuery)
146 {
147  ASSERT_TRUE(robot_memory->insert("{'testname':'js-query',a:1,b:2}"));
148  ASSERT_TRUE(robot_memory->insert("{'testname':'js-query',a:2,b:4}"));
149  ASSERT_TRUE(robot_memory->insert("{'testname':'js-query',a:3,b:5}"));
150  QResCursor qres =
151  robot_memory->query("{'testname':'js-query', $where: \"return obj.a * 2 == obj.b\"}");
152  ASSERT_TRUE(qres->more());
153  qres->next();
154  ASSERT_TRUE(qres->more());
155  qres->next();
156  ASSERT_FALSE(qres->more());
157 }
158 
159 TEST_F(RobotMemoryTest, DumpAndResore)
160 {
161  ASSERT_TRUE(robot_memory->drop_collection("robmem.test"));
162  ASSERT_TRUE(robot_memory->insert("{'testkey':'value',v:1}"));
163  ASSERT_TRUE(robot_memory->insert("{'testkey':'value',v:2}"));
164  ASSERT_TRUE(robot_memory->insert("{'testkey':'value',v:3}"));
165  ASSERT_TRUE(robot_memory->dump_collection("robmem.test"));
166  ASSERT_TRUE(robot_memory->drop_collection("robmem.test"));
167  ASSERT_TRUE(robot_memory->restore_collection("robmem.test"));
168  QResCursor qres = robot_memory->query("{'testkey':'value'}");
169  std::list<int> values = {3, 2, 1};
170  ASSERT_TRUE(qres->more());
171  int got = qres->next().getField("v").Int();
172  ASSERT_TRUE(std::find(values.begin(), values.end(), got) != values.end());
173  values.remove(got);
174  ASSERT_TRUE(qres->more());
175  got = qres->next().getField("v").Int();
176  ASSERT_TRUE(std::find(values.begin(), values.end(), got) != values.end());
177  values.remove(got);
178  ASSERT_TRUE(qres->more());
179  got = qres->next().getField("v").Int();
180  ASSERT_TRUE(std::find(values.begin(), values.end(), got) != values.end());
181  values.remove(got);
182  ASSERT_EQ(0, values.size());
183  ASSERT_FALSE(qres->more());
184 }
185 
186 TEST_F(RobotMemoryTest, EventTriggerLocal)
187 {
189  rmc->callback_counter = 0;
190  EventTrigger *trigger1 = robot_memory->register_trigger(fromjson("{test:1}"),
191  "robmem.test",
193  rmc);
194  EventTrigger *trigger2 = robot_memory->register_trigger(fromjson("{test:2}"),
195  "robmem.test",
197  rmc);
198  robot_memory->insert(fromjson("{test:0, updateid:55}"), "robmem.test");
199  robot_memory->insert(fromjson("{test:1, updateid:42}"), "robmem.test");
200  robot_memory->update(fromjson("{updateid:42}"), fromjson("{test:2, updateid:42}"), "robmem.test");
201 
202  //wait for robot memory to call triggers
203  usleep(1000000);
204 
205  ASSERT_EQ(2, rmc->callback_counter);
206 
207  robot_memory->remove_trigger(trigger1);
208  robot_memory->remove_trigger(trigger2);
209 }
210 
211 TEST_F(RobotMemoryTest, EventTriggerReplica)
212 {
214  rmc->callback_counter = 0;
215  EventTrigger *trigger1 = robot_memory->register_trigger(fromjson("{test:1}"),
216  "syncedrobmem.test",
218  rmc);
219  EventTrigger *trigger2 = robot_memory->register_trigger(fromjson("{test:2}"),
220  "syncedrobmem.test",
222  rmc);
223 
224  robot_memory->insert(fromjson("{test:0, updateid:55}"), "syncedrobmem.test");
225  robot_memory->insert(fromjson("{test:1, updateid:42}"), "syncedrobmem.test");
226  robot_memory->update(fromjson("{updateid:42}"),
227  fromjson("{test:2, updateid:42}"),
228  "syncedrobmem.test");
229 
230  //wait for robot memory to call triggers
231  usleep(1000000);
232 
233  ASSERT_EQ(2, rmc->callback_counter);
234 
235  robot_memory->remove_trigger(trigger1);
236  robot_memory->remove_trigger(trigger2);
237 }
238 
239 /**
240  * Function for testing if a document contains all key-value pairs of another document
241  * @param obj Document that should be tested
242  * @param exp Document containing all expected key-value pairs
243  * @return Assertion Result
244  */
245 ::testing::AssertionResult
246 RobotMemoryTest::contains_pairs(BSONObj obj, BSONObj exp)
247 {
248  for (BSONObjIterator it = exp.begin(); it.more();) {
249  BSONElement kvpair = it.next();
250  //printf("checking %s\n", kvpair.toString().c_str());
251  if (!obj.hasElement(kvpair.fieldName()) || obj.getField(kvpair.fieldName()) != kvpair) {
252  return ::testing::AssertionFailure()
253  << obj.toString() << " does not include {" << kvpair.toString() << "}";
254  }
255  }
256  return ::testing::AssertionSuccess();
257 }
258 
259 TEST_F(RobotMemoryTest, MapReduceQuery)
260 {
261  //Test sums up the amount of ordered products
262  ASSERT_TRUE(
263  robot_memory->insert("{'testname':'mapreduce',order:1, product:1, amount:1}", "robmem.test"));
264  ASSERT_TRUE(
265  robot_memory->insert("{'testname':'mapreduce',order:2, product:1, amount:2}", "robmem.test"));
266  ASSERT_TRUE(
267  robot_memory->insert("{'testname':'mapreduce',order:3, product:2, amount:3}", "robmem.test"));
268  ASSERT_TRUE(
269  robot_memory->insert("{'testname':'mapreduce',order:4, product:2, amount:4}", "robmem.test"));
270  ASSERT_TRUE(robot_memory->insert("{'testname':'not mapreduce',order:1, product:1, amount:2}"));
271  BSONObj res = robot_memory->mapreduce(fromjson("{'testname':'mapreduce'}"),
272  "robmem.test",
273  "function() { emit( this.product, this.amount);}",
274  "function(key, values) { return Array.sum( values )}");
275  ASSERT_TRUE(
276  contains_pairs(res,
277  fromjson("{ok: 1.0, results:[{_id:1.0, value:3.0}, {_id:2.0, value: 7.0}]}")));
278 }
279 
280 TEST_F(RobotMemoryTest, AggregationQuery)
281 {
282  //Test finds maximum with aggregation
283  ASSERT_TRUE(robot_memory->insert("{'testname':'agg', v:1}", "robmem.test"));
284  ASSERT_TRUE(robot_memory->insert("{'testname':'agg', v:333}", "robmem.test"));
285  ASSERT_TRUE(robot_memory->insert("{'testname':'agg', v:-20}", "robmem.test"));
286  ASSERT_TRUE(robot_memory->insert("{'testname':'not agg', v:666}", "robmem.test"));
287  QResCursor qres = robot_memory->aggregate(
288  fromjson("[{$match:{testname:'agg'}}, {$group: {_id:null, max:{$max: '$v'}}}]"), "robmem.test");
289  ASSERT_TRUE(qres->more());
290  ASSERT_TRUE(contains_pairs(qres->next(), fromjson("{max: 333}")));
291 }
292 
293 TEST_F(RobotMemoryTest, ComputableRegisterRemove)
294 {
295  TestComputable *tc = new TestComputable();
296  Computable * comp = robot_memory->register_computable(fromjson("{somekey:'value'}"),
297  "robmem.test",
299  tc);
300  robot_memory->remove_computable(comp);
301 }
302 
303 TEST_F(RobotMemoryTest, ComputableCall)
304 {
305  TestComputable *tc = new TestComputable();
306  Computable * comp = robot_memory->register_computable(fromjson("{computed:true}"),
307  "robmem.test",
309  tc);
310  QResCursor qres = robot_memory->query(fromjson("{computed:true}"), "robmem.test");
311  ASSERT_TRUE(qres->more());
312  ASSERT_TRUE(contains_pairs(qres->next(), fromjson("{result:'this is computed'}")));
313  robot_memory->remove_computable(comp);
314 }
315 
316 TEST_F(RobotMemoryTest, ComputableCallAddition)
317 {
318  TestComputable *tc = new TestComputable();
319  Computable * comp =
320  robot_memory->register_computable(fromjson("{compute:'sum',x:{$exists:true},y:{$exists:true}}"),
321  "robmem.test",
323  tc);
324  QResCursor qres = robot_memory->query(fromjson("{compute:'sum',x:15,y:4}"), "robmem.test");
325  ASSERT_TRUE(qres->more());
326  ASSERT_TRUE(contains_pairs(qres->next(), fromjson("{sum:19}")));
327  robot_memory->remove_computable(comp);
328 }
329 
330 TEST_F(RobotMemoryTest, ComputableMultiple)
331 {
332  TestComputable *tc = new TestComputable();
333  Computable * comp = robot_memory->register_computable(fromjson("{compute:'multiple'}"),
334  "robmem.test",
336  tc);
337  QResCursor qres = robot_memory->query(fromjson("{compute:'multiple'}"), "robmem.test");
338  std::list<int> values = {3, 2, 1};
339  ASSERT_TRUE(qres->more());
340  int got = qres->next().getField("count").Int();
341  ASSERT_TRUE(std::find(values.begin(), values.end(), got) != values.end());
342  values.remove(got);
343  ASSERT_TRUE(qres->more());
344  got = qres->next().getField("count").Int();
345  ASSERT_TRUE(std::find(values.begin(), values.end(), got) != values.end());
346  values.remove(got);
347  ASSERT_TRUE(qres->more());
348  got = qres->next().getField("count").Int();
349  ASSERT_TRUE(std::find(values.begin(), values.end(), got) != values.end());
350  values.remove(got);
351  ASSERT_EQ(0, values.size());
352  ASSERT_FALSE(qres->more());
353  robot_memory->remove_computable(comp);
354 }
355 
357 {
358  Position3DInterface *if3d = blackboard->open_for_writing<Position3DInterface>("test1");
359  if3d->set_frame("test_frame");
360  if3d->set_translation(0, 1.1);
361  if3d->set_translation(1, 2.2);
362  if3d->set_translation(2, 3.3);
363  if3d->write();
364  QResCursor qres = robot_memory->query(fromjson("{interface:'Position3DInterface',id:'test1'}"),
365  "robmem.blackboard");
366  ASSERT_TRUE(qres->more());
367  ASSERT_TRUE(contains_pairs(qres->next(),
368  fromjson("{interface:'Position3DInterface',id:'test1',frame:'test_"
369  "frame',translation:[1.1, 2.2, 3.3]}")));
370  blackboard->close(if3d);
371 }
372 
373 TEST_F(RobotMemoryTest, BlackboardComputableMultiple)
374 {
375  Position3DInterface *if3d = blackboard->open_for_writing<Position3DInterface>("test");
376  if3d->set_frame("test_frame");
377  if3d->write();
378  Position3DInterface *if3d_2 = blackboard->open_for_writing<Position3DInterface>("test_2");
379  if3d_2->set_frame("test_frame");
380  if3d_2->write();
381  QResCursor qres = robot_memory->query(fromjson("{interface:'Position3DInterface',id:'test'}"),
382  "robmem.blackboard");
383  ASSERT_TRUE(qres->more());
384  ASSERT_TRUE(contains_pairs(qres->next(), fromjson("{interface:'Position3DInterface'}")));
385  blackboard->close(if3d);
386  blackboard->close(if3d_2);
387 }
388 
390 {
391  robot_memory->insert("{name:'test pos', frame:'cam_tag', translation:[0.0, 0.0, 0.0], "
392  "rotation:[0.0, 0.0, 0.0, 1.0]}",
393  "robmem.test");
394  QResCursor qres =
395  robot_memory->query(fromjson("{name:'test pos', frame:'base_link', allow_tf:true}"),
396  "robmem.test");
397  ASSERT_TRUE(qres->more());
398  BSONObj res = qres->next();
399  ASSERT_EQ("base_link", res.getField("frame").String());
400  ASSERT_TRUE(fabs(0.1 - res.getField("translation").Array()[0].Double()) < 0.001);
401  ASSERT_TRUE(fabs(-0.5 - res.getField("rotation").Array()[0].Double()) < 0.001);
402 }
void set_frame(const char *new_frame)
Set frame value.
virtual void SetUp()
Setup for each test.
Fawkes library namespace.
Class holding information for a single computable this class also enhances computed documents by addi...
Definition: computable.h:29
Computable providing access to blackboard interfaces.
void set_translation(unsigned int index, const double new_translation)
Set translation value at given index.
Class holding all information about an EventTrigger.
Definition: event_trigger.h:32
void write()
Write from local copy into BlackBoard memory.
Definition: interface.cpp:494
Computable proving positions in other frames by using transforms.
Class to register callbacks independent of how many tests are using them at the moment.
Position3DInterface Fawkes BlackBoard Interface.
std::list< mongo::BSONObj > compute(const mongo::BSONObj &query, const std::string &collection)
Computable function for static document.
Class providing a computable function.
static RobotMemory * robot_memory
Access to Robot Memory.
Access to the robot memory based on mongodb.
Definition: robot_memory.h:48
The BlackBoard abstract class.
Definition: blackboard.h:45
static fawkes::BlackBoard * blackboard
Access to blackboard.
std::list< mongo::BSONObj > compute_sum(const mongo::BSONObj &query, const std::string &collection)
Computable function for addition.
virtual Interface * open_for_writing(const char *interface_type, const char *identifier, const char *owner=NULL)=0
Open interface for writing.
::testing::AssertionResult contains_pairs(mongo::BSONObj obj, mongo::BSONObj exp)
Function for testing if a document contains all key-value pairs of another document.
int callback_counter
Counter for how often the callback was called.
void callback_test(mongo::BSONObj update)
Test callback function.
std::list< mongo::BSONObj > compute_multiple(const mongo::BSONObj &query, const std::string &collection)
Computable function for multiple static document.
virtual void close(Interface *interface)=0
Close interface.
Class for Tests of the RobotMemory.