sensor: update to test case + fix
[oweals/gnunet.git] / src / sensor / test_gnunet-service-sensor_reporting.c
1   /*
2    * This file is part of GNUnet.
3    * (C)
4    *
5    * GNUnet is free software; you can redistribute it and/or modify
6    * it under the terms of the GNU General Public License as published
7    * by the Free Software Foundation; either version 3, or (at your
8    * option) any later version.
9    *
10    * GNUnet is distributed in the hope that it will be useful, but
11    * WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    * General Public License for more details.
14    *
15    * You should have received a copy of the GNU General Public License
16    * along with GNUnet; see the file COPYING.  If not, write to the
17    * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18    * Boston, MA 02111-1307, USA.
19    */
20 /**
21  * @file sensor/test_gnunet-service-sensor_reporting.c
22  * @brief testcase for gnunet-service-sensor_reporting.c
23  */
24 #include "platform.h"
25 #include "gnunet_util_lib.h"
26 #include "gnunet_testbed_service.h"
27 #include "gnunet_sensor_util_lib.h"
28 #include "sensor.h"
29 #include "gnunet_peerstore_service.h"
30 #include "gnunet_sensor_service.h"
31
32 /**
33  * Number of peers to start for the test
34  */
35 #define NUM_PEERS 2
36
37 /**
38  * Test timeout
39  */
40 #define TEST_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 1)
41
42 /**
43  * How long to wait between starting everything and forcing anomalies to give
44  * the peer enough time to stabilize.
45  */
46 #define ANOMALY_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 3)
47
48 /**
49  * Information about a test peer
50  */
51 struct TestPeer
52 {
53
54   /**
55    * DLL
56    */
57   struct TestPeer *prev;
58
59   /**
60    * DLL
61    */
62   struct TestPeer *next;
63
64   /**
65    * TESTBED information about the peer
66    */
67   struct GNUNET_TESTBED_Peer *testbed_peer;
68
69   /**
70    * Peer indentity
71    */
72   struct GNUNET_PeerIdentity peer_id;
73
74   /**
75    * Peerstore watch context for this peer's anomaly reports
76    */
77   struct GNUNET_PEERSTORE_WatchContext *wc;
78
79   /**
80    * TESTBED operation connecting us to sensor service
81    */
82   struct GNUNET_TESTBED_Operation *sensor_op;
83
84   /**
85    * Sensor service handle
86    */
87   struct GNUNET_SENSOR_Handle *sensor;
88
89   /**
90    * GNUNET scheduler task that forces the anomaly after a stabilization delay
91    */
92   GNUNET_SCHEDULER_TaskIdentifier delay_task;
93
94 };
95
96 /**
97  * Test name
98  */
99 const static char *testname = "test_gnunet-service-sensor_reporting";
100
101 /**
102  * Name of GNUNET config file used in this test
103  */
104 const static char *cfg_filename = "test_gnunet-service-sensor_reporting.conf";
105
106 /**
107  * Test sensor name
108  */
109 const static char *sensor_name = "test-sensor-statistics";
110
111 /**
112  * Path to read test sensor from
113  */
114 const static char *sensor_path_src = "test_sensors/test-sensor-statistics";
115
116 /**
117  * Path to write new test sensor to
118  */
119 const static char *sensor_path_dest =
120     "/tmp/test-gnunet-service-sensor-reporting/test-sensor-statistics";
121
122 /**
123  * Head of DLL of peers
124  */
125 static struct TestPeer *peer_head;
126
127 /**
128  * Tail of DLL of peers
129  */
130 static struct TestPeer *peer_tail;
131
132 /**
133  * Number of peers started and got information for
134  */
135 static int started_peers = 0;
136
137 /**
138  * Number of peers reported anomalies with full list of anomalous neighbors
139  */
140 static int reported_peers = 0;
141
142 /**
143  * TESTBED operation connecting us to peerstore service
144  */
145 static struct GNUNET_TESTBED_Operation *peerstore_op;
146
147 /**
148  * Handle to the peerstore service
149  */
150 static struct GNUNET_PEERSTORE_Handle *peerstore;
151
152 /**
153  * Task used to shutdown / expire the test
154  */
155 static GNUNET_SCHEDULER_TaskIdentifier shutdown_task;
156
157 /**
158  * Status of the test to be returned by main()
159  */
160 static int ok = 1;
161
162
163 static void
164 destroy_peer (struct TestPeer *peer)
165 {
166   if (GNUNET_SCHEDULER_NO_TASK != peer->delay_task)
167   {
168     GNUNET_SCHEDULER_cancel (peer->delay_task);
169     peer->delay_task = GNUNET_SCHEDULER_NO_TASK;
170   }
171   if (NULL != peer->sensor_op)
172   {
173     GNUNET_TESTBED_operation_done (peer->sensor_op);
174     peer->sensor_op = NULL;
175   }
176   if (NULL != peer->wc)
177   {
178     GNUNET_PEERSTORE_watch_cancel (peer->wc);
179     peer->wc = NULL;
180   }
181   GNUNET_free (peer);
182 }
183
184
185 /**
186  * Shutdown task
187  *
188  * @param cls Closure (unused)
189  * @param tc Task context (unused)
190  */
191 static void
192 do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
193 {
194   struct TestPeer *peer;
195
196   peer = peer_head;
197   while (NULL != peer)
198   {
199     GNUNET_CONTAINER_DLL_remove (peer_head, peer_tail, peer);
200     destroy_peer (peer);
201     peer = peer_head;
202   }
203   if (NULL != peerstore_op)
204   {
205     GNUNET_TESTBED_operation_done (peerstore_op);
206     peerstore_op = NULL;
207   }
208   GNUNET_SCHEDULER_shutdown ();
209 }
210
211
212 /**
213  * Write new temp sensor directory with a sensor updated with collection point
214  * peer id
215  */
216 static void
217 write_new_sensor_dir (struct TestPeer *cp_peer)
218 {
219   struct GNUNET_CONFIGURATION_Handle *sensorcfg;
220
221   GNUNET_assert (GNUNET_YES == GNUNET_DISK_file_test (sensor_path_src));
222   sensorcfg = GNUNET_CONFIGURATION_create ();
223   GNUNET_assert (GNUNET_SYSERR !=
224                  GNUNET_CONFIGURATION_parse (sensorcfg, sensor_path_src));
225   GNUNET_CONFIGURATION_set_value_string (sensorcfg, sensor_name,
226                                          "COLLECTION_POINT",
227                                          GNUNET_i2s_full (&cp_peer->peer_id));
228   GNUNET_assert (GNUNET_OK ==
229                  GNUNET_DISK_directory_create_for_file (sensor_path_dest));
230   GNUNET_CONFIGURATION_write (sensorcfg, sensor_path_dest);
231   GNUNET_CONFIGURATION_destroy (sensorcfg);
232 }
233
234
235 /**
236  * Function called by PEERSTORE for each matching record.
237  *
238  * @param cls closure
239  * @param record peerstore record information
240  * @param emsg error message, or NULL if no errors
241  * @return #GNUNET_YES to continue iterating, #GNUNET_NO to stop
242  */
243 static int
244 peerstore_watch_cb (void *cls, struct GNUNET_PEERSTORE_Record *record,
245                     char *emsg)
246 {
247   struct TestPeer *peer = cls;
248   struct GNUNET_SENSOR_DashboardAnomalyEntry *anomaly;
249
250   GNUNET_assert (NULL != record);
251   GNUNET_assert (record->value_size ==
252                  sizeof (struct GNUNET_SENSOR_DashboardAnomalyEntry));
253   anomaly = record->value;
254   GNUNET_assert (0 ==
255                  GNUNET_CRYPTO_cmp_peer_identity (&peer->peer_id,
256                                                   record->peer));
257   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
258               "Peerstore watch got an anomaly report from peer `%s':\n"
259               "Anomalous: %d\n" "Anomalous neigbors: %f.\n",
260               GNUNET_i2s (&peer->peer_id), anomaly->anomalous,
261               anomaly->anomalous_neighbors);
262   if (1 == anomaly->anomalous_neighbors)
263     reported_peers++;
264   if (reported_peers == NUM_PEERS)
265   {
266     ok = 0;
267     GNUNET_SCHEDULER_cancel (shutdown_task);
268     shutdown_task = GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
269   }
270   return GNUNET_YES;
271 }
272
273
274 /**
275  * Task that pushes fake anomalies to running peers
276  */
277 static void
278 force_anomaly_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
279 {
280   struct TestPeer *peer = cls;
281
282   peer->delay_task = GNUNET_SCHEDULER_NO_TASK;
283   GNUNET_SENSOR_force_anomaly (peer->sensor, (char *) sensor_name, GNUNET_YES,
284                                NULL, NULL);
285 }
286
287
288 /**
289  * Callback to be called when sensor service connect operation is completed
290  *
291  * @param cls the callback closure from functions generating an operation
292  * @param op the operation that has been finished
293  * @param ca_result the service handle returned from GNUNET_TESTBED_ConnectAdapter()
294  * @param emsg error message in case the operation has failed; will be NULL if
295  *          operation has executed successfully.
296  */
297 static void
298 sensor_connect_cb (void *cls, struct GNUNET_TESTBED_Operation *op,
299                    void *ca_result, const char *emsg)
300 {
301   struct TestPeer *peer = cls;
302   struct GNUNET_SENSOR_Handle *sensor = ca_result;
303
304   peer->sensor = sensor;
305   peer->delay_task =
306       GNUNET_SCHEDULER_add_delayed (ANOMALY_DELAY, &force_anomaly_task, peer);
307 }
308
309
310 /**
311  * Adapter function called to establish a connection to sensor service.
312  *
313  * @param cls closure
314  * @param cfg configuration of the peer to connect to; will be available until
315  *          GNUNET_TESTBED_operation_done() is called on the operation returned
316  *          from GNUNET_TESTBED_service_connect()
317  * @return service handle to return in 'op_result', NULL on error
318  */
319 static void *
320 sensor_connect_adapter (void *cls,
321                         const struct GNUNET_CONFIGURATION_Handle *cfg)
322 {
323   struct GNUNET_SENSOR_Handle *sensor;
324
325   sensor = GNUNET_SENSOR_connect (cfg);
326   return sensor;
327 }
328
329
330 /**
331  * Adapter function called to destroy a connection to sensor service.
332  *
333  * @param cls closure
334  * @param op_result service handle returned from the connect adapter
335  */
336 static void
337 sensor_disconnect_adapter (void *cls, void *op_result)
338 {
339   struct GNUNET_SENSOR_Handle *sensor = op_result;
340
341   GNUNET_SENSOR_disconnect (sensor);
342 }
343
344
345 /**
346  * Callback to be called when sensor service is started
347  *
348  * @param cls the callback closure from functions generating an operation
349  * @param op the operation that has been finished
350  * @param emsg error message in case the operation has failed; will be NULL if
351  *          operation has executed successfully.
352  */
353 static void
354 sensor_service_started (void *cls, struct GNUNET_TESTBED_Operation *op,
355                         const char *emsg)
356 {
357   struct TestPeer *peer = cls;
358
359   GNUNET_assert (NULL == emsg);
360   peer->sensor_op =
361       GNUNET_TESTBED_service_connect (NULL, peer->testbed_peer, "sensor",
362                                       &sensor_connect_cb, peer,
363                                       &sensor_connect_adapter,
364                                       &sensor_disconnect_adapter, NULL);
365   GNUNET_TESTBED_operation_done (op);
366 }
367
368
369 /**
370  * Callback to be called when peerstore service connect operation is completed
371  *
372  * @param cls the callback closure from functions generating an operation
373  * @param op the operation that has been finished
374  * @param ca_result the service handle returned from GNUNET_TESTBED_ConnectAdapter()
375  * @param emsg error message in case the operation has failed; will be NULL if
376  *          operation has executed successfully.
377  */
378 static void
379 peerstore_connect_cb (void *cls, struct GNUNET_TESTBED_Operation *op,
380                       void *ca_result, const char *emsg)
381 {
382   struct TestPeer *peer;
383
384   peer = peer_head;
385   while (NULL != peer)
386   {
387     GNUNET_PEERSTORE_watch (peerstore, "sensordashboard-anomalies",
388                             &peer->peer_id, sensor_name, &peerstore_watch_cb,
389                             peer);
390     /* Start sensor service */
391     GNUNET_TESTBED_peer_manage_service (NULL, peer->testbed_peer, "sensor",
392                                         &sensor_service_started, peer, 1);
393     peer = peer->next;
394   }
395 }
396
397
398 /**
399  * Adapter function called to establish a connection to peerstore service.
400  *
401  * @param cls closure
402  * @param cfg configuration of the peer to connect to; will be available until
403  *          GNUNET_TESTBED_operation_done() is called on the operation returned
404  *          from GNUNET_TESTBED_service_connect()
405  * @return service handle to return in 'op_result', NULL on error
406  */
407 static void *
408 peerstore_connect_adapter (void *cls,
409                            const struct GNUNET_CONFIGURATION_Handle *cfg)
410 {
411   peerstore = GNUNET_PEERSTORE_connect (cfg);
412   GNUNET_assert (NULL != peerstore);
413   return peerstore;
414 }
415
416
417 /**
418  * Adapter function called to destroy a connection to peerstore service.
419  *
420  * @param cls closure
421  * @param op_result service handle returned from the connect adapter
422  */
423 static void
424 peerstore_disconnect_adapter (void *cls, void *op_result)
425 {
426   GNUNET_PEERSTORE_disconnect (peerstore, GNUNET_NO);
427   peerstore = NULL;
428   peerstore_op = NULL;
429 }
430
431
432 /**
433  * Callback to be called when dashboard service is started
434  *
435  * @param cls the callback closure from functions generating an operation
436  * @param op the operation that has been finished
437  * @param emsg error message in case the operation has failed; will be NULL if
438  *          operation has executed successfully.
439  */
440 static void
441 dashboard_started (void *cls, struct GNUNET_TESTBED_Operation *op,
442                    const char *emsg)
443 {
444   GNUNET_assert (NULL == emsg);
445   GNUNET_TESTBED_operation_done (op);
446   /* Connect to peerstore service on first peer */
447   peerstore_op =
448       GNUNET_TESTBED_service_connect (NULL, peer_head->testbed_peer,
449                                       "peerstore", &peerstore_connect_cb, NULL,
450                                       &peerstore_connect_adapter,
451                                       &peerstore_disconnect_adapter, NULL);
452 }
453
454
455 /**
456  * Callback to be called when the requested peer information is available
457  *
458  * @param cb_cls the closure from GNUNET_TETSBED_peer_get_information()
459  * @param op the operation this callback corresponds to
460  * @param pinfo the result; will be NULL if the operation has failed
461  * @param emsg error message if the operation has failed; will be NULL if the
462  *          operation is successfull
463  */
464 static void
465 peer_info_cb (void *cb_cls, struct GNUNET_TESTBED_Operation *op,
466               const struct GNUNET_TESTBED_PeerInformation *pinfo,
467               const char *emsg)
468 {
469   struct GNUNET_TESTBED_Peer *testbed_peer = cb_cls;
470   struct TestPeer *peer;
471
472   peer = GNUNET_new (struct TestPeer);
473
474   peer->testbed_peer = testbed_peer;
475   peer->delay_task = GNUNET_SCHEDULER_NO_TASK;
476   GNUNET_CRYPTO_get_peer_identity (pinfo->result.cfg, &peer->peer_id);
477   if (NULL == peer_head)        /* First peer (collection point) */
478   {
479     /* Rewrite sensor with collection point peer id */
480     write_new_sensor_dir (peer);
481   }
482   GNUNET_CONTAINER_DLL_insert_tail (peer_head, peer_tail, peer);
483   started_peers++;
484   if (NUM_PEERS == started_peers)
485   {
486     /* Start dashboard service on first peer */
487     GNUNET_TESTBED_peer_manage_service (NULL, peer_head->testbed_peer,
488                                         "sensordashboard", &dashboard_started,
489                                         NULL, 1);
490   }
491   GNUNET_TESTBED_operation_done (op);
492 }
493
494
495 /**
496  * Signature of a main function for a testcase.
497  *
498  * @param cls closure
499  * @param h the run handle
500  * @param num_peers number of peers in 'peers'
501  * @param peers handle to peers run in the testbed.  NULL upon timeout (see
502  *          GNUNET_TESTBED_test_run()).
503  * @param links_succeeded the number of overlay link connection attempts that
504  *          succeeded
505  * @param links_failed the number of overlay link connection attempts that
506  *          failed
507  * @see GNUNET_TESTBED_test_run()
508  */
509 static void
510 test_master (void *cls, struct GNUNET_TESTBED_RunHandle *h,
511              unsigned int num_peers, struct GNUNET_TESTBED_Peer **peers,
512              unsigned int links_succeeded, unsigned int links_failed)
513 {
514   int i;
515
516   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
517               "%d peers started. %d links succeeded. %d links failed.\n",
518               num_peers, links_succeeded, links_failed);
519   GNUNET_assert (NUM_PEERS == num_peers);
520   GNUNET_assert (0 == links_failed);
521   /* Schedule test timeout */
522   shutdown_task =
523       GNUNET_SCHEDULER_add_delayed (TEST_TIMEOUT, &do_shutdown, NULL);
524   /* Collect peer information */
525   for (i = 0; i < num_peers; i++)
526   {
527     GNUNET_TESTBED_peer_get_information (peers[i],
528                                          GNUNET_TESTBED_PIT_CONFIGURATION,
529                                          &peer_info_cb, peers[i]);
530   }
531 }
532
533
534 int
535 main (int argc, char *argv[])
536 {
537   GNUNET_log_setup (testname, "INFO", NULL);
538   if (GNUNET_OK ==
539       GNUNET_TESTBED_test_run (testname, cfg_filename, NUM_PEERS, 0, NULL, NULL,
540                                &test_master, NULL))
541     return ok;
542   return 1;
543 }