Merged mblock work-in-progress from eb/mb -r4341:4633 into trunk.
[debian/gnuradio] / mblock / src / lib / qa_mblock_prims.cc
1 /* -*- c++ -*- */
2 /*
3  * Copyright 2006,2007 Free Software Foundation, Inc.
4  * 
5  * This file is part of GNU Radio
6  * 
7  * GNU Radio is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2, or (at your option)
10  * any later version.
11  * 
12  * GNU Radio is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with GNU Radio; see the file COPYING.  If not, write to
19  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <qa_mblock_prims.h>
28 #include <cppunit/TestAssert.h>
29 #include <mb_mblock.h>
30 #include <mb_runtime.h>
31 #include <mb_protocol_class.h>
32 #include <mb_exception.h>
33 #include <mb_msg_queue.h>
34 #include <mb_message.h>
35 #include <mb_mblock_impl.h>
36 #include <mb_msg_accepter.h>
37 #include <stdio.h>
38
39 static pmt_t s_cs = pmt_intern("cs");
40 static pmt_t s_debug = pmt_intern("debug");
41 static pmt_t s_in = pmt_intern("in");
42 static pmt_t s_out = pmt_intern("out");
43   
44
45 // ================================================================
46
47 class dp_1 : public mb_mblock
48 {
49 public:
50   dp_1();
51   ~dp_1();
52 };
53
54 dp_1::dp_1()
55 {
56 }
57
58 dp_1::~dp_1(){}
59
60 // ----------------------------------------------------------------
61
62 class dp_2 : public mb_mblock
63 {
64 public:
65   dp_2();
66   ~dp_2();
67 };
68
69 dp_2::dp_2()
70 {
71   define_port("cs", "cs-protocol", false, mb_port::EXTERNAL);
72 }
73
74 dp_2::~dp_2(){}
75
76 // ----------------------------------------------------------------
77
78 class dp_3 : public mb_mblock
79 {
80 public:
81   dp_3();
82   ~dp_3();
83 };
84
85 dp_3::dp_3()
86 {
87   define_port("cs", "cs-protocol", false, mb_port::EXTERNAL);
88   define_port("cs", "cs-protocol", false, mb_port::EXTERNAL);   // duplicate def
89 }
90
91 dp_3::~dp_3(){}
92
93 // ----------------------------------------------------------------
94
95 void
96 qa_mblock_prims::test_define_ports()
97 {
98   mb_runtime_sptr       rt = mb_make_runtime();
99   // std::vector<mb_port_sptr>  intf;
100
101   mb_mblock_sptr        mb1 = mb_mblock_sptr(new dp_1());
102   // intf = mb1->peer_interface();
103   // CPPUNIT_ASSERT_EQUAL(size_t(0), intf.size());
104
105   // raises runtime_error because of unknown protocol "cs-protocol"
106   CPPUNIT_ASSERT_THROW(mb_mblock_sptr(new dp_2()), std::runtime_error);
107
108   // define the protocol class
109   pmt_t pc = mb_make_protocol_class(pmt_intern("cs-protocol"),
110                                     pmt_list2(pmt_intern("start"),
111                                               pmt_intern("stop")),
112                                     PMT_NIL);
113
114   // std::cout << "pc = " << pc << '\n';
115
116   mb_mblock_sptr mb2 = mb_mblock_sptr(new dp_2());
117
118   // raises pmt_exception because of duplicate port definition of "cs"
119   CPPUNIT_ASSERT_THROW(mb_mblock_sptr(new dp_3()), mbe_duplicate_port);
120 }
121
122 // ================================================================
123
124 class dc_0 : public mb_mblock
125 {
126 public:
127   dc_0();
128   ~dc_0();
129 };
130
131 dc_0::dc_0()
132 {
133 }
134
135 dc_0::~dc_0() {}
136
137 // ----------------------------------------------------------------
138
139 class dc_ok : public mb_mblock
140 {
141 public:
142   dc_ok();
143   ~dc_ok();
144 };
145
146 dc_ok::dc_ok()
147 {
148   define_component("c0", mb_mblock_sptr(new dc_0()));
149   define_component("c1", mb_mblock_sptr(new dc_0()));
150   define_component("c2", mb_mblock_sptr(new dc_0()));
151 }
152
153 dc_ok::~dc_ok(){}
154
155 // ----------------------------------------------------------------
156
157 class dc_not_ok : public mb_mblock
158 {
159 public:
160   dc_not_ok();
161   ~dc_not_ok();
162 };
163
164 dc_not_ok::dc_not_ok()
165   : mb_mblock()
166 {
167   define_component("c0", mb_mblock_sptr(new dc_0()));
168   define_component("c0", mb_mblock_sptr(new dc_0()));   // duplicate name
169 }
170
171 dc_not_ok::~dc_not_ok(){}
172
173 // ----------------------------------------------------------------
174
175 void
176 qa_mblock_prims::test_define_components()
177 {
178   mb_runtime_sptr       rt = mb_make_runtime();
179   mb_mblock_sptr        mb1 = mb_mblock_sptr(new dc_ok());      // OK
180
181   // raises pmt_exception because of duplicate component definition of "c0"
182   CPPUNIT_ASSERT_THROW(mb_mblock_sptr(new dc_not_ok()), mbe_duplicate_component);
183 }
184
185 // ================================================================
186
187 class tc_norm : public mb_mblock
188 {
189 public:
190   tc_norm(){
191     define_port("data", "i/o", false, mb_port::EXTERNAL);
192     define_port("norm", "i/o", false, mb_port::EXTERNAL);
193     define_port("conj", "i/o", true,  mb_port::EXTERNAL);
194     define_port("int",  "i/o", false, mb_port::INTERNAL);
195   }
196
197   ~tc_norm();
198 };
199
200 tc_norm::~tc_norm(){}
201
202 ////////////////////////////////////////////////////////////////
203
204 class tc_0 : public mb_mblock
205 {
206 public:
207   tc_0(){
208     define_port("norm", "i/o", false, mb_port::EXTERNAL);
209     define_port("conj", "i/o", true,  mb_port::EXTERNAL);
210     define_port("int",  "i/o", false, mb_port::INTERNAL);
211
212     define_component("c0", mb_mblock_sptr(new tc_norm()));
213     define_component("c1", mb_mblock_sptr(new tc_norm()));
214     define_component("c2", mb_mblock_sptr(new tc_norm()));
215     define_component("c3", mb_mblock_sptr(new tc_norm()));
216     define_component("c4", mb_mblock_sptr(new tc_norm()));
217     define_component("c5", mb_mblock_sptr(new tc_norm()));
218
219     // OK
220     connect("c0", "norm", "c1", "conj");
221
222     // No:  No such component name
223     CPPUNIT_ASSERT_THROW(connect("foo", "data", "c1", "norm"), mbe_no_such_component);
224
225     // No:  No such port name
226     CPPUNIT_ASSERT_THROW(connect("c0", "data", "c1", "foo"), mbe_no_such_port);
227
228     // No:  already connected
229     CPPUNIT_ASSERT_THROW(connect("c0", "norm", "c2", "data"), mbe_already_connected);
230
231     // No:  already connected
232     CPPUNIT_ASSERT_THROW(connect("c2", "data", "c0", "norm"), mbe_already_connected);
233
234     // No: incompatible ports
235     CPPUNIT_ASSERT_THROW(connect("c1", "norm", "c2", "norm"), mbe_incompatible_ports);
236
237     // OK
238     connect("c1", "norm", "c2", "conj");
239
240     // No: No such port name
241     CPPUNIT_ASSERT_THROW(connect("c2", "norm", "self", "foo"), mbe_no_such_port);
242
243     // No: can't connect to child's internal port
244     CPPUNIT_ASSERT_THROW(connect("c0", "conj", "c2", "int"), mbe_no_such_port);
245
246     // No: can't connect to our own external port
247     CPPUNIT_ASSERT_THROW(connect("self", "norm", "c0", "conj"), mbe_invalid_port_type);
248
249     // OK:  connecting to one of our internal ports
250     connect("self", "int", "c3", "conj");
251
252     // =====  Now test disconnecting some stuff =====
253
254     // Confirm we're already connected
255     CPPUNIT_ASSERT_THROW(connect("self", "int", "c3", "conj"), mbe_already_connected);
256
257     int nc = nconnections();
258     disconnect("self", "int", "c3", "conj");    // disconnect
259     CPPUNIT_ASSERT_EQUAL(nc-1, nconnections());
260     
261     connect("self", "int", "c3", "conj");       // reconnect
262     CPPUNIT_ASSERT_EQUAL(nc, nconnections());
263
264     // confirm we're already connected
265     CPPUNIT_ASSERT_THROW(connect("self", "int", "c3", "conj"), mbe_already_connected);
266
267
268     connect("c0", "conj", "c5", "data");
269     connect("c4", "norm", "c5", "conj");
270     connect("c4", "conj", "c5", "norm");
271
272     nc = nconnections();
273     disconnect_component("c4");
274     CPPUNIT_ASSERT_EQUAL(nc-2, nconnections());
275
276     disconnect_component("c5");
277     CPPUNIT_ASSERT_EQUAL(nc-3, nconnections());
278
279     disconnect_all();
280     CPPUNIT_ASSERT_EQUAL(0, nconnections());
281
282   }
283
284   ~tc_0();
285 };
286
287 tc_0::~tc_0(){}
288
289 ////////////////////////////////////////////////////////////////
290
291 class tc_1 : public mb_mblock
292 {
293 public:
294   tc_1(){
295     define_component("c0", mb_mblock_sptr(new tc_norm()));
296     define_component("c1", mb_mblock_sptr(new tc_norm()));
297
298     connect("c0", "norm", "c1", "conj");
299   }
300
301   ~tc_1();
302 };
303
304 tc_1::~tc_1(){}
305
306 ////////////////////////////////////////////////////////////////
307
308 void
309 qa_mblock_prims::test_connect()
310 {
311   // define the protocol class
312   mb_make_protocol_class(pmt_intern("data"),                    // name of class
313                          pmt_list1(pmt_intern("data")),         // in
314                          PMT_NIL);                              // out
315
316   mb_make_protocol_class(pmt_intern("i/o"),                     // name of class
317                          pmt_list1(pmt_intern("in")),           // in
318                          pmt_list1(pmt_intern("out")));         // out
319
320
321   mb_runtime_sptr       rt = mb_make_runtime();
322   mb_mblock_sptr        mb0 = mb_mblock_sptr(new tc_0());
323 }
324
325 ////////////////////////////////////////////////////////////////
326
327 void
328 qa_mblock_prims::test_msg_queue()
329 {
330   mb_msg_queue  q;
331
332   // check initial state
333   CPPUNIT_ASSERT(q.get_highest_pri_msg() == 0);
334
335   CPPUNIT_ASSERT(MB_NPRI >= 5); // sanity check for this test
336
337   // insert three messages at the same pri and ensure that they come out in order
338   //                       signal       data          metadata     pri
339   q.insert(mb_make_message(PMT_NIL, pmt_from_long(0), PMT_NIL, MB_PRI_BEST + 2));
340   q.insert(mb_make_message(PMT_NIL, pmt_from_long(1), PMT_NIL, MB_PRI_BEST + 2));
341   q.insert(mb_make_message(PMT_NIL, pmt_from_long(2), PMT_NIL, MB_PRI_BEST + 2));
342   
343   CPPUNIT_ASSERT_EQUAL(0L, pmt_to_long(q.get_highest_pri_msg()->data()));
344   CPPUNIT_ASSERT_EQUAL(1L, pmt_to_long(q.get_highest_pri_msg()->data()));
345   CPPUNIT_ASSERT_EQUAL(2L, pmt_to_long(q.get_highest_pri_msg()->data()));
346
347   CPPUNIT_ASSERT(q.get_highest_pri_msg() == 0);
348
349
350   // insert messages of different priorities in pseudo-random order
351   //                       signal   data     metadata     pri
352   q.insert(mb_make_message(PMT_NIL, PMT_NIL, PMT_NIL, MB_PRI_BEST + 3));
353   q.insert(mb_make_message(PMT_NIL, PMT_NIL, PMT_NIL, MB_PRI_BEST + 2));
354   q.insert(mb_make_message(PMT_NIL, PMT_NIL, PMT_NIL, MB_PRI_BEST + 4));
355   q.insert(mb_make_message(PMT_NIL, PMT_NIL, PMT_NIL, MB_PRI_BEST + 0));
356   q.insert(mb_make_message(PMT_NIL, PMT_NIL, PMT_NIL, MB_PRI_BEST + 1));
357   q.insert(mb_make_message(PMT_NIL, PMT_NIL, PMT_NIL, MB_PRI_BEST + 3));
358   q.insert(mb_make_message(PMT_NIL, PMT_NIL, PMT_NIL, MB_PRI_BEST + 2));
359   q.insert(mb_make_message(PMT_NIL, PMT_NIL, PMT_NIL, MB_PRI_BEST + 4));
360   q.insert(mb_make_message(PMT_NIL, PMT_NIL, PMT_NIL, MB_PRI_BEST + 0));
361   q.insert(mb_make_message(PMT_NIL, PMT_NIL, PMT_NIL, MB_PRI_BEST + 1));
362
363   // confirm that they come out in order
364   CPPUNIT_ASSERT_EQUAL(MB_PRI_BEST + 0, q.get_highest_pri_msg()->priority());
365   CPPUNIT_ASSERT_EQUAL(MB_PRI_BEST + 0, q.get_highest_pri_msg()->priority());
366   CPPUNIT_ASSERT_EQUAL(MB_PRI_BEST + 1, q.get_highest_pri_msg()->priority());
367   CPPUNIT_ASSERT_EQUAL(MB_PRI_BEST + 1, q.get_highest_pri_msg()->priority());
368   CPPUNIT_ASSERT_EQUAL(MB_PRI_BEST + 2, q.get_highest_pri_msg()->priority());
369   CPPUNIT_ASSERT_EQUAL(MB_PRI_BEST + 2, q.get_highest_pri_msg()->priority());
370   CPPUNIT_ASSERT_EQUAL(MB_PRI_BEST + 3, q.get_highest_pri_msg()->priority());
371   CPPUNIT_ASSERT_EQUAL(MB_PRI_BEST + 3, q.get_highest_pri_msg()->priority());
372   CPPUNIT_ASSERT_EQUAL(MB_PRI_BEST + 4, q.get_highest_pri_msg()->priority());
373   CPPUNIT_ASSERT_EQUAL(MB_PRI_BEST + 4, q.get_highest_pri_msg()->priority());
374   
375   // check final state
376   CPPUNIT_ASSERT(q.get_highest_pri_msg() == 0);
377 }
378
379 ////////////////////////////////////////////////////////////////
380
381 void
382 qa_mblock_prims::test_make_accepter()
383 {
384   // create a block
385   mb_mblock_sptr mb = mb_mblock_sptr(new dp_2());
386
387   // use "internal use only" method...
388   mb_msg_accepter_sptr accepter = mb->impl()->make_accepter("cs");
389
390   // Now push a few messages into it...
391   //          signal       data          metadata     pri
392   (*accepter)(PMT_NIL, pmt_from_long(0), PMT_NIL, MB_PRI_BEST + 2);
393   (*accepter)(PMT_NIL, pmt_from_long(1), PMT_NIL, MB_PRI_BEST + 2);
394   (*accepter)(PMT_NIL, pmt_from_long(2), PMT_NIL, MB_PRI_BEST + 2);
395
396   // try to pull them out
397
398   pmt_t cs = pmt_intern("cs");
399
400   mb_message_sptr msg = mb->impl()->msgq().get_highest_pri_msg();
401   CPPUNIT_ASSERT(pmt_eq(cs, msg->port_id()));         // confirm that port_id is set
402   CPPUNIT_ASSERT_EQUAL(0L, pmt_to_long(msg->data())); // and that data is correct
403
404   CPPUNIT_ASSERT_EQUAL(1L, pmt_to_long(mb->impl()->msgq().get_highest_pri_msg()->data()));
405   CPPUNIT_ASSERT_EQUAL(2L, pmt_to_long(mb->impl()->msgq().get_highest_pri_msg()->data()));
406 }