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