getting these octopus tests updated. not quite there yet.
[feisty_meow.git] / octopi / library / tests_octopus / t_unpacker.cpp
diff --git a/octopi/library/tests_octopus/t_unpacker.cpp b/octopi/library/tests_octopus/t_unpacker.cpp
new file mode 100644 (file)
index 0000000..a12412a
--- /dev/null
@@ -0,0 +1,396 @@
+/*****************************************************************************\
+*                                                                             *
+*  Name   : unpacking octopus test                                            *
+*  Author : Chris Koeritz                                                     *
+*                                                                             *
+*  Purpose:                                                                   *
+*                                                                             *
+*    A test of octopuses used for unpacking flat structures.                  *
+*                                                                             *
+*******************************************************************************
+* Copyright (c) 2002-$now By Author.  This program is free software; you can  *
+* redistribute it and/or modify it under the terms of the GNU General Public  *
+* License as published by the Free Software Foundation; either version 2 of   *
+* the License or (at your option) any later version.  This is online at:      *
+*     http://www.fsf.org/copyleft/gpl.html                                    *
+* Please send any updates to: fred@gruntose.com                               *
+\*****************************************************************************/
+
+#include <basis/astring.h>
+#include <structures/static_memory_gremlin.h>
+#include <octopus/entity_defs.h>
+#include <octopus/infoton.h>
+#include <octopus/octopus.h>
+#include <octopus/tentacle_helper.h>
+#include <application/application_shell.h>
+#include <loggers/console_logger.h>
+#include <loggers/file_logger.h>
+#include <structures/static_memory_gremlin.h>
+#include <sockets/internet_address.h>
+
+//hmmm: provide equality ops to be able to check that same stuff
+//      came back out that went in.
+
+class test_unpacker : public application_shell
+{
+public:
+  test_unpacker() : application_shell(class_name()) {}
+  DEFINE_CLASS_NAME("test_unpacker");
+  virtual int execute();
+  void test_unpacking();
+};
+
+//////////////
+
+// the infotons here have a three level classifier.  the outer level is
+// for the benefit of the handler_arm tentacle that just checks that the
+// group name is correct before passing off the request to its internal
+// octopus.  then the second level specifies which class of infotons are
+// being managed.  the third level specifies the leaf type of the infoton--
+// the specific type of data being wrapped.
+
+const char *base_list[] = { "gruntiak" };
+
+SAFE_STATIC_CONST(string_array, base_classifier, (1, base_list))
+
+const char *math_list[] = { "math" };
+
+SAFE_STATIC_CONST(string_array, math_classifier, (base_classifier()
+    + string_array(1, math_list)))
+
+const char *addr_list[] = { "address" };
+
+SAFE_STATIC_CONST(string_array, addr_classifier, (base_classifier()
+    + string_array(1, addr_list)))
+
+class address_ton : public infoton, public network_address
+{
+public:
+  address_ton() : infoton(addr_classifier() + "leaf") {}
+
+  virtual void pack(byte_array &packed_form) const {
+    network_address::pack(packed_form);
+  }
+
+  virtual bool unpack(byte_array &packed_form) {
+    return network_address::unpack(packed_form);
+  }
+
+  virtual int packed_size() const {
+    return 5 * sizeof(int) + 128 /*address estimate*/;
+  }
+
+  virtual clonable *clone() const {
+    return new address_ton(*this);
+  }
+};
+
+//some floating point nums.
+class float_ton : public infoton
+{
+public:
+  float f1;
+  double d1;
+
+  float_ton() : infoton(math_classifier() + "float") {}
+
+  virtual void pack(byte_array &packed_form) const {
+    structures::attach(packed_form, f1);
+    structures::attach(packed_form, d1);
+  }
+
+  virtual int packed_size() const {
+    return sizeof(double) + sizeof(float);
+  }
+
+  virtual bool unpack(byte_array &packed_form) {
+    double hold;
+    if (!structures::detach(packed_form, hold)) return false;
+    f1 = float(hold);
+    if (!structures::detach(packed_form, d1)) return false;
+    return true;
+  }
+
+  virtual clonable *clone() const {
+    return new float_ton(*this);
+  }
+};
+
+//an integer set.
+class int_set_ton : public infoton
+{
+public:
+  int_set nums;
+
+  int_set_ton() : infoton(math_classifier() + "intset") {}
+
+  virtual void pack(byte_array &packed_form) const {
+    structures::attach(packed_form, nums.elements());
+    for (int i = 0; i < nums.elements(); i++)
+      structures::attach(packed_form, nums[i]);
+  }
+
+  virtual int packed_size() const {
+    return sizeof(int) + nums.elements() * sizeof(int);
+  }
+
+  virtual bool unpack(byte_array &packed_form) {
+    int len = 0;
+    nums.reset();
+    if (!structures::detach(packed_form, len)) return false;
+    for (int i = 0; i < len; i++) {
+      int got = 0;
+      if (!structures::detach(packed_form, got)) return false;
+      nums += got;
+    }
+    return true;
+  }
+
+  virtual clonable *clone() const {
+    return new int_set_ton(*this);
+  }
+};
+
+//////////////
+
+// handles network addresses.
+class address_chomper : public tentacle_helper<address_ton>
+{
+public:
+  address_chomper()
+  : tentacle_helper<address_ton>(addr_classifier().subarray(1, 1), true) {}
+};
+
+// handles floats and int_sets.
+class numerical_chomper : public tentacle
+{
+public:
+  numerical_chomper() : tentacle(math_classifier().subarray(1, 1), true) {}
+
+  outcome reconstitute(const string_array &classifier, byte_array &packed_form,
+      infoton * &reformed)
+  {
+    reformed = NIL;
+    if (classifier.length() < 2) return BAD_INPUT;
+    astring key = classifier[1];
+    if (key == "float") {
+      float_ton *to_return = new float_ton;
+      if (!to_return->unpack(packed_form)) {
+        WHACK(to_return);
+        return NIL;
+      }
+      reformed = to_return;
+      return OKAY;
+    } else if (key == "intset") {
+      int_set_ton *to_return = new int_set_ton;
+      if (!to_return->unpack(packed_form)) {
+        WHACK(to_return);
+        return NIL;
+      }
+      reformed = to_return;
+      return OKAY;
+    } else
+      return NO_HANDLER;
+  }
+
+  outcome consume(infoton &formal(to_chow), const octopus_request_id &formal(item_id),
+          byte_array &transformed)
+  { transformed.reset(); return tentacle::BAD_INPUT; }
+
+  virtual void expunge(const octopus_entity &formal(zapola)) {}
+};
+
+//////////////
+
+// delegates the unpacking to an internal tentacle.  it peels off a level
+// of classifier to find the real handler.
+class outer_arm : public tentacle
+{
+public:
+  outer_arm()
+  : tentacle(base_classifier(), true),
+    _unpackers("local", 10 * MEGABYTE),
+    _numer(new numerical_chomper),
+    _addron(new address_chomper)
+  {
+    // register the two tentacles.
+    outcome ret = _unpackers.add_tentacle(_numer);
+    if (ret != tentacle::OKAY)
+      deadly_error(class_name(), "adding numerical tentacle",
+          astring("failed to add: ") + tentacle::outcome_name(ret));
+    ret = _unpackers.add_tentacle(_addron);
+    if (ret != tentacle::OKAY)
+      deadly_error(class_name(), "adding address tentacle",
+          astring("failed to add: ") + tentacle::outcome_name(ret));
+  }
+
+  ~outer_arm() {
+    // just reset the two tentacles, since the _unpackers octopus should
+    // clean them up.
+    _numer = NIL;
+    _addron = NIL;
+  }
+
+  outcome reconstitute(const string_array &classifier, byte_array &packed_form,
+      infoton * &reformed)
+  {
+    // strip first word of classifier.
+    string_array real_class = classifier;
+    real_class.zap(0, 0);
+    // route to octopus.
+    return _unpackers.restore(real_class, packed_form, reformed);
+  }
+
+  outcome consume(infoton &to_chow, const octopus_request_id &item_id,
+          byte_array &transformed)
+  {
+    transformed.reset();
+    // strip first word of classifier.
+    string_array real_class = to_chow.classifier();
+    real_class.zap(0, 0);
+    to_chow.set_classifier(real_class);
+    // route to octopus.
+    return _unpackers.evaluate((infoton *)to_chow.clone(), item_id);
+  }
+
+  void expunge(const octopus_entity &formal(whackola)) {}
+
+private:
+  octopus _unpackers;
+  numerical_chomper *_numer;
+  address_chomper *_addron;
+};
+
+//////////////
+
+void test_unpacker::test_unpacking()
+{
+  octopus unpacky("local", 10 * MEGABYTE);
+  outer_arm *outer = new outer_arm;
+  outcome ret = unpacky.add_tentacle(outer);
+  if (ret != tentacle::OKAY)
+    deadly_error(class_name(), "adding outer tentacle",
+        astring("failed to add: ") + tentacle::outcome_name(ret));
+
+  // test infoton fast packing.
+  int_set_ton jubjub;
+  jubjub.nums.add(299);
+  jubjub.nums.add(39274);
+  jubjub.nums.add(25182);
+  byte_array packed(10388);  // have data in there to start.
+  infoton::fast_pack(packed, jubjub);
+  if (jubjub.packed_size() + infoton::fast_pack_overhead(jubjub.classifier())
+      != packed.length() - 10388)
+    deadly_error(class_name(), "packing test",
+        astring("erroneous size calculated for first fast_pack"));
+  string_array shirley_class;
+  byte_array shirley_data;
+  packed.zap(0, 10387);  // remove the original data.
+
+  // testing the overhead calculation.
+  byte_array junk_jub;
+  jubjub.pack(junk_jub);
+  if (packed.length() != junk_jub.length()
+      + infoton::fast_pack_overhead(jubjub.classifier()))
+    deadly_error(class_name(), "test fast pack overhead",
+        "sizes differed from calculated");
+
+  if (!infoton::fast_unpack(packed, shirley_class, shirley_data))
+    deadly_error(class_name(), "test infoton fast pack",
+        "failed shirley unpack");
+  if (packed.length() != 0)
+    deadly_error(class_name(), "test infoton fast pack",
+        "shirley didn't consume all");
+  if (shirley_class != jubjub.classifier())
+    deadly_error(class_name(), "test infoton fast pack",
+        "inequal orig classifier");
+  int_set_ton scroop;
+  if (!scroop.unpack(shirley_data))
+    deadly_error(class_name(), "test infoton fast pack",
+        "failed scroop unpack");
+  if (shirley_data.length())
+    deadly_error(class_name(), "test infoton fast pack",
+        "scroop didn't consume all");
+  if (scroop.nums.length() != 3)
+    deadly_error(class_name(), "test infoton fast pack",
+        "wrong length in scroop");
+  if ( (scroop.nums[0] != jubjub.nums[0]) || (scroop.nums[1] != jubjub.nums[1])
+      || (scroop.nums[2] != jubjub.nums[2]) )
+    deadly_error(class_name(), "test infoton fast pack",
+        "erroneous information");
+
+  byte_array fasting;
+  infoton::fast_pack(fasting, jubjub);
+  if (jubjub.packed_size() + infoton::fast_pack_overhead(jubjub.classifier())
+      != fasting.length())
+    deadly_error(class_name(), "packing test",
+        astring("erroneous size calculated for second fast_pack"));
+
+  // another test of the overhead calculator.
+  byte_array junk_fast;
+  jubjub.pack(junk_fast);
+  if (fasting.length() != junk_fast.length()
+      + infoton::fast_pack_overhead(jubjub.classifier()))
+    deadly_error(class_name(), "test fast pack overhead 2",
+        "sizes differed from calculated");
+
+  string_array nudge_class;
+  byte_array nudge_data;
+  if (!infoton::fast_unpack(fasting, nudge_class, nudge_data))
+    deadly_error(class_name(), "test infoton fast pack", "fast pack failed to unpack");
+  if (fasting.length())
+    deadly_error(class_name(), "test infoton fast pack", "fast pack didn't consume all");
+  int_set_ton croup;
+  if (!croup.unpack(nudge_data))
+    deadly_error(class_name(), "test infoton fast pack", "croup wouldn't unpack");
+  if ( (croup.nums[0] != jubjub.nums[0]) || (croup.nums[1] != jubjub.nums[1])
+      || (croup.nums[2] != jubjub.nums[2]) )
+    deadly_error(class_name(), "test infoton fast pack", "croup has errors");
+  byte_array chunkmo;
+  chunkmo += 0x23;
+  chunkmo += 0xf8;
+  chunkmo += 0x37;
+  chunkmo += 0x65;
+  address_ton norf;
+  (network_address &)norf = network_address(internet_address
+      (chunkmo, "urp", 23841));
+  chunkmo.reset();
+  infoton::fast_pack(chunkmo, norf);
+  string_array clarfiator;
+  byte_array pacula;
+  if (!infoton::fast_unpack(chunkmo, clarfiator, pacula))
+    deadly_error(class_name(), "test fast_unpack", "chunkmo has errors");
+  infoton *scrung = NIL;
+//log(astring("classif is ") + clarfiator.text_form());
+
+  outcome scrung_ret = unpacky.restore(clarfiator, pacula, scrung);
+  if (scrung_ret != tentacle::OKAY)
+    deadly_error(class_name(), "test fast_unpack",
+        a_sprintf("can't restore scrung: %s",
+            tentacle::outcome_name(scrung_ret)));
+  address_ton *rescrung = dynamic_cast<address_ton *>(scrung);
+  if (!rescrung)
+    deadly_error(class_name(), "test fast_unpack", "wrong dynamic type for scrung");
+  address_ton &prescrung = *rescrung;
+  if ((network_address &)prescrung != (network_address &)norf)
+    deadly_error(class_name(), "test fast_unpack", "wrong network address restored");
+  WHACK(scrung);
+}
+
+const int MAXIMUM_TESTS = 10;
+  // was added to check for memory leaks.
+
+int test_unpacker::execute()
+{
+  int iters = 0;
+  while (iters++ < MAXIMUM_TESTS) {
+//log(a_sprintf("iter #%d", iters));
+    test_unpacking();
+  }
+  log("unpacking octopus:: works for all functions tested.");
+//time_control::sleep_ms(30000);
+  return 0;
+}
+
+HOOPLE_MAIN(test_unpacker, )
+