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 <bsoncxx/exception/exception.hpp>
28 #include <list>
29 #include <math.h>
30 #include <mongocxx/exception/exception.hpp>
31 
32 using namespace fawkes;
33 using namespace mongocxx;
34 
35 //init static variable
38 
39 /**
40  * Setup for each test
41  */
42 void
44 {
47 }
48 
49 TEST_F(RobotMemoryTest, TestsWorking)
50 {
51  ASSERT_EQ(1, 3 - 2);
52 }
53 
54 TEST_F(RobotMemoryTest, AspectAvailable)
55 {
56  ASSERT_FALSE(robot_memory == NULL);
57 }
58 
59 TEST_F(RobotMemoryTest, QueryResultEmpty)
60 {
61  ASSERT_TRUE(
62  robot_memory->insert(bsoncxx::from_json("{'insert':'something to have the namespace'}")));
63  auto qres = robot_memory->query(bsoncxx::from_json("{somekey:'should_not_exist'}"));
64  ASSERT_EQ(qres.begin(), qres.end());
65 }
66 
67 TEST_F(RobotMemoryTest, StoreAndQuery)
68 {
69  ASSERT_TRUE(robot_memory->insert(bsoncxx::from_json("{'testkey':'value'}")));
70  auto qres = robot_memory->query(bsoncxx::from_json("{'testkey':'value'}"));
71  ASSERT_TRUE(contains_pairs(qres, bsoncxx::from_json("{'testkey':'value'}")));
72 }
73 
74 TEST_F(RobotMemoryTest, StoreAndQueryOtherCollection)
75 {
76  ASSERT_TRUE(
77  robot_memory->insert(bsoncxx::from_json("{'testkey':'value'}"), "robmem.othercollection"));
78  auto qres =
79  robot_memory->query(bsoncxx::from_json("{'testkey':'value'}"), "robmem.othercollection");
80  ASSERT_TRUE(contains_pairs(qres, bsoncxx::from_json("{'testkey':'value'}")));
81 }
82 
83 TEST_F(RobotMemoryTest, StoreUpdateQuery)
84 {
85  ASSERT_TRUE(robot_memory->insert("{'inserting':'something',as:0.5}"));
86  ASSERT_TRUE(
87  robot_memory->update(bsoncxx::from_json("{'inserting':'something',as:0.5}"),
88  bsoncxx::from_json("{'updated':'something',as:3.0,extra:true}")));
89  auto qres = robot_memory->query(bsoncxx::from_json("{'updated':'something'}"));
90  ASSERT_TRUE(
91  contains_pairs(qres, bsoncxx::from_json("{'updated':'something',as:3.0,extra:true}")));
92 }
93 
94 TEST_F(RobotMemoryTest, StoreRemoveQuery)
95 {
96  ASSERT_TRUE(robot_memory->insert(bsoncxx::from_json("{to_be:'removed'}")));
97  ASSERT_TRUE(robot_memory->remove(bsoncxx::from_json("{to_be:'removed'}")));
98  auto qres = robot_memory->query(bsoncxx::from_json("{to_be:'removed'}"));
99  ASSERT_EQ(qres.begin(), qres.end());
100 }
101 
102 TEST_F(RobotMemoryTest, Upsert)
103 {
104  ASSERT_TRUE(robot_memory->update(bsoncxx::from_json("{upsert:'not existing'}"),
105  bsoncxx::from_json("{upsert:'should not exist'}"),
106  "",
107  false));
108  auto qres = robot_memory->query(bsoncxx::from_json("{upsert:'should not exist'}"));
109  ASSERT_EQ(qres.begin(), qres.end());
110  ASSERT_TRUE(robot_memory->update(bsoncxx::from_json("{upsert:'not existing'}"),
111  bsoncxx::from_json("{upsert:'should exist'}"),
112  "",
113  true));
114  qres = robot_memory->query(bsoncxx::from_json("{upsert:'should exist'}"));
115  ASSERT_NE(qres.begin(), qres.end());
116 }
117 
118 TEST_F(RobotMemoryTest, QueryInvalid)
119 {
120  ASSERT_THROW(robot_memory->query(bsoncxx::from_json("{key-:+'not existing'}")),
121  bsoncxx::exception);
122 }
123 
124 TEST_F(RobotMemoryTest, InsertInvalidCaught)
125 {
126  ASSERT_THROW(robot_memory->insert(bsoncxx::from_json("{'testkey'::'value'}")),
127  bsoncxx::exception);
128  ASSERT_THROW(robot_memory->insert(bsoncxx::from_json("warbagarbl")), bsoncxx::exception);
129 }
130 
131 TEST_F(RobotMemoryTest, UpdateInvalidCaught)
132 {
133  ASSERT_THROW(robot_memory->update(bsoncxx::from_json("{'testkey':'good'}"),
134  bsoncxx::from_json("{'bad':1.2.3}")),
135  bsoncxx::exception);
136  ASSERT_THROW(robot_memory->update(bsoncxx::from_json("{([})]"), bsoncxx::from_json("{'key':4}")),
137  bsoncxx::exception);
138 }
139 
140 TEST_F(RobotMemoryTest, RemoveInvalidCaught)
141 {
142  ASSERT_THROW(robot_memory->remove(bsoncxx::from_json("{___:4.56!}")), bsoncxx::exception);
143  ASSERT_THROW(robot_memory->remove(bsoncxx::from_json("{([})]")), bsoncxx::exception);
144 }
145 
146 /*
147 TEST_F(RobotMemoryTest, AggregationSumQuery)
148 {
149  ASSERT_TRUE(robot_memory->insert(bsoncxx::from_json("{'agg':'summand',value:0.5}")));
150  ASSERT_TRUE(robot_memory->insert(bsoncxx::from_json("{'agg':'summand',value:0.7}")));
151  ASSERT_TRUE(robot_memory->insert(bsoncxx::from_json("{'agg':'not-summand',value:0.9}")));
152 
153  std::vector<BSONObj> pipeline;
154  pipeline.push_back(bsoncxx::from_json("{'$match': {'agg':'summand'}}"));
155  pipeline.push_back(bsoncxx::from_json("{'$group': {'_id': null, 'total': {'$sum': '$value'}}}"));
156  BSONObj res = robot_memory->aggregate(pipeline);
157  ASSERT_TRUE(contains_pairs(res.getField("result").Array()[0].Obj(), bsoncxx::from_json("{'total':1.2}")));
158 }
159 */
160 
161 TEST_F(RobotMemoryTest, JavaScriptQuery)
162 {
163  ASSERT_TRUE(robot_memory->insert(bsoncxx::from_json("{'testname':'js-query',a:1,b:2}")));
164  ASSERT_TRUE(robot_memory->insert(bsoncxx::from_json("{'testname':'js-query',a:2,b:4}")));
165  ASSERT_TRUE(robot_memory->insert(bsoncxx::from_json("{'testname':'js-query',a:3,b:5}")));
166  auto qres = robot_memory->query(
167  bsoncxx::from_json("{'testname':'js-query', $where: \"return obj.a * 2 == obj.b\"}"));
168  ASSERT_NE(qres.begin(), qres.end());
169  ASSERT_NE(qres.begin(), qres.end());
170  ASSERT_EQ(qres.begin(), qres.end());
171 }
172 
173 TEST_F(RobotMemoryTest, DumpAndResore)
174 {
175  ASSERT_TRUE(robot_memory->drop_collection("robmem.test"));
176  ASSERT_TRUE(robot_memory->insert(bsoncxx::from_json("{'testkey':'value',v:1}")));
177  ASSERT_TRUE(robot_memory->insert(bsoncxx::from_json("{'testkey':'value',v:2}")));
178  ASSERT_TRUE(robot_memory->insert(bsoncxx::from_json("{'testkey':'value',v:3}")));
179  ASSERT_TRUE(robot_memory->dump_collection("robmem.test"));
180  ASSERT_TRUE(robot_memory->drop_collection("robmem.test"));
181  ASSERT_TRUE(robot_memory->restore_collection("robmem.test"));
182  auto qres = robot_memory->query(bsoncxx::from_json("{'testkey':'value'}"));
183  std::list<int> values = {3, 2, 1};
184  for (auto __attribute__((unused)) i : values) {
185  auto doc = qres.begin();
186  ASSERT_NE(doc, qres.end());
187  int got = (*doc)["v"].get_int64();
188  ASSERT_TRUE(std::find(values.begin(), values.end(), got) != values.end());
189  values.remove(got);
190  }
191  ASSERT_EQ(0, values.size());
192  ASSERT_EQ(qres.begin(), qres.end());
193 }
194 
195 TEST_F(RobotMemoryTest, EventTriggerLocal)
196 {
198  rmc->callback_counter = 0;
199  EventTrigger *trigger1 = robot_memory->register_trigger(bsoncxx::from_json("{test:1}"),
200  "robmem.test",
202  rmc);
203  EventTrigger *trigger2 = robot_memory->register_trigger(bsoncxx::from_json("{test:2}"),
204  "robmem.test",
206  rmc);
207  robot_memory->insert(bsoncxx::from_json("{test:0, updateid:55}"), "robmem.test");
208  robot_memory->insert(bsoncxx::from_json("{test:1, updateid:42}"), "robmem.test");
209  robot_memory->update(bsoncxx::from_json("{updateid:42}"),
210  bsoncxx::from_json("{test:2, updateid:42}"),
211  "robmem.test");
212 
213  //wait for robot memory to call triggers
214  usleep(1000000);
215 
216  ASSERT_EQ(2, rmc->callback_counter);
217 
218  robot_memory->remove_trigger(trigger1);
219  robot_memory->remove_trigger(trigger2);
220 }
221 
222 TEST_F(RobotMemoryTest, EventTriggerReplica)
223 {
225  rmc->callback_counter = 0;
226  EventTrigger *trigger1 = robot_memory->register_trigger(bsoncxx::from_json("{test:1}"),
227  "syncedrobmem.test",
229  rmc);
230  EventTrigger *trigger2 = robot_memory->register_trigger(bsoncxx::from_json("{test:2}"),
231  "syncedrobmem.test",
233  rmc);
234 
235  robot_memory->insert(bsoncxx::from_json("{test:0, updateid:55}"), "syncedrobmem.test");
236  robot_memory->insert(bsoncxx::from_json("{test:1, updateid:42}"), "syncedrobmem.test");
237  robot_memory->update(bsoncxx::from_json("{updateid:42}"),
238  bsoncxx::from_json("{test:2, updateid:42}"),
239  "syncedrobmem.test");
240 
241  //wait for robot memory to call triggers
242  usleep(1000000);
243 
244  ASSERT_EQ(2, rmc->callback_counter);
245 
246  robot_memory->remove_trigger(trigger1);
247  robot_memory->remove_trigger(trigger2);
248 }
249 
250 /**
251  * Function for testing if a document contains all key-value pairs of another document
252  * @param obj Document that should be tested
253  * @param exp Document containing all expected key-value pairs
254  * @return Assertion Result
255  */
256 ::testing::AssertionResult
257 RobotMemoryTest::contains_pairs(const bsoncxx::document::view_or_value &obj,
258  const bsoncxx::document::view_or_value &exp)
259 {
260  for (auto expected_element : exp.view()) {
261  if (obj.view().find(expected_element.key()) == obj.view().end()
262  || obj.view()[expected_element.key()].get_value() != expected_element.get_value()) {
263  return ::testing::AssertionFailure()
264  << bsoncxx::to_json(obj) << " does not include {"
265  << bsoncxx::to_json(expected_element.get_document()) << "}";
266  }
267  }
268  return ::testing::AssertionSuccess();
269 }
270 
271 /**
272  * Function for testing if a cursor to a query results contains all key-value
273  * pairs of another document
274  * @param cursor Cursor to a query result that should be tested
275  * @param exp Document containing all expected key-value pairs
276  * @return Assertion Result
277  */
278 ::testing::AssertionResult
279 RobotMemoryTest::contains_pairs(mongocxx::cursor & cursor,
280  const bsoncxx::document::view_or_value &exp)
281 {
282  bsoncxx::builder::basic::document doc;
283  for (auto e : cursor) {
284  doc.append(bsoncxx::builder::concatenate(e));
285  }
286  return contains_pairs(doc.extract(), exp);
287 }
288 
289 TEST_F(RobotMemoryTest, MapReduceQuery)
290 {
291  //Test sums up the amount of ordered products
292  ASSERT_TRUE(
293  robot_memory->insert("{'testname':'mapreduce',order:1, product:1, amount:1}", "robmem.test"));
294  ASSERT_TRUE(
295  robot_memory->insert("{'testname':'mapreduce',order:2, product:1, amount:2}", "robmem.test"));
296  ASSERT_TRUE(
297  robot_memory->insert("{'testname':'mapreduce',order:3, product:2, amount:3}", "robmem.test"));
298  ASSERT_TRUE(
299  robot_memory->insert("{'testname':'mapreduce',order:4, product:2, amount:4}", "robmem.test"));
300  ASSERT_TRUE(robot_memory->insert("{'testname':'not mapreduce',order:1, product:1, amount:2}"));
301  auto res = robot_memory->mapreduce(bsoncxx::from_json("{'testname':'mapreduce'}"),
302  "robmem.test",
303  "function() { emit( this.product, this.amount);}",
304  "function(key, values) { return Array.sum( values )}");
305  ASSERT_TRUE(contains_pairs(
306  res.view(),
307  bsoncxx::from_json("{ok: 1.0, results:[{_id:1.0, value:3.0}, {_id:2.0, value: 7.0}]}")));
308 }
309 
310 TEST_F(RobotMemoryTest, AggregationQuery)
311 {
312  //Test finds maximum with aggregation
313  ASSERT_TRUE(robot_memory->insert(bsoncxx::from_json("{'testname':'agg', v:1}"), "robmem.test"));
314  ASSERT_TRUE(robot_memory->insert(bsoncxx::from_json("{'testname':'agg', v:333}"), "robmem.test"));
315  ASSERT_TRUE(robot_memory->insert(bsoncxx::from_json("{'testname':'agg', v:-20}"), "robmem.test"));
316  ASSERT_TRUE(
317  robot_memory->insert(bsoncxx::from_json("{'testname':'not agg', v:666}"), "robmem.test"));
318  auto qres = robot_memory->aggregate(
319  bsoncxx::from_json("[{$match:{testname:'agg'}}, {$group: {_id:null, max:{$max: '$v'}}}]"),
320  "robmem.test");
321  ASSERT_TRUE(contains_pairs(qres, bsoncxx::from_json("{max: 333}")));
322 }
323 
324 TEST_F(RobotMemoryTest, ComputableRegisterRemove)
325 {
326  TestComputable *tc = new TestComputable();
327  Computable * comp = robot_memory->register_computable(bsoncxx::from_json("{somekey:'value'}"),
328  "robmem.test",
330  tc);
331  robot_memory->remove_computable(comp);
332 }
333 
334 TEST_F(RobotMemoryTest, ComputableCall)
335 {
336  TestComputable *tc = new TestComputable();
337  Computable * comp = robot_memory->register_computable(bsoncxx::from_json("{computed:true}"),
338  "robmem.test",
340  tc);
341  auto qres = robot_memory->query(bsoncxx::from_json("{computed:true}"), "robmem.test");
342  ASSERT_TRUE(contains_pairs(qres, bsoncxx::from_json("{result:'this is computed'}")));
343  robot_memory->remove_computable(comp);
344 }
345 
346 TEST_F(RobotMemoryTest, ComputableCallAddition)
347 {
348  TestComputable *tc = new TestComputable();
349  Computable * comp =
350  robot_memory->register_computable(bsoncxx::from_json(
351  "{compute:'sum',x:{$exists:true},y:{$exists:true}}"),
352  "robmem.test",
354  tc);
355  auto qres = robot_memory->query(bsoncxx::from_json("{compute:'sum',x:15,y:4}"), "robmem.test");
356  ASSERT_TRUE(contains_pairs(qres, bsoncxx::from_json("{sum:19}")));
357  robot_memory->remove_computable(comp);
358 }
359 
360 TEST_F(RobotMemoryTest, ComputableMultiple)
361 {
362  TestComputable *tc = new TestComputable();
363  Computable *comp = robot_memory->register_computable(bsoncxx::from_json("{compute:'multiple'}"),
364  "robmem.test",
366  tc);
367  auto qres = robot_memory->query(bsoncxx::from_json("{compute:'multiple'}"), "robmem.test");
368  std::list<int> values = {3, 2, 1};
369  for (auto __attribute__((unused)) i : values) {
370  auto doc = qres.begin();
371  ASSERT_NE(doc, qres.end());
372  int got = (*doc)["count"].get_int32();
373  ASSERT_TRUE(std::find(values.begin(), values.end(), got) != values.end());
374  values.remove(got);
375  }
376  ASSERT_EQ(0, values.size());
377  ASSERT_EQ(qres.begin(), qres.end());
378  robot_memory->remove_computable(comp);
379 }
380 
382 {
383  Position3DInterface *if3d = blackboard->open_for_writing<Position3DInterface>("test1");
384  if3d->set_frame("test_frame");
385  if3d->set_translation(0, 1.1);
386  if3d->set_translation(1, 2.2);
387  if3d->set_translation(2, 3.3);
388  if3d->write();
389  auto qres =
390  robot_memory->query(bsoncxx::from_json("{interface:'Position3DInterface',id:'test1'}"),
391  "robmem.blackboard");
392  ASSERT_TRUE(
393  contains_pairs(qres,
394  bsoncxx::from_json("{interface:'Position3DInterface',id:'test1',frame:'test_"
395  "frame',translation:[1.1, 2.2, 3.3]}")));
396  blackboard->close(if3d);
397 }
398 
399 TEST_F(RobotMemoryTest, BlackboardComputableMultiple)
400 {
401  Position3DInterface *if3d = blackboard->open_for_writing<Position3DInterface>("test");
402  if3d->set_frame("test_frame");
403  if3d->write();
404  Position3DInterface *if3d_2 = blackboard->open_for_writing<Position3DInterface>("test_2");
405  if3d_2->set_frame("test_frame");
406  if3d_2->write();
407  auto qres = robot_memory->query(bsoncxx::from_json("{interface:'Position3DInterface',id:'test'}"),
408  "robmem.blackboard");
409  ASSERT_TRUE(contains_pairs(qres, bsoncxx::from_json("{interface:'Position3DInterface'}")));
410  blackboard->close(if3d);
411  blackboard->close(if3d_2);
412 }
413 
415 {
416  robot_memory->insert(bsoncxx::from_json(
417  "{name:'test pos', frame:'cam_tag', translation:[0.0, 0.0, 0.0], "
418  "rotation:[0.0, 0.0, 0.0, 1.0]}"),
419  "robmem.test");
420  auto qres =
421  robot_memory->query(bsoncxx::from_json("{name:'test pos', frame:'base_link', allow_tf:true}"),
422  "robmem.test");
423  auto res = *(qres.begin());
424  ASSERT_EQ("base_link", res["frame"].get_utf8().value.to_string());
425  bsoncxx::array::view trans_view{res["translation"].get_array()};
426  ASSERT_TRUE(fabs(0.1 - trans_view[0].get_double()) < 0.001);
427  bsoncxx::array::view rot_view{res["rotation"].get_array()};
428  ASSERT_TRUE(fabs(-0.5 - rot_view[0].get_double()) < 0.001);
429 }
RobotMemoryTest::contains_pairs
::testing::AssertionResult contains_pairs(const bsoncxx::document::view_or_value &obj, const bsoncxx::document::view_or_value &exp)
Function for testing if a document contains all key-value pairs of another document.
Definition: robot_memory_test.cpp:257
RobotMemoryTest::SetUp
virtual void SetUp()
Setup for each test.
Definition: robot_memory_test.cpp:43
RobotMemoryTest
Class for Tests of the RobotMemory.
Definition: robot_memory_test.h:74
RobotMemoryCallback
Class to register callbacks independent of how many tests are using them at the moment.
Definition: robot_memory_test.h:93
fawkes::Position3DInterface::set_frame
void set_frame(const char *new_frame)
Set frame value.
Definition: Position3DInterface.cpp:103
TransformComputable
Definition: transform_computable.h:33
RobotMemory
Definition: robot_memory.h:46
RobotMemoryTestEnvironment::robot_memory
static RobotMemory * robot_memory
Access to Robot Memory.
Definition: robot_memory_test.h:73
fawkes::BlackBoard
Definition: blackboard.h:50
TestComputable
Class providing a computable function.
Definition: robot_memory_test.h:117
EventTrigger
Definition: event_trigger.h:31
BlackboardComputable
Definition: blackboard_computable.h:37
TestComputable::compute
std::list< bsoncxx::document::value > compute(const bsoncxx::document::view &query, const std::string &collection)
Computable function for static document.
Definition: robot_memory_test.h:130
TestComputable::compute_multiple
std::list< bsoncxx::document::value > compute_multiple(const bsoncxx::document::view &query, const std::string &collection)
Computable function for multiple static document.
Definition: robot_memory_test.h:169
fawkes::BlackBoard::close
virtual void close(Interface *interface)=0
fawkes::Position3DInterface::set_translation
void set_translation(unsigned int index, const double new_translation)
Set translation value at given index.
Definition: Position3DInterface.cpp:281
fawkes
RobotMemoryCallback::callback_counter
int callback_counter
Counter for how often the callback was called.
Definition: robot_memory_test.h:100
RobotMemoryCallback::callback_test
void callback_test(const bsoncxx::document::view &update)
Test callback function.
Definition: robot_memory_test.h:108
TestComputable::compute_sum
std::list< bsoncxx::document::value > compute_sum(const bsoncxx::document::view &query, const std::string &collection)
Computable function for addition.
Definition: robot_memory_test.h:147
fawkes::Position3DInterface
Definition: Position3DInterface.h:39
Computable
Definition: computable.h:31
fawkes::Interface::write
void write()
Write from local copy into BlackBoard memory.
Definition: interface.cpp:499
RobotMemoryTestEnvironment::blackboard
static fawkes::BlackBoard * blackboard
Access to blackboard.
Definition: robot_memory_test.h:75
fawkes::BlackBoard::open_for_writing
virtual Interface * open_for_writing(const char *interface_type, const char *identifier, const char *owner=NULL)=0