Merge branch 'master' of feistymeow.org:feisty_meow
[feisty_meow.git] / octopi / library / tests_octopus / t_unpacker.cpp
1 /*****************************************************************************\
2 *                                                                             *
3 *  Name   : unpacking octopus test                                            *
4 *  Author : Chris Koeritz                                                     *
5 *                                                                             *
6 *  Purpose:                                                                   *
7 *                                                                             *
8 *    A test of octopuses used for unpacking flat structures.                  *
9 *                                                                             *
10 *******************************************************************************
11 * Copyright (c) 2002-$now By Author.  This program is free software; you can  *
12 * redistribute it and/or modify it under the terms of the GNU General Public  *
13 * License as published by the Free Software Foundation; either version 2 of   *
14 * the License or (at your option) any later version.  This is online at:      *
15 *     http://www.fsf.org/copyleft/gpl.html                                    *
16 * Please send any updates to: fred@gruntose.com                               *
17 \*****************************************************************************/
18
19 #include <basis/astring.h>
20 #include <structures/static_memory_gremlin.h>
21 #include <octopus/entity_defs.h>
22 #include <octopus/infoton.h>
23 #include <octopus/octopus.h>
24 #include <octopus/tentacle_helper.h>
25 #include <application/application_shell.h>
26 #include <loggers/console_logger.h>
27 #include <loggers/file_logger.h>
28 #include <structures/static_memory_gremlin.h>
29 #include <sockets/internet_address.h>
30
31 //hmmm: provide equality ops to be able to check that same stuff
32 //      came back out that went in.
33
34 class test_unpacker : public application_shell
35 {
36 public:
37   test_unpacker() : application_shell(class_name()) {}
38   DEFINE_CLASS_NAME("test_unpacker");
39   virtual int execute();
40   void test_unpacking();
41 };
42
43 //////////////
44
45 // the infotons here have a three level classifier.  the outer level is
46 // for the benefit of the handler_arm tentacle that just checks that the
47 // group name is correct before passing off the request to its internal
48 // octopus.  then the second level specifies which class of infotons are
49 // being managed.  the third level specifies the leaf type of the infoton--
50 // the specific type of data being wrapped.
51
52 const char *base_list[] = { "gruntiak" };
53
54 SAFE_STATIC_CONST(string_array, base_classifier, (1, base_list))
55
56 const char *math_list[] = { "math" };
57
58 SAFE_STATIC_CONST(string_array, math_classifier, (base_classifier()
59     + string_array(1, math_list)))
60
61 const char *addr_list[] = { "address" };
62
63 SAFE_STATIC_CONST(string_array, addr_classifier, (base_classifier()
64     + string_array(1, addr_list)))
65
66 class address_ton : public infoton, public network_address
67 {
68 public:
69   address_ton() : infoton(addr_classifier() + "leaf") {}
70
71   virtual void pack(byte_array &packed_form) const {
72     network_address::pack(packed_form);
73   }
74
75   virtual bool unpack(byte_array &packed_form) {
76     return network_address::unpack(packed_form);
77   }
78
79   virtual int packed_size() const {
80     return 5 * sizeof(int) + 128 /*address estimate*/;
81   }
82
83   virtual clonable *clone() const {
84     return new address_ton(*this);
85   }
86 };
87
88 //some floating point nums.
89 class float_ton : public infoton
90 {
91 public:
92   float f1;
93   double d1;
94
95   float_ton() : infoton(math_classifier() + "float") {}
96
97   virtual void pack(byte_array &packed_form) const {
98     structures::attach(packed_form, f1);
99     structures::attach(packed_form, d1);
100   }
101
102   virtual int packed_size() const {
103     return sizeof(double) + sizeof(float);
104   }
105
106   virtual bool unpack(byte_array &packed_form) {
107     double hold;
108     if (!structures::detach(packed_form, hold)) return false;
109     f1 = float(hold);
110     if (!structures::detach(packed_form, d1)) return false;
111     return true;
112   }
113
114   virtual clonable *clone() const {
115     return new float_ton(*this);
116   }
117 };
118
119 //an integer set.
120 class int_set_ton : public infoton
121 {
122 public:
123   int_set nums;
124
125   int_set_ton() : infoton(math_classifier() + "intset") {}
126
127   virtual void pack(byte_array &packed_form) const {
128     structures::attach(packed_form, nums.elements());
129     for (int i = 0; i < nums.elements(); i++)
130       structures::attach(packed_form, nums[i]);
131   }
132
133   virtual int packed_size() const {
134     return sizeof(int) + nums.elements() * sizeof(int);
135   }
136
137   virtual bool unpack(byte_array &packed_form) {
138     int len = 0;
139     nums.reset();
140     if (!structures::detach(packed_form, len)) return false;
141     for (int i = 0; i < len; i++) {
142       int got = 0;
143       if (!structures::detach(packed_form, got)) return false;
144       nums += got;
145     }
146     return true;
147   }
148
149   virtual clonable *clone() const {
150     return new int_set_ton(*this);
151   }
152 };
153
154 //////////////
155
156 // handles network addresses.
157 class address_chomper : public tentacle_helper<address_ton>
158 {
159 public:
160   address_chomper()
161   : tentacle_helper<address_ton>(addr_classifier().subarray(1, 1), true) {}
162 };
163
164 // handles floats and int_sets.
165 class numerical_chomper : public tentacle
166 {
167 public:
168   numerical_chomper() : tentacle(math_classifier().subarray(1, 1), true) {}
169
170   outcome reconstitute(const string_array &classifier, byte_array &packed_form,
171       infoton * &reformed)
172   {
173     reformed = NULL_POINTER;
174     if (classifier.length() < 2) return BAD_INPUT;
175     astring key = classifier[1];
176     if (key == "float") {
177       float_ton *to_return = new float_ton;
178       if (!to_return->unpack(packed_form)) {
179         WHACK(to_return);
180         return NULL_POINTER;
181       }
182       reformed = to_return;
183       return OKAY;
184     } else if (key == "intset") {
185       int_set_ton *to_return = new int_set_ton;
186       if (!to_return->unpack(packed_form)) {
187         WHACK(to_return);
188         return NULL_POINTER;
189       }
190       reformed = to_return;
191       return OKAY;
192     } else
193       return NO_HANDLER;
194   }
195
196   outcome consume(infoton &formal(to_chow), const octopus_request_id &formal(item_id),
197           byte_array &transformed)
198   { transformed.reset(); return tentacle::BAD_INPUT; }
199
200   virtual void expunge(const octopus_entity &formal(zapola)) {}
201 };
202
203 //////////////
204
205 // delegates the unpacking to an internal tentacle.  it peels off a level
206 // of classifier to find the real handler.
207 class outer_arm : public tentacle
208 {
209 public:
210   outer_arm()
211   : tentacle(base_classifier(), true),
212     _unpackers("local", 10 * MEGABYTE),
213     _numer(new numerical_chomper),
214     _addron(new address_chomper)
215   {
216     // register the two tentacles.
217     outcome ret = _unpackers.add_tentacle(_numer);
218     if (ret != tentacle::OKAY)
219       deadly_error(class_name(), "adding numerical tentacle",
220           astring("failed to add: ") + tentacle::outcome_name(ret));
221     ret = _unpackers.add_tentacle(_addron);
222     if (ret != tentacle::OKAY)
223       deadly_error(class_name(), "adding address tentacle",
224           astring("failed to add: ") + tentacle::outcome_name(ret));
225   }
226
227   ~outer_arm() {
228     // just reset the two tentacles, since the _unpackers octopus should
229     // clean them up.
230     _numer = NULL_POINTER;
231     _addron = NULL_POINTER;
232   }
233
234   outcome reconstitute(const string_array &classifier, byte_array &packed_form,
235       infoton * &reformed)
236   {
237     // strip first word of classifier.
238     string_array real_class = classifier;
239     real_class.zap(0, 0);
240     // route to octopus.
241     return _unpackers.restore(real_class, packed_form, reformed);
242   }
243
244   outcome consume(infoton &to_chow, const octopus_request_id &item_id,
245           byte_array &transformed)
246   {
247     transformed.reset();
248     // strip first word of classifier.
249     string_array real_class = to_chow.classifier();
250     real_class.zap(0, 0);
251     to_chow.set_classifier(real_class);
252     // route to octopus.
253     return _unpackers.evaluate((infoton *)to_chow.clone(), item_id);
254   }
255
256   void expunge(const octopus_entity &formal(whackola)) {}
257
258 private:
259   octopus _unpackers;
260   numerical_chomper *_numer;
261   address_chomper *_addron;
262 };
263
264 //////////////
265
266 void test_unpacker::test_unpacking()
267 {
268   octopus unpacky("local", 10 * MEGABYTE);
269   outer_arm *outer = new outer_arm;
270   outcome ret = unpacky.add_tentacle(outer);
271   if (ret != tentacle::OKAY)
272     deadly_error(class_name(), "adding outer tentacle",
273         astring("failed to add: ") + tentacle::outcome_name(ret));
274
275   // test infoton fast packing.
276   int_set_ton jubjub;
277   jubjub.nums.add(299);
278   jubjub.nums.add(39274);
279   jubjub.nums.add(25182);
280   byte_array packed(10388);  // have data in there to start.
281   infoton::fast_pack(packed, jubjub);
282   if (jubjub.packed_size() + infoton::fast_pack_overhead(jubjub.classifier())
283       != packed.length() - 10388)
284     deadly_error(class_name(), "packing test",
285         astring("erroneous size calculated for first fast_pack"));
286   string_array shirley_class;
287   byte_array shirley_data;
288   packed.zap(0, 10387);  // remove the original data.
289
290   // testing the overhead calculation.
291   byte_array junk_jub;
292   jubjub.pack(junk_jub);
293   if (packed.length() != junk_jub.length()
294       + infoton::fast_pack_overhead(jubjub.classifier()))
295     deadly_error(class_name(), "test fast pack overhead",
296         "sizes differed from calculated");
297
298   if (!infoton::fast_unpack(packed, shirley_class, shirley_data))
299     deadly_error(class_name(), "test infoton fast pack",
300         "failed shirley unpack");
301   if (packed.length() != 0)
302     deadly_error(class_name(), "test infoton fast pack",
303         "shirley didn't consume all");
304   if (shirley_class != jubjub.classifier())
305     deadly_error(class_name(), "test infoton fast pack",
306         "inequal orig classifier");
307   int_set_ton scroop;
308   if (!scroop.unpack(shirley_data))
309     deadly_error(class_name(), "test infoton fast pack",
310         "failed scroop unpack");
311   if (shirley_data.length())
312     deadly_error(class_name(), "test infoton fast pack",
313         "scroop didn't consume all");
314   if (scroop.nums.length() != 3)
315     deadly_error(class_name(), "test infoton fast pack",
316         "wrong length in scroop");
317   if ( (scroop.nums[0] != jubjub.nums[0]) || (scroop.nums[1] != jubjub.nums[1])
318       || (scroop.nums[2] != jubjub.nums[2]) )
319     deadly_error(class_name(), "test infoton fast pack",
320         "erroneous information");
321
322   byte_array fasting;
323   infoton::fast_pack(fasting, jubjub);
324   if (jubjub.packed_size() + infoton::fast_pack_overhead(jubjub.classifier())
325       != fasting.length())
326     deadly_error(class_name(), "packing test",
327         astring("erroneous size calculated for second fast_pack"));
328
329   // another test of the overhead calculator.
330   byte_array junk_fast;
331   jubjub.pack(junk_fast);
332   if (fasting.length() != junk_fast.length()
333       + infoton::fast_pack_overhead(jubjub.classifier()))
334     deadly_error(class_name(), "test fast pack overhead 2",
335         "sizes differed from calculated");
336
337   string_array nudge_class;
338   byte_array nudge_data;
339   if (!infoton::fast_unpack(fasting, nudge_class, nudge_data))
340     deadly_error(class_name(), "test infoton fast pack", "fast pack failed to unpack");
341   if (fasting.length())
342     deadly_error(class_name(), "test infoton fast pack", "fast pack didn't consume all");
343   int_set_ton croup;
344   if (!croup.unpack(nudge_data))
345     deadly_error(class_name(), "test infoton fast pack", "croup wouldn't unpack");
346   if ( (croup.nums[0] != jubjub.nums[0]) || (croup.nums[1] != jubjub.nums[1])
347       || (croup.nums[2] != jubjub.nums[2]) )
348     deadly_error(class_name(), "test infoton fast pack", "croup has errors");
349   byte_array chunkmo;
350   chunkmo += 0x23;
351   chunkmo += 0xf8;
352   chunkmo += 0x37;
353   chunkmo += 0x65;
354   address_ton norf;
355   (network_address &)norf = network_address(internet_address
356       (chunkmo, "urp", 23841));
357   chunkmo.reset();
358   infoton::fast_pack(chunkmo, norf);
359   string_array clarfiator;
360   byte_array pacula;
361   if (!infoton::fast_unpack(chunkmo, clarfiator, pacula))
362     deadly_error(class_name(), "test fast_unpack", "chunkmo has errors");
363   infoton *scrung = NULL_POINTER;
364 //log(astring("classif is ") + clarfiator.text_form());
365
366   outcome scrung_ret = unpacky.restore(clarfiator, pacula, scrung);
367   if (scrung_ret != tentacle::OKAY)
368     deadly_error(class_name(), "test fast_unpack",
369         a_sprintf("can't restore scrung: %s",
370             tentacle::outcome_name(scrung_ret)));
371   address_ton *rescrung = dynamic_cast<address_ton *>(scrung);
372   if (!rescrung)
373     deadly_error(class_name(), "test fast_unpack", "wrong dynamic type for scrung");
374   address_ton &prescrung = *rescrung;
375   if ((network_address &)prescrung != (network_address &)norf)
376     deadly_error(class_name(), "test fast_unpack", "wrong network address restored");
377   WHACK(scrung);
378 }
379
380 const int MAXIMUM_TESTS = 10;
381   // was added to check for memory leaks.
382
383 int test_unpacker::execute()
384 {
385   int iters = 0;
386   while (iters++ < MAXIMUM_TESTS) {
387 //log(a_sprintf("iter #%d", iters));
388     test_unpacking();
389   }
390   log("unpacking octopus:: works for all functions tested.");
391 //time_control::sleep_ms(30000);
392   return 0;
393 }
394
395 HOOPLE_MAIN(test_unpacker, )
396