feisty meow concerns codebase  2.140
test_unpacker.cpp
Go to the documentation of this file.
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 
21 #include <basis/astring.h>
22 #include <loggers/console_logger.h>
23 #include <loggers/file_logger.h>
25 #include <octopus/entity_defs.h>
26 #include <octopus/infoton.h>
27 #include <octopus/octopus.h>
32 #include <unit_test/unit_base.h>
33 
34 using namespace application;
35 using namespace basis;
36 using namespace configuration;
37 using namespace loggers;
38 using namespace mathematics;
39 using namespace octopi;
40 using namespace sockets;
41 using namespace structures;
42 using namespace textual;
43 using namespace unit_test;
44 
45 #define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), astring(s))
46 
47 //hmmm: provide equality ops to be able to check that same stuff
48 // came back out that went in.
49 
50 class test_unpacker : virtual public unit_base, virtual public application_shell
51 {
52 public:
53  test_unpacker() : application_shell() {}
54  DEFINE_CLASS_NAME("test_unpacker");
55  virtual int execute();
56  void test_unpacking();
57 };
58 
60 
61 // the infotons here have a three level classifier. the outer level is
62 // for the benefit of the handler_arm tentacle that just checks that the
63 // group name is correct before passing off the request to its internal
64 // octopus. then the second level specifies which class of infotons are
65 // being managed. the third level specifies the leaf type of the infoton--
66 // the specific type of data being wrapped.
67 
68 const char *base_list[] = { "gruntiak" };
69 
70 SAFE_STATIC_CONST(string_array, base_classifier, (1, base_list))
71 
72 const char *math_list[] = { "math" };
73 
74 SAFE_STATIC_CONST(string_array, math_classifier, (base_classifier()
76 
77 const char *addr_list[] = { "address" };
78 
79 SAFE_STATIC_CONST(string_array, addr_classifier, (base_classifier()
80  + string_array(1, addr_list)))
81 
82 class address_ton : public infoton, public internet_address
83 {
84 public:
85  address_ton() : infoton(addr_classifier() + "leaf") {}
86 
87  const char *class_name() const { return "address_ton"; }
88 
89  virtual void pack(byte_array &packed_form) const {
90  internet_address::pack(packed_form);
91  }
92 
93  virtual void text_form(basis::base_string &state_fill) const {
94  state_fill.concatenate_string(internet_address::text_form());
95  }
96 
97  virtual bool unpack(byte_array &packed_form) {
98  return internet_address::unpack(packed_form);
99  }
100 
101  virtual int packed_size() const {
102  return 5 * sizeof(int) + 128 /*address estimate*/;
103  }
104 
105  virtual clonable *clone() const {
106  return new address_ton(*this);
107  }
108 };
109 
110 //some floating point nums.
111 class float_ton : public infoton
112 {
113 public:
114  float f1;
115  double d1;
116 
117  float_ton() : infoton(math_classifier() + "float") {}
118 
119  const char *class_name() const { return "float_ton"; }
120 
121  virtual void pack(byte_array &packed_form) const {
122  structures::attach(packed_form, f1);
123  structures::attach(packed_form, d1);
124  }
125 
126  virtual void text_form(basis::base_string &state_fill) const {
127  state_fill.concatenate_string(a_sprintf("f1=%f d1=%f", f1, d1));
128  }
129 
130  virtual int packed_size() const {
131  return sizeof(double) + sizeof(float);
132  }
133 
134  virtual bool unpack(byte_array &packed_form) {
135  double hold;
136  if (!structures::detach(packed_form, hold)) return false;
137  f1 = float(hold);
138  if (!structures::detach(packed_form, d1)) return false;
139  return true;
140  }
141 
142  virtual clonable *clone() const {
143  return new float_ton(*this);
144  }
145 };
146 
147 //an integer set.
148 class int_set_ton : public infoton
149 {
150 public:
151  int_set nums;
152 
153  int_set_ton() : infoton(math_classifier() + "intset") {}
154 
155  const char *class_name() const { return "int_set_ton"; }
156 
157  virtual void text_form(basis::base_string &state_fill) const {
158  state_fill.concatenate_string(astring("( "));
159  for (int indy = 0; indy < nums.length(); indy++) {
160  state_fill.concatenate_string(a_sprintf("%d", nums[indy]));
161  if (indy < nums.length() - 1) {
162  state_fill.concatenate_string(astring(", "));
163  }
164  }
165  state_fill.concatenate_string(astring(" )"));
166  }
167 
168  virtual void pack(byte_array &packed_form) const {
169  structures::attach(packed_form, nums.elements());
170  for (int i = 0; i < nums.elements(); i++)
171  structures::attach(packed_form, nums[i]);
172  }
173 
174  virtual int packed_size() const {
175  return sizeof(int) + nums.elements() * sizeof(int);
176  }
177 
178  virtual bool unpack(byte_array &packed_form) {
179  int len = 0;
180  nums.reset();
181  if (!structures::detach(packed_form, len)) return false;
182  for (int i = 0; i < len; i++) {
183  int got = 0;
184  if (!structures::detach(packed_form, got)) return false;
185  nums += got;
186  }
187  return true;
188  }
189 
190  virtual clonable *clone() const {
191  return new int_set_ton(*this);
192  }
193 };
194 
196 
197 // handles network addresses.
198 class address_chomper : public tentacle_helper<address_ton>
199 {
200 public:
201  address_chomper()
202  : tentacle_helper<address_ton>(addr_classifier().subarray(1, 1), true) {}
203 };
204 
205 // handles floats and int_sets.
206 class numerical_chomper : public tentacle
207 {
208 public:
209  numerical_chomper() : tentacle(math_classifier().subarray(1, 1), true) {}
210 
211  outcome reconstitute(const string_array &classifier, byte_array &packed_form,
212  infoton * &reformed)
213  {
214  reformed = NULL_POINTER;
215  if (classifier.length() < 2) return BAD_INPUT;
216  astring key = classifier[1];
217  if (key == astring("float")) {
218  float_ton *to_return = new float_ton;
219  if (!to_return->unpack(packed_form)) {
220  WHACK(to_return);
221  return NULL_POINTER;
222  }
223  reformed = to_return;
224  return OKAY;
225  } else if (key == astring("intset")) {
226  int_set_ton *to_return = new int_set_ton;
227  if (!to_return->unpack(packed_form)) {
228  WHACK(to_return);
229  return NULL_POINTER;
230  }
231  reformed = to_return;
232  return OKAY;
233  } else
234  return NO_HANDLER;
235  }
236 
237  outcome consume(infoton &formal(to_chow), const octopus_request_id &formal(item_id),
238  byte_array &transformed)
239  { transformed.reset(); return tentacle::BAD_INPUT; }
240 
241  virtual void expunge(const octopus_entity &formal(zapola)) {}
242 };
243 
245 
246 // delegates the unpacking to an internal tentacle. it peels off a level
247 // of classifier to find the real handler.
248 class outer_arm : public tentacle
249 {
250 public:
251  outer_arm()
252  : tentacle(base_classifier(), true),
253  _unpackers("local", 10 * MEGABYTE),
254  _numer(new numerical_chomper),
255  _addron(new address_chomper)
256  {
257  // register the two tentacles.
258  outcome ret = _unpackers.add_tentacle(_numer);
259  if (ret != tentacle::OKAY)
260  deadly_error(class_name(), "adding numerical tentacle",
261  astring("failed to add: ") + tentacle::outcome_name(ret));
262  ret = _unpackers.add_tentacle(_addron);
263  if (ret != tentacle::OKAY)
264  deadly_error(class_name(), "adding address tentacle",
265  astring("failed to add: ") + tentacle::outcome_name(ret));
266  }
267 
268  ~outer_arm() {
269  // just reset the two tentacles, since the _unpackers octopus should
270  // clean them up.
271  _numer = NULL_POINTER;
272  _addron = NULL_POINTER;
273  }
274 
275  outcome reconstitute(const string_array &classifier, byte_array &packed_form,
276  infoton * &reformed)
277  {
278  // strip first word of classifier.
279  string_array real_class = classifier;
280  real_class.zap(0, 0);
281  // route to octopus.
282  return _unpackers.restore(real_class, packed_form, reformed);
283  }
284 
285  outcome consume(infoton &to_chow, const octopus_request_id &item_id,
286  byte_array &transformed)
287  {
288  transformed.reset();
289  // strip first word of classifier.
290  string_array real_class = to_chow.classifier();
291  real_class.zap(0, 0);
292  to_chow.set_classifier(real_class);
293  // route to octopus.
294  return _unpackers.evaluate(dynamic_cast<infoton *>(to_chow.clone()), item_id);
295  }
296 
297  void expunge(const octopus_entity &formal(whackola)) {}
298 
299 private:
300  octopus _unpackers;
301  numerical_chomper *_numer;
302  address_chomper *_addron;
303 };
304 
306 
307 void test_unpacker::test_unpacking()
308 {
309  octopus unpacky("local", 10 * MEGABYTE);
310  outer_arm *outer = new outer_arm;
311  outcome ret = unpacky.add_tentacle(outer);
312  if (ret != tentacle::OKAY)
313  deadly_error(class_name(), "adding outer tentacle",
314  astring("failed to add: ") + tentacle::outcome_name(ret));
315 
316  // test infoton fast packing.
317  int_set_ton jubjub;
318  jubjub.nums.add(299);
319  jubjub.nums.add(39274);
320  jubjub.nums.add(25182);
321  byte_array packed(10388); // have data in there to start.
322  infoton::fast_pack(packed, jubjub);
323  if (jubjub.packed_size() + infoton::fast_pack_overhead(jubjub.classifier())
324  != packed.length() - 10388)
325  deadly_error(class_name(), "packing test",
326  astring("erroneous size calculated for first fast_pack"));
327  string_array shirley_class;
328  byte_array shirley_data;
329  packed.zap(0, 10387); // remove the original data.
330 
331  // testing the overhead calculation.
332  byte_array junk_jub;
333  jubjub.pack(junk_jub);
334  if (packed.length() != junk_jub.length()
335  + infoton::fast_pack_overhead(jubjub.classifier()))
336  deadly_error(class_name(), "test fast pack overhead",
337  "sizes differed from calculated");
338 
339  if (!infoton::fast_unpack(packed, shirley_class, shirley_data))
340  deadly_error(class_name(), "test infoton fast pack",
341  "failed shirley unpack");
342  if (packed.length() != 0)
343  deadly_error(class_name(), "test infoton fast pack",
344  "shirley didn't consume all");
345  if (shirley_class != jubjub.classifier())
346  deadly_error(class_name(), "test infoton fast pack",
347  "inequal orig classifier");
348  int_set_ton scroop;
349  if (!scroop.unpack(shirley_data))
350  deadly_error(class_name(), "test infoton fast pack",
351  "failed scroop unpack");
352  if (shirley_data.length())
353  deadly_error(class_name(), "test infoton fast pack",
354  "scroop didn't consume all");
355  if (scroop.nums.length() != 3)
356  deadly_error(class_name(), "test infoton fast pack",
357  "wrong length in scroop");
358  if ( (scroop.nums[0] != jubjub.nums[0]) || (scroop.nums[1] != jubjub.nums[1])
359  || (scroop.nums[2] != jubjub.nums[2]) )
360  deadly_error(class_name(), "test infoton fast pack",
361  "erroneous information");
362 
363  byte_array fasting;
364  infoton::fast_pack(fasting, jubjub);
365  if (jubjub.packed_size() + infoton::fast_pack_overhead(jubjub.classifier())
366  != fasting.length())
367  deadly_error(class_name(), "packing test",
368  astring("erroneous size calculated for second fast_pack"));
369 
370  // another test of the overhead calculator.
371  byte_array junk_fast;
372  jubjub.pack(junk_fast);
373  if (fasting.length() != junk_fast.length()
374  + infoton::fast_pack_overhead(jubjub.classifier()))
375  deadly_error(class_name(), "test fast pack overhead 2",
376  "sizes differed from calculated");
377 
378  string_array nudge_class;
379  byte_array nudge_data;
380  if (!infoton::fast_unpack(fasting, nudge_class, nudge_data))
381  deadly_error(class_name(), "test infoton fast pack", "fast pack failed to unpack");
382  if (fasting.length())
383  deadly_error(class_name(), "test infoton fast pack", "fast pack didn't consume all");
384  int_set_ton croup;
385  if (!croup.unpack(nudge_data))
386  deadly_error(class_name(), "test infoton fast pack", "croup wouldn't unpack");
387  if ( (croup.nums[0] != jubjub.nums[0]) || (croup.nums[1] != jubjub.nums[1])
388  || (croup.nums[2] != jubjub.nums[2]) )
389  deadly_error(class_name(), "test infoton fast pack", "croup has errors");
390  byte_array chunkmo;
391  chunkmo += 0x23;
392  chunkmo += 0xf8;
393  chunkmo += 0x37;
394  chunkmo += 0x65;
395  address_ton norf;
397  (chunkmo, "urp", 23841));
398  chunkmo.reset();
399  infoton::fast_pack(chunkmo, norf);
400  string_array clarfiator;
401  byte_array pacula;
402  if (!infoton::fast_unpack(chunkmo, clarfiator, pacula))
403  deadly_error(class_name(), "test fast_unpack", "chunkmo has errors");
404  infoton *scrung = NULL_POINTER;
405 //LOG(astring("classif is ") + clarfiator.text_form());
406 
407  outcome scrung_ret = unpacky.restore(clarfiator, pacula, scrung);
408  if (scrung_ret != tentacle::OKAY)
409  deadly_error(class_name(), "test fast_unpack",
410  a_sprintf("can't restore scrung: %s",
411  tentacle::outcome_name(scrung_ret)));
412  address_ton *rescrung = dynamic_cast<address_ton *>(scrung);
413  if (!rescrung)
414  deadly_error(class_name(), "test fast_unpack", "wrong dynamic type for scrung");
415  address_ton &prescrung = *rescrung;
416  if ((internet_address &)prescrung != (internet_address &)norf)
417  deadly_error(class_name(), "test fast_unpack", "wrong network address restored");
418  WHACK(scrung);
419 }
420 
421 const int MAXIMUM_TESTS = 10;
422  // was added to check for memory leaks.
423 
424 int test_unpacker::execute()
425 {
426  FUNCDEF("execute");
427  int iters = 0;
428  while (iters++ < MAXIMUM_TESTS) {
429 //LOG(a_sprintf("iter #%d", iters));
430  test_unpacking();
431  }
432  LOG(astring(class_name()) + ":: works for all functions tested.");
433 //time_control::sleep_ms(30000);
434  return 0;
435 }
436 
437 HOOPLE_MAIN(test_unpacker, )
438 
The application_shell is a base object for console programs.
a_sprintf is a specialization of astring that provides printf style support.
Definition: astring.h:440
void reset(int number=0, const contents *initial_contents=NULL_POINTER)
Resizes this array and sets the contents from an array of contents.
Definition: array.h:349
int length() const
Returns the current reported length of the allocated C array.
Definition: array.h:115
outcome zap(int start, int end)
Deletes from "this" the objects inclusively between "start" and "end".
Definition: array.h:769
Provides a dynamically resizable ASCII character string.
Definition: astring.h:35
Defines the base class for all string processing objects in hoople.
Definition: base_string.h:28
virtual base_string & concatenate_string(const base_string &s)=0
Modifies "this" by concatenating "s" onto it.
A very common template for a dynamic array of bytes.
Definition: byte_array.h:36
A clonable object knows how to make copy of itself.
Definition: contracts.h:109
Outcomes describe the state of completion for an operation.
Definition: outcome.h:31
An infoton is an individual request parcel with accompanying information.
Definition: infoton.h:32
void set_classifier(const structures::string_array &new_classifier)
sets the infoton's classifier to the "new_classifier".
Definition: infoton.cpp:104
const structures::string_array & classifier() const
this array of strings is the "name" for this infoton.
Definition: infoton.cpp:85
virtual clonable * clone() const =0
must be provided to allow creation of a copy of this object.
Provides a way of identifying users of an octopus object.
Definition: entity_defs.h:35
Identifies requests made on an octopus by users.
Definition: entity_defs.h:114
Octopus is a design pattern for generalized request processing systems.
Definition: octopus.h:47
provides prefab implementations for parts of the tentacle object.
Manages a service within an octopus by processing certain infotons.
Definition: tentacle.h:36
this type of address describes a destination out on the internet.
A simple object that wraps a templated set of ints.
Definition: set.h:156
int elements() const
Returns the number of elements in this set.
Definition: set.h:47
An array of strings with some additional helpful methods.
Definition: string_array.h:32
#define deadly_error(c, f, i)
#define formal(parameter)
This macro just eats what it's passed; it marks unused formal parameters.
Definition: definitions.h:48
#define NULL_POINTER
The value representing a pointer to nothing.
Definition: definitions.h:32
#define DEFINE_CLASS_NAME(objname)
Defines the name of a class by providing a couple standard methods.
Definition: enhance_cpp.h:42
#define FUNCDEF(func_in)
FUNCDEF sets the name of a function (and plugs it into the callstack).
Definition: enhance_cpp.h:54
Provides macros that implement the 'main' program of an application.
#define HOOPLE_MAIN(obj_name, obj_args)
options that should work for most unix and linux apps.
Definition: hoople_main.h:61
Implements an application lock to ensure only one is running at once.
The guards collection helps in testing preconditions and reporting errors.
Definition: array.h:30
void WHACK(contents *&ptr)
deletion with clearing of the pointer.
Definition: functions.h:121
const int MEGABYTE
Number of bytes in a megabyte.
Definition: definitions.h:135
A logger that sends to the console screen using the standard output device.
An extension to floating point primitives providing approximate equality.
Definition: averager.h:21
Provides access to the operating system's socket methods.
Definition: base_address.h:26
A dynamic container class that holds any kind of object via pointers.
Definition: amorph.h:55
bool unpack(basis::byte_array &packed_form, set< contents > &to_unpack)
provides a way to unpack any set that stores packable objects.
Definition: set.h:139
void attach(byte_array &packed_form, const byte_array &to_attach)
Packs a byte_array "to_attach" into "packed_form".
void pack(basis::byte_array &packed_form, const set< contents > &to_pack)
provides a way to pack any set that stores packable objects.
Definition: set.h:131
bool detach(byte_array &packed_form, byte_array &to_detach)
Unpacks a byte_array "to_detach" from "packed_form".
int packed_size(const byte_array &packed_form)
Reports the size required to pack a byte array into a byte array.
Useful support functions for unit testing, especially within hoople.
Definition: unit_base.cpp:35
#define SAFE_STATIC_CONST(type, func_name, parms)
this version returns a constant object instead.
Automates some common tasks for tentacle implementations. This template provides some default impleme...
const char * math_list[]
#define LOG(s)
string_array(1, math_list))) const char *addr_list[]
const int MAXIMUM_TESTS
const char * base_list[]