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>
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
34using namespace application;
35using namespace basis;
36using namespace configuration;
37using namespace loggers;
38using namespace mathematics;
39using namespace octopi;
40using namespace sockets;
41using namespace structures;
42using namespace textual;
43using 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
50class test_unpacker : virtual public unit_base, virtual public application_shell
51{
52public:
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
68const char *base_list[] = { "gruntiak" };
69
70SAFE_STATIC_CONST(string_array, base_classifier, (1, base_list))
71
72const char *math_list[] = { "math" };
73
74SAFE_STATIC_CONST(string_array, math_classifier, (base_classifier()
76
77const char *addr_list[] = { "address" };
78
79SAFE_STATIC_CONST(string_array, addr_classifier, (base_classifier()
80 + string_array(1, addr_list)))
81
82class address_ton : public infoton, public internet_address
83{
84public:
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 {
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.
111class float_ton : public infoton
112{
113public:
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.
148class int_set_ton : public infoton
149{
150public:
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.
198class address_chomper : public tentacle_helper<address_ton>
199{
200public:
201 address_chomper()
202 : tentacle_helper<address_ton>(addr_classifier().subarray(1, 1), true) {}
203};
204
205// handles floats and int_sets.
206class numerical_chomper : public tentacle
207{
208public:
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.
248class outer_arm : public tentacle
249{
250public:
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
299private:
300 octopus _unpackers;
301 numerical_chomper *_numer;
302 address_chomper *_addron;
303};
304
306
307void 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
421const int MAXIMUM_TESTS = 10;
422 // was added to check for memory leaks.
423
424int 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
437HOOPLE_MAIN(test_unpacker, )
438
The application_shell is a base object for console programs.
virtual int execute()=0
< retrieves the command line from the /proc hierarchy on linux.
application_shell()
constructs an application_shell to serve as the root of the program.
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
virtual const char * class_name() const =0
Returns the bare name of this class as a constant character pointer.
An infoton is an individual request parcel with accompanying information.
Definition infoton.h:32
virtual basis::astring text_form() const
local version just makes text_form() more functional.
Definition infoton.h:108
virtual int packed_size() const =0
reports how large the infoton will be when packed.
static void fast_pack(basis::byte_array &packed_form, const infoton &to_pack)
flattens an infoton "to_pack" into the byte array "packed_form".
Definition infoton.cpp:162
virtual bool unpack(basis::byte_array &packed_form)=0
restores an infoton from a packed form.
static bool fast_unpack(basis::byte_array &packed_form, structures::string_array &classifier, basis::byte_array &info)
undoes a previous fast_pack to restore the previous information.
Definition infoton.cpp:227
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
static int fast_pack_overhead(const structures::string_array &classifier)
reports how much space is needed to pack the "classifier".
Definition infoton.cpp:155
virtual clonable * clone() const =0
must be provided to allow creation of a copy of this object.
virtual void pack(basis::byte_array &packed_form) const =0
stuffs the data in the infoton into the "packed_form".
Provides a way of identifying users of an octopus object.
Definition entity_defs.h:35
Identifies requests made on an octopus by users.
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
virtual basis::outcome consume(infoton &to_chow, const octopus_request_id &item_id, basis::byte_array &transformed)=0
this is the main function that processes infotons for this tentacle.
virtual basis::outcome reconstitute(const structures::string_array &classifier, basis::byte_array &packed_form, infoton *&reformed)=0
regenerates an infoton from its packed form.
virtual void expunge(const octopus_entity &to_remove)=0
called to remove traces of the entity "to_remove".
@ NO_HANDLER
no handler for that type of infoton.
Definition tentacle.h:73
static const char * outcome_name(const basis::outcome &to_name)
returns the textual form of the outcome "to_name".
Definition tentacle.cpp:98
this type of address describes a destination out on the internet.
basis::astring text_form() const
void pack(basis::byte_array &packed_form) const
Creates a packed form of the packable object in "packed_form".
bool unpack(basis::byte_array &packed_form)
Restores the packable from the "packed_form".
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.
#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.
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.
A dynamic container class that holds any kind of object via pointers.
Definition amorph.h:55
void attach(byte_array &packed_form, const byte_array &to_attach)
Packs a byte_array "to_attach" into "packed_form".
bool detach(byte_array &packed_form, byte_array &to_detach)
Unpacks a byte_array "to_detach" from "packed_form".
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)
const int MAXIMUM_TESTS
const char * base_list[]