1 /* -*- c++ -*- */
2 /*
3  * Copyright 2010,2013 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 3, 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., 51 Franklin Street,
20  * Boston, MA 02110-1301, USA.
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26 
27 #include <gnuradio/block.h>
28 #include <gnuradio/blocks/annotator_1to1.h>
29 #include <gnuradio/blocks/annotator_alltoall.h>
30 #include <gnuradio/blocks/head.h>
31 #include <gnuradio/blocks/keep_one_in_n.h>
32 #include <gnuradio/blocks/null_sink.h>
33 #include <gnuradio/blocks/null_source.h>
34 #include <gnuradio/top_block.h>
35 #include <boost/test/unit_test.hpp>
36 
37 
38 // ----------------------------------------------------------------
39 
40 // set to 1 to turn on debug output
41 // The debug output fully checks that the tags seen are what are expected. While
42 // this behavior currently works with our implementation, there is no guarantee
43 // that the tags will be coming in this specific order, so it's dangerous to
44 // rely on this as a test of the tag system working. We would really want to
45 // tags we know we should see and then test that they all occur once, but in no
46 // particular order.
47 #define QA_TAGS_DEBUG 0
48 
make_tag(uint64_t offset,pmt::pmt_t key,pmt::pmt_t value,pmt::pmt_t srcid)49 gr::tag_t make_tag(uint64_t offset, pmt::pmt_t key, pmt::pmt_t value, pmt::pmt_t srcid)
50 {
51     gr::tag_t result;
52     result.offset = offset;
53     result.key = key;
54     result.value = value;
55     result.srcid = srcid;
56     return result;
57 }
58 
operator <<(std::ostream & os,const gr::tag_t & t)59 std::ostream& operator<<(std::ostream& os, const gr::tag_t& t) { return os; }
60 
BOOST_AUTO_TEST_CASE(t0)61 BOOST_AUTO_TEST_CASE(t0)
62 {
63     unsigned int N = 1000;
64     gr::top_block_sptr tb = gr::make_top_block("top");
65     gr::block_sptr src(gr::blocks::null_source::make(sizeof(int)));
66     gr::block_sptr head(gr::blocks::head::make(sizeof(int), N));
67     gr::block_sptr snk(gr::blocks::null_sink::make(sizeof(int)));
68 
69     tb->connect(src, 0, head, 0);
70     tb->connect(head, 0, snk, 0);
71 
72     BOOST_REQUIRE_EQUAL(src->nitems_read(0), (uint64_t)0);
73     BOOST_REQUIRE_EQUAL(src->nitems_written(0), (uint64_t)0);
74 
75     tb->run();
76 
77     BOOST_REQUIRE_THROW(src->nitems_read(0), std::invalid_argument);
78     BOOST_REQUIRE(src->nitems_written(0) >= N);
79     BOOST_REQUIRE_EQUAL(snk->nitems_read(0), (uint64_t)1000);
80     BOOST_REQUIRE_THROW(snk->nitems_written(0), std::invalid_argument);
81 }
82 
83 
BOOST_AUTO_TEST_CASE(t1)84 BOOST_AUTO_TEST_CASE(t1)
85 {
86     int N = 40000;
87     gr::top_block_sptr tb = gr::make_top_block("top");
88     gr::block_sptr src(gr::blocks::null_source::make(sizeof(int)));
89     gr::block_sptr head(gr::blocks::head::make(sizeof(int), N));
90     gr::blocks::annotator_alltoall::sptr ann0(
91         gr::blocks::annotator_alltoall::make(10000, sizeof(int)));
92     gr::blocks::annotator_alltoall::sptr ann1(
93         gr::blocks::annotator_alltoall::make(10000, sizeof(int)));
94     gr::blocks::annotator_alltoall::sptr ann2(
95         gr::blocks::annotator_alltoall::make(10000, sizeof(int)));
96     gr::blocks::annotator_alltoall::sptr ann3(
97         gr::blocks::annotator_alltoall::make(10000, sizeof(int)));
98     gr::blocks::annotator_alltoall::sptr ann4(
99         gr::blocks::annotator_alltoall::make(10000, sizeof(int)));
100     gr::block_sptr snk0(gr::blocks::null_sink::make(sizeof(int)));
101     gr::block_sptr snk1(gr::blocks::null_sink::make(sizeof(int)));
102 
103     tb->connect(src, 0, head, 0);
104     tb->connect(head, 0, ann0, 0);
105 
106     tb->connect(ann0, 0, ann1, 0);
107     tb->connect(ann0, 1, ann2, 0);
108     tb->connect(ann1, 0, ann3, 0);
109     tb->connect(ann2, 0, ann4, 0);
110 
111     tb->connect(ann3, 0, snk0, 0);
112     tb->connect(ann4, 0, snk1, 0);
113 
114     tb->run();
115 
116     std::vector<gr::tag_t> tags0 = ann0->data();
117     std::vector<gr::tag_t> tags3 = ann3->data();
118     std::vector<gr::tag_t> tags4 = ann4->data();
119 
120     // The first annotator does not receive any tags from the null sink upstream
121     BOOST_REQUIRE_EQUAL(tags0.size(), (size_t)0);
122     BOOST_REQUIRE_EQUAL(tags3.size(), (size_t)8);
123     BOOST_REQUIRE_EQUAL(tags4.size(), (size_t)8);
124 
125 #if QA_TAGS_DEBUG
126     // Kludge together the tags that we know should result from the above graph
127     std::stringstream str0, str1, str2;
128     str0 << ann0->name() << ann0->unique_id();
129     str1 << ann1->name() << ann1->unique_id();
130     str2 << ann2->name() << ann2->unique_id();
131 
132     gr::tag_t expected_tags3[8];
133     expected_tags3[0] = make_tag(0, pmt::mp(str1.str()), pmt::mp("seq"), pmt::mp(0));
134     expected_tags3[1] = make_tag(0, pmt::mp(str0.str()), pmt::mp("seq"), pmt::mp(0));
135     expected_tags3[2] = make_tag(10000, pmt::mp(str1.str()), pmt::mp("seq"), pmt::mp(1));
136     expected_tags3[3] = make_tag(10000, pmt::mp(str0.str()), pmt::mp("seq"), pmt::mp(2));
137     expected_tags3[4] = make_tag(20000, pmt::mp(str1.str()), pmt::mp("seq"), pmt::mp(2));
138     expected_tags3[5] = make_tag(20000, pmt::mp(str0.str()), pmt::mp("seq"), pmt::mp(4));
139     expected_tags3[6] = make_tag(30000, pmt::mp(str1.str()), pmt::mp("seq"), pmt::mp(3));
140     expected_tags3[7] = make_tag(30000, pmt::mp(str0.str()), pmt::mp("seq"), pmt::mp(6));
141 
142     gr::tag_t expected_tags4[8];
143     expected_tags4[0] = make_tag(0, pmt::mp(str2.str()), pmt::mp("seq"), pmt::mp(0));
144     expected_tags4[1] = make_tag(0, pmt::mp(str0.str()), pmt::mp("seq"), pmt::mp(1));
145     expected_tags4[2] = make_tag(10000, pmt::mp(str2.str()), pmt::mp("seq"), pmt::mp(1));
146     expected_tags4[3] = make_tag(10000, pmt::mp(str0.str()), pmt::mp("seq"), pmt::mp(3));
147     expected_tags4[4] = make_tag(20000, pmt::mp(str2.str()), pmt::mp("seq"), pmt::mp(2));
148     expected_tags4[5] = make_tag(20000, pmt::mp(str0.str()), pmt::mp("seq"), pmt::mp(5));
149     expected_tags4[6] = make_tag(30000, pmt::mp(str2.str()), pmt::mp("seq"), pmt::mp(3));
150     expected_tags4[7] = make_tag(30000, pmt::mp(str0.str()), pmt::mp("seq"), pmt::mp(7));
151 
152     std::cout << std::endl << "qa_block_tags::t1" << std::endl;
153 
154     // For annotator 3, we know it gets tags from ann0 and ann1, test this
155     for (size_t i = 0; i < tags3.size(); i++) {
156         std::cout << "tags3[" << i << "] = " << tags3[i] << "\t\t" << expected_tags3[i]
157                   << std::endl;
158         BOOST_REQUIRE_EQUAL(tags3[i], expected_tags3[i]);
159     }
160 
161     // For annotator 4, we know it gets tags from ann0 and ann2, test this
162     std::cout << std::endl;
163     for (size_t i = 0; i < tags4.size(); i++) {
164         std::cout << "tags4[" << i << "] = " << tags4[i] << "\t\t" << expected_tags4[i]
165                   << std::endl;
166         BOOST_REQUIRE_EQUAL(tags4[i], expected_tags4[i]);
167     }
168 #endif
169 }
170 
BOOST_AUTO_TEST_CASE(t2)171 BOOST_AUTO_TEST_CASE(t2)
172 {
173     int N = 40000;
174     gr::top_block_sptr tb = gr::make_top_block("top");
175     gr::block_sptr src(gr::blocks::null_source::make(sizeof(int)));
176     gr::block_sptr head(gr::blocks::head::make(sizeof(int), N));
177     gr::blocks::annotator_alltoall::sptr ann0(
178         gr::blocks::annotator_alltoall::make(10000, sizeof(int)));
179     gr::blocks::annotator_alltoall::sptr ann1(
180         gr::blocks::annotator_alltoall::make(10000, sizeof(int)));
181     gr::blocks::annotator_alltoall::sptr ann2(
182         gr::blocks::annotator_alltoall::make(10000, sizeof(int)));
183     gr::blocks::annotator_alltoall::sptr ann3(
184         gr::blocks::annotator_alltoall::make(10000, sizeof(int)));
185     gr::blocks::annotator_alltoall::sptr ann4(
186         gr::blocks::annotator_alltoall::make(10000, sizeof(int)));
187     gr::block_sptr snk0(gr::blocks::null_sink::make(sizeof(int)));
188     gr::block_sptr snk1(gr::blocks::null_sink::make(sizeof(int)));
189     gr::block_sptr snk2(gr::blocks::null_sink::make(sizeof(int)));
190 
191     tb->connect(src, 0, head, 0);
192     tb->connect(head, 0, ann0, 0);
193 
194     tb->connect(ann0, 0, ann1, 0);
195     tb->connect(ann0, 1, ann1, 1);
196     tb->connect(ann1, 0, ann2, 0);
197     tb->connect(ann1, 1, ann3, 0);
198     tb->connect(ann1, 2, ann4, 0);
199 
200     tb->connect(ann2, 0, snk0, 0);
201     tb->connect(ann3, 0, snk1, 0);
202     tb->connect(ann4, 0, snk2, 0);
203 
204     tb->run();
205 
206     std::vector<gr::tag_t> tags0 = ann0->data();
207     std::vector<gr::tag_t> tags1 = ann1->data();
208     std::vector<gr::tag_t> tags2 = ann2->data();
209     std::vector<gr::tag_t> tags3 = ann4->data();
210     std::vector<gr::tag_t> tags4 = ann4->data();
211 
212     // The first annotator does not receive any tags from the null sink upstream
213     BOOST_REQUIRE_EQUAL(tags0.size(), (size_t)0);
214     BOOST_REQUIRE_EQUAL(tags1.size(), (size_t)8);
215 
216     // Make sure the rest all have 12 tags
217     BOOST_REQUIRE_EQUAL(tags2.size(), (size_t)12);
218     BOOST_REQUIRE_EQUAL(tags3.size(), (size_t)12);
219     BOOST_REQUIRE_EQUAL(tags4.size(), (size_t)12);
220 
221 
222 #if QA_TAGS_DEBUG
223     // Kludge together the tags that we know should result from the above graph
224     std::stringstream str0, str1;
225     str0 << ann0->name() << ann0->unique_id();
226     str1 << ann1->name() << ann1->unique_id();
227 
228     gr::tag_t expected_tags2[12];
229     expected_tags2[0] = make_tag(0, pmt::mp(str1.str()), pmt::mp("seq"), pmt::mp(0));
230     expected_tags2[1] = make_tag(0, pmt::mp(str0.str()), pmt::mp("seq"), pmt::mp(0));
231     expected_tags2[2] = make_tag(0, pmt::mp(str0.str()), pmt::mp("seq"), pmt::mp(1));
232     expected_tags2[3] = make_tag(10000, pmt::mp(str1.str()), pmt::mp("seq"), pmt::mp(3));
233     expected_tags2[4] = make_tag(10000, pmt::mp(str0.str()), pmt::mp("seq"), pmt::mp(2));
234     expected_tags2[5] = make_tag(10000, pmt::mp(str0.str()), pmt::mp("seq"), pmt::mp(3));
235     expected_tags2[6] = make_tag(20000, pmt::mp(str1.str()), pmt::mp("seq"), pmt::mp(6));
236     expected_tags2[7] = make_tag(20000, pmt::mp(str0.str()), pmt::mp("seq"), pmt::mp(4));
237     expected_tags2[8] = make_tag(20000, pmt::mp(str0.str()), pmt::mp("seq"), pmt::mp(5));
238     expected_tags2[9] = make_tag(30000, pmt::mp(str1.str()), pmt::mp("seq"), pmt::mp(9));
239     expected_tags2[10] = make_tag(30000, pmt::mp(str0.str()), pmt::mp("seq"), pmt::mp(6));
240     expected_tags2[11] = make_tag(30000, pmt::mp(str0.str()), pmt::mp("seq"), pmt::mp(7));
241 
242     gr::tag_t expected_tags4[12];
243     expected_tags4[0] = make_tag(0, pmt::mp(str1.str()), pmt::mp("seq"), pmt::mp(2));
244     expected_tags4[1] = make_tag(0, pmt::mp(str0.str()), pmt::mp("seq"), pmt::mp(0));
245     expected_tags4[2] = make_tag(0, pmt::mp(str0.str()), pmt::mp("seq"), pmt::mp(1));
246     expected_tags4[3] = make_tag(10000, pmt::mp(str1.str()), pmt::mp("seq"), pmt::mp(5));
247     expected_tags4[4] = make_tag(10000, pmt::mp(str0.str()), pmt::mp("seq"), pmt::mp(2));
248     expected_tags4[5] = make_tag(10000, pmt::mp(str0.str()), pmt::mp("seq"), pmt::mp(3));
249     expected_tags4[6] = make_tag(20000, pmt::mp(str1.str()), pmt::mp("seq"), pmt::mp(8));
250     expected_tags4[7] = make_tag(20000, pmt::mp(str0.str()), pmt::mp("seq"), pmt::mp(4));
251     expected_tags4[8] = make_tag(20000, pmt::mp(str0.str()), pmt::mp("seq"), pmt::mp(5));
252     expected_tags4[9] = make_tag(30000, pmt::mp(str1.str()), pmt::mp("seq"), pmt::mp(11));
253     expected_tags4[10] = make_tag(30000, pmt::mp(str0.str()), pmt::mp("seq"), pmt::mp(6));
254     expected_tags4[11] = make_tag(30000, pmt::mp(str0.str()), pmt::mp("seq"), pmt::mp(7));
255 
256     std::cout << std::endl << "qa_block_tags::t2" << std::endl;
257 
258     // For annotator[2-4], we know it gets tags from ann0 and ann1
259     // but the tags from the different outputs of ann1 are different for each.
260     // Just testing ann2 and ann4; if they are correct it would be
261     // inconceivable for ann3 to have it wrong.
262     for (size_t i = 0; i < tags2.size(); i++) {
263         std::cout << "tags2[" << i << "] = " << tags2[i] << "\t\t" << expected_tags2[i]
264                   << std::endl;
265         BOOST_REQUIRE_EQUAL(tags2[i], expected_tags2[i]);
266     }
267 
268     std::cout << std::endl;
269     for (size_t i = 0; i < tags4.size(); i++) {
270         std::cout << "tags2[" << i << "] = " << tags4[i] << "\t\t" << expected_tags4[i]
271                   << std::endl;
272         BOOST_REQUIRE_EQUAL(tags4[i], expected_tags4[i]);
273     }
274 #endif
275 }
276 
277 
BOOST_AUTO_TEST_CASE(t3)278 BOOST_AUTO_TEST_CASE(t3)
279 {
280     int N = 40000;
281     gr::top_block_sptr tb = gr::make_top_block("top");
282     gr::block_sptr src(gr::blocks::null_source::make(sizeof(int)));
283     gr::block_sptr head(gr::blocks::head::make(sizeof(int), N));
284     gr::blocks::annotator_1to1::sptr ann0(
285         gr::blocks::annotator_1to1::make(10000, sizeof(int)));
286     gr::blocks::annotator_alltoall::sptr ann1(
287         gr::blocks::annotator_alltoall::make(10000, sizeof(int)));
288     gr::blocks::annotator_alltoall::sptr ann2(
289         gr::blocks::annotator_alltoall::make(10000, sizeof(int)));
290     gr::blocks::annotator_1to1::sptr ann3(
291         gr::blocks::annotator_1to1::make(10000, sizeof(int)));
292     gr::blocks::annotator_1to1::sptr ann4(
293         gr::blocks::annotator_1to1::make(10000, sizeof(int)));
294     gr::block_sptr snk0(gr::blocks::null_sink::make(sizeof(int)));
295     gr::block_sptr snk1(gr::blocks::null_sink::make(sizeof(int)));
296 
297     tb->connect(src, 0, head, 0);
298     tb->connect(head, 0, ann0, 0);
299     tb->connect(head, 0, ann0, 1);
300 
301     tb->connect(ann0, 0, ann1, 0);
302     tb->connect(ann0, 1, ann2, 0);
303     tb->connect(ann1, 0, ann3, 0);
304     tb->connect(ann2, 0, ann4, 0);
305 
306     tb->connect(ann3, 0, snk0, 0);
307     tb->connect(ann4, 0, snk1, 0);
308 
309     tb->run();
310 
311 
312     std::vector<gr::tag_t> tags0 = ann0->data();
313     std::vector<gr::tag_t> tags3 = ann3->data();
314     std::vector<gr::tag_t> tags4 = ann4->data();
315 
316     // The first annotator does not receive any tags from the null sink upstream
317     BOOST_REQUIRE_EQUAL(tags0.size(), (size_t)0);
318     BOOST_REQUIRE_EQUAL(tags3.size(), (size_t)8);
319     BOOST_REQUIRE_EQUAL(tags4.size(), (size_t)8);
320 
321 #if QA_TAGS_DEBUG
322     // Kludge together the tags that we know should result from the above graph
323     std::stringstream str0, str1, str2;
324     str0 << ann0->name() << ann0->unique_id();
325     str1 << ann1->name() << ann1->unique_id();
326     str2 << ann2->name() << ann2->unique_id();
327 
328     gr::tag_t expected_tags3[8];
329     expected_tags3[0] = make_tag(0, pmt::mp(str1.str()), pmt::mp("seq"), pmt::mp(0));
330     expected_tags3[1] = make_tag(0, pmt::mp(str0.str()), pmt::mp("seq"), pmt::mp(0));
331     expected_tags3[2] = make_tag(10000, pmt::mp(str1.str()), pmt::mp("seq"), pmt::mp(1));
332     expected_tags3[3] = make_tag(10000, pmt::mp(str0.str()), pmt::mp("seq"), pmt::mp(2));
333     expected_tags3[4] = make_tag(20000, pmt::mp(str1.str()), pmt::mp("seq"), pmt::mp(2));
334     expected_tags3[5] = make_tag(20000, pmt::mp(str0.str()), pmt::mp("seq"), pmt::mp(4));
335     expected_tags3[6] = make_tag(30000, pmt::mp(str1.str()), pmt::mp("seq"), pmt::mp(3));
336     expected_tags3[7] = make_tag(30000, pmt::mp(str0.str()), pmt::mp("seq"), pmt::mp(6));
337 
338     gr::tag_t expected_tags4[8];
339     expected_tags4[0] = make_tag(0, pmt::mp(str2.str()), pmt::mp("seq"), pmt::mp(0));
340     expected_tags4[1] = make_tag(0, pmt::mp(str0.str()), pmt::mp("seq"), pmt::mp(1));
341     expected_tags4[2] = make_tag(10000, pmt::mp(str2.str()), pmt::mp("seq"), pmt::mp(1));
342     expected_tags4[3] = make_tag(10000, pmt::mp(str0.str()), pmt::mp("seq"), pmt::mp(3));
343     expected_tags4[4] = make_tag(20000, pmt::mp(str2.str()), pmt::mp("seq"), pmt::mp(2));
344     expected_tags4[5] = make_tag(20000, pmt::mp(str0.str()), pmt::mp("seq"), pmt::mp(5));
345     expected_tags4[6] = make_tag(30000, pmt::mp(str2.str()), pmt::mp("seq"), pmt::mp(3));
346     expected_tags4[7] = make_tag(30000, pmt::mp(str0.str()), pmt::mp("seq"), pmt::mp(7));
347 
348     std::cout << std::endl << "qa_block_tags::t3" << std::endl;
349 
350     // For annotator 3, we know it gets tags from ann0 and ann1, test this
351     for (size_t i = 0; i < tags3.size(); i++) {
352         std::cout << "tags3[" << i << "] = " << tags3[i] << "\t\t" << expected_tags3[i]
353                   << std::endl;
354         BOOST_REQUIRE_EQUAL(tags3[i], expected_tags3[i]);
355     }
356 
357     // For annotator 4, we know it gets tags from ann0 and ann2, test this
358     std::cout << std::endl;
359     for (size_t i = 0; i < tags4.size(); i++) {
360         std::cout << "tags4[" << i << "] = " << tags4[i] << "\t\t" << expected_tags4[i]
361                   << std::endl;
362         BOOST_REQUIRE_EQUAL(tags4[i], expected_tags4[i]);
363     }
364 #endif
365 }
366 
367 
BOOST_AUTO_TEST_CASE(t4)368 BOOST_AUTO_TEST_CASE(t4)
369 {
370     int N = 40000;
371     gr::top_block_sptr tb = gr::make_top_block("top");
372     gr::block_sptr src(gr::blocks::null_source::make(sizeof(int)));
373     gr::block_sptr head(gr::blocks::head::make(sizeof(int), N));
374     gr::blocks::annotator_1to1::sptr ann0(
375         gr::blocks::annotator_1to1::make(10000, sizeof(int)));
376     gr::blocks::annotator_1to1::sptr ann1(
377         gr::blocks::annotator_1to1::make(10000, sizeof(int)));
378     gr::blocks::annotator_1to1::sptr ann2(
379         gr::blocks::annotator_1to1::make(10000, sizeof(int)));
380     gr::block_sptr snk0(gr::blocks::null_sink::make(sizeof(int)));
381     gr::block_sptr snk1(gr::blocks::null_sink::make(sizeof(int)));
382 
383     // using 1-to-1 tag propagation without having equal number of
384     // ins and outs. Make sure this works; will just exit run early.
385     tb->connect(src, 0, head, 0);
386     tb->connect(head, 0, ann0, 0);
387     tb->connect(ann0, 0, ann1, 0);
388     tb->connect(ann0, 1, ann2, 0);
389     tb->connect(ann1, 0, snk0, 0);
390     tb->connect(ann2, 0, snk1, 0);
391 
392     std::cerr << std::endl
393               << "NOTE: This is supposed to produce an error from block_executor"
394               << std::endl;
395     tb->run();
396 }
397 
398 
BOOST_AUTO_TEST_CASE(t5)399 BOOST_AUTO_TEST_CASE(t5)
400 {
401     int N = 40000;
402     gr::top_block_sptr tb = gr::make_top_block("top");
403     gr::block_sptr src(gr::blocks::null_source::make(sizeof(float)));
404     gr::block_sptr head(gr::blocks::head::make(sizeof(float), N));
405     gr::blocks::annotator_alltoall::sptr ann0(
406         gr::blocks::annotator_alltoall::make(10000, sizeof(float)));
407     gr::blocks::annotator_alltoall::sptr ann1(
408         gr::blocks::annotator_alltoall::make(10000, sizeof(float)));
409     gr::blocks::annotator_alltoall::sptr ann2(
410         gr::blocks::annotator_alltoall::make(1000, sizeof(float)));
411     gr::block_sptr snk0(gr::blocks::null_sink::make(sizeof(float)));
412 
413     // Rate change blocks
414     gr::blocks::keep_one_in_n::sptr dec10(
415         gr::blocks::keep_one_in_n::make(sizeof(float), 10));
416 
417     tb->connect(src, 0, head, 0);
418     tb->connect(head, 0, ann0, 0);
419     tb->connect(ann0, 0, ann1, 0);
420     tb->connect(ann1, 0, dec10, 0);
421     tb->connect(dec10, 0, ann2, 0);
422     tb->connect(ann2, 0, snk0, 0);
423 
424     tb->run();
425 
426     std::vector<gr::tag_t> tags0 = ann0->data();
427     std::vector<gr::tag_t> tags1 = ann1->data();
428     std::vector<gr::tag_t> tags2 = ann2->data();
429 
430     // The first annotator does not receive any tags from the null sink upstream
431     BOOST_REQUIRE_EQUAL(tags0.size(), (size_t)0);
432     BOOST_REQUIRE_EQUAL(tags1.size(), (size_t)4);
433     BOOST_REQUIRE_EQUAL(tags2.size(), (size_t)8);
434 
435 
436 #if QA_TAGS_DEBUG
437     // Kludge together the tags that we know should result from the above graph
438     std::stringstream str0, str1, str2;
439     str0 << ann0->name() << ann0->unique_id();
440     str1 << ann1->name() << ann1->unique_id();
441     str2 << ann2->name() << ann2->unique_id();
442 
443     gr::tag_t expected_tags1[5];
444     expected_tags1[0] = make_tag(0, pmt::mp(str0.str()), pmt::mp("seq"), pmt::mp(0));
445     expected_tags1[1] = make_tag(10000, pmt::mp(str0.str()), pmt::mp("seq"), pmt::mp(1));
446     expected_tags1[2] = make_tag(20000, pmt::mp(str0.str()), pmt::mp("seq"), pmt::mp(2));
447     expected_tags1[3] = make_tag(30000, pmt::mp(str0.str()), pmt::mp("seq"), pmt::mp(3));
448 
449     gr::tag_t expected_tags2[10];
450     expected_tags2[0] = make_tag(0, pmt::mp(str1.str()), pmt::mp("seq"), pmt::mp(0));
451     expected_tags2[1] = make_tag(0, pmt::mp(str0.str()), pmt::mp("seq"), pmt::mp(0));
452     expected_tags2[2] = make_tag(1000, pmt::mp(str1.str()), pmt::mp("seq"), pmt::mp(1));
453     expected_tags2[3] = make_tag(1000, pmt::mp(str0.str()), pmt::mp("seq"), pmt::mp(1));
454     expected_tags2[4] = make_tag(2000, pmt::mp(str1.str()), pmt::mp("seq"), pmt::mp(2));
455     expected_tags2[5] = make_tag(2000, pmt::mp(str0.str()), pmt::mp("seq"), pmt::mp(2));
456     expected_tags2[6] = make_tag(3000, pmt::mp(str1.str()), pmt::mp("seq"), pmt::mp(3));
457     expected_tags2[7] = make_tag(3000, pmt::mp(str0.str()), pmt::mp("seq"), pmt::mp(3));
458     expected_tags2[8] = make_tag(4000, pmt::mp(str1.str()), pmt::mp("seq"), pmt::mp(4));
459     expected_tags2[9] = make_tag(4000, pmt::mp(str0.str()), pmt::mp("seq"), pmt::mp(4));
460 
461     std::cout << std::endl << "qa_block_tags::t5" << std::endl;
462 
463     // annotator 1 gets tags from annotator 0
464     std::cout << "tags1.size(): " << tags1.size() << std::endl;
465     for (size_t i = 0; i < tags1.size(); i++) {
466         std::cout << "tags1[" << i << "] = " << tags1[i] << "\t\t" << expected_tags1[i]
467                   << std::endl;
468         BOOST_REQUIRE_EQUAL(tags1[i], expected_tags1[i]);
469     }
470 
471     // annotator 2 gets tags from annotators 0 and 1
472     std::cout << std::endl;
473     std::cout << "tags2.size(): " << tags2.size() << std::endl;
474     for (size_t i = 0; i < tags2.size(); i++) {
475         std::cout << "tags2[" << i << "] = " << tags2[i] << "\t\t" << expected_tags2[i]
476                   << std::endl;
477         BOOST_REQUIRE_EQUAL(tags2[i], expected_tags2[i]);
478     }
479 #endif
480 }
481