getting these octopus tests updated. not quite there yet.
authorChris Koeritz <fred@gruntose.com>
Thu, 24 Oct 2013 02:13:25 +0000 (21:13 -0500)
committerChris Koeritz <fred@gruntose.com>
Thu, 24 Oct 2013 02:13:25 +0000 (21:13 -0500)
octopi/library/makefile
octopi/library/tests_octopus/makefile [new file with mode: 0755]
octopi/library/tests_octopus/t_bin.cpp [new file with mode: 0644]
octopi/library/tests_octopus/t_bin_threaded.cpp [new file with mode: 0644]
octopi/library/tests_octopus/t_entity.cpp [new file with mode: 0644]
octopi/library/tests_octopus/t_file_transfer.cpp [new file with mode: 0644]
octopi/library/tests_octopus/t_identity.cpp [new file with mode: 0644]
octopi/library/tests_octopus/t_security.cpp [new file with mode: 0644]
octopi/library/tests_octopus/t_unpacker.cpp [new file with mode: 0644]

index 67b1a3298f1241763b9ddcd573beab1a22a22bd2..29af249d83c2ec3ff56ef875126dd3885ac7b18e 100644 (file)
@@ -6,7 +6,9 @@ BUILD_BEFORE = octopus \
   tentacles \
   cromp \
   synchronic \
-  tests_sockets
+  tests_sockets 
+
+#  tests_octopus
 
 include rules.def
 
diff --git a/octopi/library/tests_octopus/makefile b/octopi/library/tests_octopus/makefile
new file mode 100755 (executable)
index 0000000..6784c3b
--- /dev/null
@@ -0,0 +1,15 @@
+CONSOLE_MODE = t
+
+include cpp/variables.def
+
+PROJECT = tests_octopus
+TYPE = test
+TARGETS = t_bin.exe t_bin_threaded.exe t_entity.exe t_identity.exe \
+  t_security.exe t_unpacker.exe t_file_transfer.exe
+LOCAL_LIBS_USED = tentacles octopus sockets unit_test application configuration loggers \
+  textual timely processes filesystem structures basis 
+VCPP_USE_SOCK = t
+RUN_TARGETS = $(ACTUAL_TARGETS)
+
+include cpp/rules.def
+
diff --git a/octopi/library/tests_octopus/t_bin.cpp b/octopi/library/tests_octopus/t_bin.cpp
new file mode 100644 (file)
index 0000000..6cbadd3
--- /dev/null
@@ -0,0 +1,187 @@
+/*****************************************************************************\
+*                                                                             *
+*  Name   : entity_data_bin tester                                            *
+*  Author : Chris Koeritz                                                     *
+*                                                                             *
+*  Purpose:                                                                   *
+*                                                                             *
+*    Checks that the entity_data_bin class is behaving as expected.           *
+*                                                                             *
+*******************************************************************************
+* 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 <application/hoople_main.h>
+#include <basis/byte_array.h>
+#include <mathematics/chaos.h>
+#include <basis/functions.h>
+#include <basis/guards.h>
+#include <basis/astring.h>
+#include <application/application_shell.h>
+#include <loggers/console_logger.h>
+#include <loggers/program_wide_logger.h>
+#include <structures/static_memory_gremlin.h>
+#include <octopus/entity_data_bin.h>
+#include <octopus/entity_defs.h>
+#include <tentacles/security_infoton.h>
+#include <textual/string_manipulation.h>
+
+#include <stdio.h>
+
+using namespace application;
+using namespace basis;
+using namespace loggers;
+using namespace octopi;
+using namespace textual;
+
+const int ITEM_COUNT = 10000;
+  // the number of times to repeat each test operation.
+
+#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger().get(), astring(s))
+#define BASE_LOG(s) EMERGENCY_LOG(program_wide_logger().get(), astring(s))
+
+class test_bin : public application_shell
+{
+public:
+  test_bin() : application_shell() {}
+  DEFINE_CLASS_NAME("test_bin");
+  int execute();
+};
+
+//////////////
+  
+int test_bin::execute()
+{
+  FUNCDEF("execute");
+  char c = '\0';
+
+  array<octopus_request_id> item_list;
+
+  entity_data_bin *bing = new entity_data_bin(10 * MEGABYTE);
+
+  enum test_types { ANY = 1, ENT, ID };
+
+  for (int q = ANY; q <= ID; q++) {
+LOG(a_sprintf("test type %d beginning...%c", q, c));
+  // using c just shuts up warnings.
+//LOG("note memory usage and hit a key:");
+//c = getchar();
+
+    program_wide_logger().get().eol(parser_bits::NO_ENDING);
+    for (int i = 1; i <= ITEM_COUNT; i++) {
+      // test the basic filling of the values in an entity.
+      octopus_request_id req_id;
+      int sequencer = randomizer().inclusive(1, MAXINT32 - 10);
+      int add_in = randomizer().inclusive(0, MAXINT32 - 10);
+      int process_id = randomizer().inclusive(0, MAXINT32 - 10);
+      req_id._entity = octopus_entity(string_manipulation::make_random_name(),
+          process_id, sequencer, add_in);
+      req_id._request_num = randomizer().inclusive(1, MAXINT32 - 10);
+      infoton *torp = new security_infoton;
+      bing->add_item(torp, req_id);
+      item_list += req_id;
+
+      if (! (i % 50) ) {
+        printf("^");
+        fflush(NIL);
+      }
+    }
+    program_wide_logger().get().eol(parser_bits::CRLF_AT_END);
+    LOG("");
+
+    int items_seen = 0;
+
+    program_wide_logger().get().eol(parser_bits::NO_ENDING);
+    if (q == ANY) {
+      while (item_list.length()) {
+        octopus_request_id id;
+        infoton *found = bing->acquire_for_any(id);
+        if (!found) 
+          deadly_error(class_name(), "ANY", "item was missing");
+        WHACK(found);
+        items_seen++;
+        if (! (items_seen % 50) ) {
+          printf("v");
+          fflush(NIL);
+        }
+        bool saw_it = false;
+        for (int q = 0; q < item_list.length(); q++) {
+          if (item_list[q] == id) {
+            saw_it = true;
+            item_list.zap(q, q);
+            break;
+          }
+        }
+        if (!saw_it)
+          deadly_error(class_name(), "ANY", "didn't see id for the item");
+      }
+    } else if (q == ENT) {
+      while (item_list.length()) {
+        octopus_request_id id;
+        infoton *found = bing->acquire_for_entity(item_list[0]._entity, id);
+        if (!found) 
+          deadly_error(class_name(), "ENT", "item was missing");
+        WHACK(found);
+        items_seen++;
+        if (! (items_seen % 50) ) {
+          printf("v");
+          fflush(NIL);
+        }
+        bool saw_it = false;
+        for (int q = 0; q < item_list.length(); q++) {
+          if (item_list[q] == id) {
+            saw_it = true;
+            item_list.zap(q, q);
+            break;
+          }
+        }
+        if (!saw_it)
+          deadly_error(class_name(), "ENT", "didn't see id for the item");
+      }
+    } else if (q == ID) {
+      for (int j = 0; j < item_list.length(); j++) {
+        infoton *found = bing->acquire_for_identifier(item_list[j]);
+        if (!found) 
+          deadly_error(class_name(), "ENT", "item was missing");
+        WHACK(found);
+        items_seen++;
+        if (! (items_seen % 50) ) {
+          printf("v");
+          fflush(NIL);
+        }
+        item_list.zap(j, j);
+        j--;  // skip back.
+      }
+    } else {
+      deadly_error(class_name(), "looping", "bad enum value");
+    }
+    program_wide_logger().get().eol(parser_bits::CRLF_AT_END);
+    LOG("");
+    item_list.reset();
+    item_list.shrink();
+
+    if (bing->entities())
+      deadly_error(class_name(), "check left", "there are still contents in table!");
+
+    bing->clean_out_deadwood();
+
+LOG(a_sprintf("test type %d ending...", q));
+//LOG("note memory usage and hit a key:");
+//c = getchar();
+  }
+
+  WHACK(bing);
+LOG("done testing, zapped bin, now should be low memory.");
+//c = getchar();
+
+  LOG("octopus_entity:: works for those functions tested.");
+  return 0;
+}
+
+HOOPLE_MAIN(test_bin, )
+
diff --git a/octopi/library/tests_octopus/t_bin_threaded.cpp b/octopi/library/tests_octopus/t_bin_threaded.cpp
new file mode 100644 (file)
index 0000000..df719f7
--- /dev/null
@@ -0,0 +1,337 @@
+/*****************************************************************************\
+*                                                                             *
+*  Name   : test_entity_data_bin_threaded                                     *
+*  Author : Chris Koeritz                                                     *
+*                                                                             *
+*******************************************************************************
+* Copyright (c) 2010-$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 <application/hoople_main.h>
+#include <basis/byte_array.h>
+#include <mathematics/chaos.h>
+#include <basis/functions.h>
+#include <basis/guards.h>
+#include <basis/astring.h>
+#include <basis/mutex.h>
+#include <structures/amorph.h>
+#include <structures/static_memory_gremlin.h>
+#include <loggers/console_logger.h>
+#include <processes/ethread.h>
+#include <processes/safe_roller.h>
+#include <timely/time_control.h>
+#include <timely/time_stamp.h>
+#include <octopus/entity_data_bin.h>
+#include <octopus/entity_defs.h>
+#include <octopus/unhandled_request.h>
+#include <application/application_shell.h>
+#include <configuration/application_configuration.h>
+#include <textual/string_manipulation.h>
+
+#include <stdio.h>
+
+#ifdef __WIN32__
+  #include <process.h>
+#endif
+
+using namespace application;
+using namespace loggers;
+using namespace octopi;
+using namespace processes;
+using namespace timely;
+
+// global constants...
+
+// how much data is the entity data bin allowed to hold at one time.
+const int MAXIMUM_DATA_PER_ENTITY = 1 * KILOBYTE;
+//tiny limit to test having too much data.
+
+// controls the timing of the thread that adds items.
+const int MIN_ADDER_THREAD_PAUSE = 3;
+const int MAX_ADDER_THREAD_PAUSE = 20;
+
+// controls the timing of the item deleting thread.
+const int MIN_WHACKER_THREAD_PAUSE = 8;
+const int MAX_WHACKER_THREAD_PAUSE = 70;
+
+// bound the randomly chosen pause time for the cleanup thread.
+const int MIN_TIDIER_THREAD_PAUSE = 60;
+const int MAX_TIDIER_THREAD_PAUSE = 500;
+
+// monk is kept asleep most of the time or he'd be trashing
+// all our data too frequently.
+const int MIN_MONK_THREAD_PAUSE = 2 * MINUTE_ms;
+const int MAX_MONK_THREAD_PAUSE = 4 * MINUTE_ms;
+
+// the range of new items added whenever the creator thread is hit.
+const int MINIMUM_ITEMS_ADDED = 1;
+const int MAXIMUM_ITEMS_ADDED = 20;
+
+const int DEFAULT_THREADS = 90;
+  // the number of threads we create by default.
+
+const int DEFAULT_RUN_TIME = 80 * MINUTE_ms;
+//2 * MINUTE_ms;
+  // the length of time to run the program.
+
+const int DATA_DECAY_TIME = 1 * MINUTE_ms;
+  // how long we retain unclaimed data.
+
+const int MONKS_CLEANING_TIME = 10 * SECOND_ms;
+  // a very short duration for data to live.
+
+#define LOG(to_print) printf("%s\n", (char *)astring(to_print).s());
+//CLASS_EMERGENCY_LOG(program_wide_logger().get(), to_print)
+  // our macro for logging with a timestamp.
+
+// global objects...
+
+chaos _rando;  // our randomizer.
+
+// replace app_shell version with local randomizer, so all the static
+// functions can employ it also.
+#define randomizer() _rando
+
+entity_data_bin binger(MAXIMUM_DATA_PER_ENTITY);
+
+octopus_request_id create_request_id()
+{
+  // test the basic filling of the values in an entity.
+  octopus_request_id req_id;
+  if (randomizer().inclusive(1, 100) < 25) {
+    // some of the time we make a totally random entity id.
+    int sequencer = randomizer().inclusive(1, MAXINT - 10);
+    int add_in = randomizer().inclusive(0, MAXINT - 10);
+    int process_id = randomizer().inclusive(0, MAXINT - 10);
+    req_id._entity = octopus_entity(string_manipulation::make_random_name(),
+        process_id, sequencer, add_in);
+  } else {
+    // sometimes we use a less random identity.
+    int sequencer = randomizer().inclusive(1, 3);
+    int add_in = 12;
+    int process_id = randomizer().inclusive(1, 4);
+    req_id._entity = octopus_entity("boringentity",
+        process_id, sequencer, add_in);
+  }
+  req_id._request_num = randomizer().inclusive(1, MAXINT - 10);
+  return req_id;
+}
+
+// this thread creates new items for the entity data bin.
+class ballot_box_stuffer : public ethread
+{
+public:
+  ballot_box_stuffer() : ethread(0) {
+    FUNCDEF("constructor");
+    LOG("+creator");
+  }
+
+  virtual ~ballot_box_stuffer() {
+    FUNCDEF("destructor");
+    LOG("~creator");
+  }
+
+  DEFINE_CLASS_NAME("ballot_box_stuffer");
+
+  void perform_activity(void *formal(data)) {
+    FUNCDEF("perform_activity");
+    while (!should_stop()) {
+      // add a new item to the cache.
+      int how_many = randomizer().inclusive(MINIMUM_ITEMS_ADDED,
+          MAXIMUM_ITEMS_ADDED);
+      for (int i = 0; i < how_many; i++) {
+        string_array random_strings;
+        int string_count = randomizer().inclusive(1, 10);
+        // we create a random classifier, just to use up some space.
+        for (int q = 0; q < string_count; q++) {
+          random_strings += string_manipulation::make_random_name();
+        }
+        unhandled_request *newbert = new unhandled_request(create_request_id(),
+            random_strings);
+        binger.add_item(newbert, create_request_id());
+      }
+      // snooze.
+      int sleepy_time = randomizer().inclusive(MIN_ADDER_THREAD_PAUSE,
+          MAX_ADDER_THREAD_PAUSE);
+      time_control::sleep_ms(sleepy_time);
+    }
+  }
+
+};
+
+// this thread eliminates entries in the ballot box.
+class vote_destroyer : public ethread
+{
+public:
+  vote_destroyer() : ethread(0) {
+    FUNCDEF("constructor");
+    LOG("+destroyer");
+  }
+
+  virtual ~vote_destroyer() {
+    FUNCDEF("destructor");
+    LOG("~destroyer");
+  }
+
+  DEFINE_CLASS_NAME("vote_destroyer");
+
+  void perform_activity(void *formal(data)) {
+    FUNCDEF("perform_activity");
+    while (!should_stop()) {
+      // snag any old item and drop it on the floor.
+      octopus_request_id id;
+      infoton *found = binger.acquire_for_any(id);
+      WHACK(found);
+      // snooze.
+      int sleepy_time = randomizer().inclusive(MIN_WHACKER_THREAD_PAUSE,
+          MAX_WHACKER_THREAD_PAUSE);
+      time_control::sleep_ms(sleepy_time);
+    }
+  }
+};
+
+// this class makes sure the deadwood is cleaned out of the entity bin.
+class obsessive_compulsive : public ethread
+{
+public:
+  obsessive_compulsive() : ethread(0) {
+    FUNCDEF("constructor");
+    LOG("+cleaner");
+  }
+
+  virtual ~obsessive_compulsive() {
+    FUNCDEF("destructor");
+    LOG("~cleaner");
+  }
+
+  DEFINE_CLASS_NAME("obsessive_compulsive");
+
+  void perform_activity(void *formal(data)) {
+    FUNCDEF("perform_activity");
+    while (!should_stop()) {
+      // make sure there's nothing rotting too long.
+      binger.clean_out_deadwood(DATA_DECAY_TIME);
+      // snooze.
+      int sleepy_time = randomizer().inclusive(MIN_TIDIER_THREAD_PAUSE,
+          MAX_TIDIER_THREAD_PAUSE);
+      time_control::sleep_ms(sleepy_time);
+    }
+  }
+};
+
+// this thread will destroy all data in the bins while cleaning furiously.
+class monk_the_detective : public ethread
+{
+public:
+  monk_the_detective() : ethread(0) {
+    FUNCDEF("constructor");
+    LOG("+monk");
+  }
+
+  virtual ~monk_the_detective() {
+    FUNCDEF("destructor");
+    LOG("~monk");
+  }
+
+  DEFINE_CLASS_NAME("monk_the_detective");
+
+  void perform_activity(void *formal(data)) {
+    FUNCDEF("perform_activity");
+    while (!should_stop()) {
+      // one activation of monk has devastating consequences.  we empty out
+      // the data one item at a time until we see no data at all.  after
+      // cleaning each item, we ensure that the deadwood is cleaned out.
+      binger._ent_lock->lock();
+LOG(a_sprintf("monk sees %d items.", binger.items_held()));
+      while (binger.items_held()) {
+        // grab one instance of any item in the bin.
+        octopus_request_id id;
+        infoton *found = binger.acquire_for_any(id);
+        WHACK(found);
+        // also clean out things a lot faster than normal.  
+        binger.clean_out_deadwood(MONKS_CLEANING_TIME);
+      }
+      binger._ent_lock->unlock();
+LOG(a_sprintf("after a little cleaning, monk sees %d items.", binger.items_held()));
+      // snooze.
+      int sleepy_time = randomizer().inclusive(MIN_MONK_THREAD_PAUSE,
+          MAX_MONK_THREAD_PAUSE);
+      time_control::sleep_ms(sleepy_time);
+    }
+  }
+};
+
+//////////////
+
+class test_entity_data_bin_threaded : public application_shell
+{
+public:
+  test_entity_data_bin_threaded() : application_shell(class_name()) {}
+
+  DEFINE_CLASS_NAME("test_entity_data_bin_threaded");
+
+  int execute();
+};
+
+int test_entity_data_bin_threaded::execute()
+{
+  FUNCDEF("execute");
+
+  amorph<ethread> thread_list;
+
+  for (int i = 0; i < DEFAULT_THREADS; i++) {
+    ethread *t = NIL;
+    if (i == DEFAULT_THREADS - 1) {
+      // last item gets special treatment; we reserve this space for monk.
+      t = new monk_the_detective;
+    } else if (i % 3 == 0) {
+      t = new ballot_box_stuffer;
+    } else if (i % 3 == 1) {
+      t = new vote_destroyer;
+    } else {  // i % 3 must = 2.
+      t = new obsessive_compulsive;
+    }
+    thread_list.append(t);
+    ethread *q = thread_list[thread_list.elements() - 1];
+    if (q != t)
+      deadly_error(class_name(), func, "amorph has incorrect pointer!");
+    // start the thread we added.
+    thread_list[thread_list.elements() - 1]->start(NIL);
+  }
+
+  time_stamp when_to_leave(DEFAULT_RUN_TIME);
+  while (when_to_leave > time_stamp()) {
+    time_control::sleep_ms(100);
+  }
+
+//  LOG("now cancelling all threads....");
+
+//  for (int j = 0; j < thread_list.elements(); j++) thread_list[j]->cancel();
+
+//  LOG("now stopping all threads....");
+
+//  for (int k = 0; k < thread_list.elements(); k++) thread_list[k]->stop();
+
+//  LOG("resetting thread list....");
+
+  thread_list.reset();  // should whack all threads.
+
+  LOG("done exiting from all threads....");
+
+//report the results:
+// how many objects created.
+// how many got destroyed.
+// how many evaporated due to timeout.
+
+
+  guards::alert_message("t_bin_threaded:: works for all functions tested.");
+  return 0;
+}
+
+HOOPLE_MAIN(test_entity_data_bin_threaded, )
+
diff --git a/octopi/library/tests_octopus/t_entity.cpp b/octopi/library/tests_octopus/t_entity.cpp
new file mode 100644 (file)
index 0000000..d924098
--- /dev/null
@@ -0,0 +1,133 @@
+/*****************************************************************************\
+*                                                                             *
+*  Name   : octopus_entity tester                                             *
+*  Author : Chris Koeritz                                                     *
+*                                                                             *
+*  Purpose:                                                                   *
+*                                                                             *
+*    Checks that the octopus_entity class is behaving as expected.            *
+*                                                                             *
+*******************************************************************************
+* 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/byte_array.h>
+#include <mathematics/chaos.h>
+#include <basis/guards.h>
+#include <basis/astring.h>
+#include <octopus/entity_defs.h>
+#include <application/application_shell.h>
+#include <loggers/console_logger.h>
+#include <loggers/file_logger.h>
+#include <structures/static_memory_gremlin.h>
+#include <sockets/tcpip_stack.h>
+#include <textual/string_manipulation.h>
+
+#ifdef __WIN32__
+  #include <process.h>
+#else
+  #include <unistd.h>
+#endif
+
+const int ITERATE_EACH_TEST = 1000;
+  // the number of times to repeat each test operation.
+
+class test_entity : public application_shell
+{
+public:
+  test_entity() : application_shell(class_name()) {}
+  DEFINE_CLASS_NAME("test_entity");
+  virtual int execute();
+};
+
+int test_entity::execute()
+{
+  chaos rando;
+  SET_DEFAULT_COMBO_LOGGER;
+  tcpip_stack stack;
+
+  octopus_entity blankie;
+  if (!blankie.blank())
+    deadly_error(class_name(), "emptiness test",
+        "the blank entity was not seen as empty.");
+  octopus_entity fullish("gurp", 28, 39, 4);
+  if (fullish.blank())
+    deadly_error(class_name(), "emptiness test",
+        "the non-blank entity was seen as empty.");
+
+  for (int i = 0; i < ITERATE_EACH_TEST; i++) {
+    // test the basic filling of the values in an entity.
+    octopus_entity blank_ent;
+    int sequencer = rando.inclusive(1, MAXINT - 10);
+    int add_in = rando.inclusive(0, MAXINT - 10);
+    octopus_entity filled_ent(stack.hostname(), application_configuration::process_id(), sequencer,
+        add_in);
+    blank_ent = octopus_entity(stack.hostname(), application_configuration::process_id(), sequencer,
+        add_in);
+    if (blank_ent != filled_ent)
+      deadly_error(class_name(), "simple reset test",
+          "failed to resolve to same id");
+    astring text1 = filled_ent.to_text();
+    astring text2 = blank_ent.to_text();
+    if (text1 != text2)
+      deadly_error(class_name(), "to_text test", "strings are different");
+///log(text1);
+    octopus_entity georgio = octopus_entity::from_text(text2);
+///log(georgio.to_text());
+    if (georgio != filled_ent)
+      deadly_error(class_name(), "from_text test",
+          "entity is different after from_text");
+
+    octopus_request_id fudnix(filled_ent, 8232390);
+    astring text3 = fudnix.to_text();
+    octopus_request_id resur = octopus_request_id::from_text(text3);
+    if (resur != fudnix)
+      deadly_error(class_name(), "from_text test",
+          "request id is different after from_text");
+    
+    blank_ent = octopus_entity();  // reset it again forcefully.
+    blank_ent = octopus_entity(filled_ent.hostname(), filled_ent.process_id(),
+        filled_ent.sequencer(), filled_ent.add_in());
+    if (blank_ent != filled_ent)
+      deadly_error(class_name(), "reset from attribs test",
+          "failed to resolve to same id");
+//    log(a_sprintf("%d: ", i + 1) + filled_ent.mangled_form());
+
+    byte_array chunk1;
+    filled_ent.pack(chunk1);
+    octopus_entity unpacked1;
+    unpacked1.unpack(chunk1);
+    if (unpacked1 != filled_ent)
+      deadly_error(class_name(), "pack/unpack test",
+          "failed to return same values");
+
+    // test of entity packing and size calculation.
+    octopus_entity ent(string_manipulation::make_random_name(1, 428),
+            randomizer().inclusive(0, MAXINT/2),
+            randomizer().inclusive(0, MAXINT/2),
+            randomizer().inclusive(0, MAXINT/2));
+    octopus_request_id bobo(ent, randomizer().inclusive(0, MAXINT/2));
+    int packed_estimate = bobo.packed_size();
+    byte_array packed_bobo;
+    bobo.pack(packed_bobo);
+    if (packed_bobo.length() != packed_estimate)
+      deadly_error(class_name(), "entity packed_size test",
+          "calculated incorrect packed size");
+  }
+
+
+  log("octopus_entity:: works for those functions tested.");
+  return 0;
+}
+
+//hmmm: tests the octopus entity object,
+//      can do exact text check if want but that's not guaranteed to be useful
+//      in the future.
+
+HOOPLE_MAIN(test_entity, )
+
diff --git a/octopi/library/tests_octopus/t_file_transfer.cpp b/octopi/library/tests_octopus/t_file_transfer.cpp
new file mode 100644 (file)
index 0000000..9deb90a
--- /dev/null
@@ -0,0 +1,187 @@
+/*****************************************************************************\
+*                                                                             *
+*  Name   : test_file_transfer_tentacle                                       *
+*  Author : Chris Koeritz                                                     *
+*                                                                             *
+*  Purpose:                                                                   *
+*                                                                             *
+*    Tests the file_transfer_tentacle without any networking involved.        *
+*                                                                             *
+*******************************************************************************
+* Copyright (c) 2005-$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/functions.h>
+#include <structures/string_array.h>
+#include <structures/static_memory_gremlin.h>
+#include <loggers/console_logger.h>
+#include <application/application_shell.h>
+#include <tentacles/file_transfer_tentacle.h>
+#include <tentacles/recursive_file_copy.h>
+
+#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s)
+
+class test_file_transfer_tentacle : public application_shell
+{
+public:
+  test_file_transfer_tentacle() : application_shell(static_class_name()) {}
+  DEFINE_CLASS_NAME("test_dirtree_fcopy");
+  int execute();
+};
+
+int test_file_transfer_tentacle::execute()
+{
+  FUNCDEF("execute");
+
+  if (__argc < 3) {
+    log("\
+This program needs two parameters:\n\
+a directory for the source root and one for the target root.\n\
+Optionally, a third parameter may specify a starting point within the\n\
+source root.\n\
+Further, if fourth or more parameters are found, they are taken to be\n\
+files to include; only they will be transferred.\n");
+    return 23;
+  }
+
+  astring source_dir = __argv[1];
+  astring target_dir = __argv[2];
+
+  astring source_start = "";
+  if (__argc >= 4) {
+    source_start = __argv[3];
+  }
+
+  string_array includes;
+  if (__argc >= 5) {
+    for (int i = 4; i < __argc; i++) {
+      includes += __argv[i];
+    }
+  }
+
+
+  outcome returned = recursive_file_copy::copy_hierarchy
+      (file_transfer_tentacle::COMPARE_SIZE_AND_TIME, source_dir,
+      target_dir, includes, source_start);
+
+/*
+  astring source_root = "snootums";
+  if (source_start.t()) {
+    source_root += filename::default_separator() + source_start;
+  }
+
+  tcpip_stack stack;
+  octopus ring_leader(stack.hostname(), 10 * MEGABYTE);
+  file_transfer_tentacle *tran = new file_transfer_tentacle(MAX_CHUNK, false);
+  ring_leader.add_tentacle(tran);
+
+  outcome add_ret = tran->add_correspondence("snootums", source_dir,
+      10 * MINUTE_ms);
+  if (add_ret != tentacle::OKAY)
+    deadly_error(class_name(), func, "failed to add the correspondence");
+
+  file_transfer_infoton *initiate = new file_transfer_infoton;
+  initiate->_request = true;
+  initiate->_command = file_transfer_infoton::TREE_COMPARISON;
+  initiate->_src_root = source_root;
+  initiate->_dest_root = target_dir;
+  directory_tree target_area(target_dir);
+  target_area.calculate();
+  initiate->package_tree_info(target_area, includes);
+
+  octopus_entity ent = ring_leader.issue_identity();
+  octopus_request_id req_id(ent, 1);
+  outcome start_ret = ring_leader.evaluate(initiate, req_id);
+  if (start_ret != tentacle::OKAY)
+    deadly_error(class_name(), func, "failed to start the comparison");
+
+  file_transfer_infoton *reply_from_init
+      = (file_transfer_infoton *)ring_leader.acquire_specific_result(req_id);
+  if (!reply_from_init)
+    deadly_error(class_name(), func, "no response to tree compare start");
+
+  filename_list diffs;
+  byte_array pack_copy = reply_from_init->_packed_data;
+  if (!diffs.unpack(pack_copy))
+    deadly_error(class_name(), func, "could not unpack filename list!");
+//  LOG(astring("got list of diffs:\n") + diffs.text_form());
+
+  octopus client_spider(stack.hostname(), 10 * MEGABYTE);
+  file_transfer_tentacle *tran2 = new file_transfer_tentacle(MAX_CHUNK, false);
+  tran2->register_file_transfer(ent, source_root, target_dir, includes);
+  client_spider.add_tentacle(tran2);
+
+  octopus_request_id resp_id(ent, 2);
+  outcome ini_resp_ret = client_spider.evaluate(reply_from_init, resp_id);
+  if (ini_resp_ret != tentacle::OKAY)
+    deadly_error(class_name(), func, "failed to process the start response!");
+
+  infoton *junk = client_spider.acquire_specific_result(resp_id);
+  if (junk)
+    deadly_error(class_name(), func, "got a response we shouldn't have!");
+
+  int iter = 0;
+  while (true) {
+LOG(a_sprintf("ongoing chunk %d", ++iter));
+
+    // keep going until we find a broken reply.
+    file_transfer_infoton *ongoing = new file_transfer_infoton;
+    ongoing->_request = true;
+    ongoing->_command = file_transfer_infoton::PLACE_FILE_CHUNKS;
+    ongoing->_src_root = source_root;
+    ongoing->_dest_root = target_dir;
+
+    octopus_request_id chunk_id(ent, iter + 10);
+    outcome place_ret = ring_leader.evaluate(ongoing, chunk_id);
+    if (place_ret != tentacle::OKAY)
+      deadly_error(class_name(), func, "failed to run ongoing transfer");
+  
+    file_transfer_infoton *reply = (file_transfer_infoton *)ring_leader
+         .acquire_specific_result(chunk_id);
+    if (!reply)
+      deadly_error(class_name(), func, "failed to get ongoing transfer reply");
+
+    if (!reply->_packed_data.length()) {
+      LOG("hit termination condition: no data packed in for file chunks.");
+      break;
+    }
+
+    byte_array copy = reply->_packed_data;
+    while (copy.length()) {
+      file_transfer_header head;
+      if (!head.unpack(copy)) 
+        deadly_error(class_name(), func, "failed to unpack header");
+LOG(astring("header: ") + head.text_form());
+LOG(a_sprintf("size in array: %d", copy.length()));
+      if (copy.length() < head._length)
+        deadly_error(class_name(), func, "not enough length in array");
+      copy.zap(0, head._length - 1);
+LOG(a_sprintf("size in array now: %d", copy.length()));
+    }
+    if (copy.length())
+      deadly_error(class_name(), func, "still had data in array");
+
+    octopus_request_id resp_id(ent, iter + 11);
+    outcome resp_ret = client_spider.evaluate(reply, resp_id);
+    if (resp_ret != tentacle::OKAY)
+      deadly_error(class_name(), func, "failed to process the transfer reply!");
+  
+  }
+*/
+
+  if (returned == common::OKAY)
+    guards::alert_message("file_transfer_tentacle:: works for those "
+        "functions tested.");
+  else
+    guards::alert_message(astring("file_transfer_tentacle:: failed with "
+        "outcome=") + recursive_file_copy::outcome_name(returned));
+  return 0;
+}
+
+HOOPLE_MAIN(test_file_transfer_tentacle, )
+
diff --git a/octopi/library/tests_octopus/t_identity.cpp b/octopi/library/tests_octopus/t_identity.cpp
new file mode 100644 (file)
index 0000000..eb7dd35
--- /dev/null
@@ -0,0 +1,86 @@
+/*****************************************************************************\
+*                                                                             *
+*  Name   : octopus identity test                                             *
+*  Author : Chris Koeritz                                                     *
+*                                                                             *
+*  Purpose:                                                                   *
+*                                                                             *
+*    Checks out the client identification methods in octopus.                 *
+*                                                                             *
+*******************************************************************************
+* 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 <octopus/entity_defs.h>
+#include <octopus/identity_infoton.h>
+#include <octopus/infoton.h>
+#include <octopus/octopus.h>
+#include <octopus/tentacle.h>
+#include <application/application_shell.h>
+#include <loggers/console_logger.h>
+#include <structures/static_memory_gremlin.h>
+
+//////////////
+
+class test_octopus_identity : public application_shell
+{
+public:
+  test_octopus_identity() : application_shell(class_name()) {}
+  DEFINE_CLASS_NAME("test_octopus_identity");
+  virtual int execute();
+};
+
+int test_octopus_identity::execute()
+{
+  octopus logos("local", 18 * MEGABYTE);
+
+  identity_infoton *ide = new identity_infoton;
+  octopus_request_id junk_id = octopus_request_id::randomized_id();
+    // bogus right now.
+
+  byte_array packed;
+  ide->pack(packed);
+  if (ide->packed_size() != packed.length())
+    deadly_error(class_name(), "packing test",
+        astring("the packed size was different than expected."));
+
+  outcome ret = logos.evaluate(ide, junk_id);
+  if (ret != tentacle::OKAY)
+    deadly_error(class_name(), "evaluate test",
+        astring("the evaluation failed with an error ")
+        + tentacle::outcome_name(ret));
+log("point a");
+
+  octopus_request_id response_id;  // based on bogus from before.
+  infoton *response = logos.acquire_result(junk_id._entity, response_id);
+  if (!response)
+    deadly_error(class_name(), "acquire test",
+        astring("the acquire_result failed to produce a result."));
+
+  identity_infoton *new_id = dynamic_cast<identity_infoton *>(response);
+  if (!new_id)
+    deadly_error(class_name(), "casting",
+        astring("the returned infoton is not the right type."));
+
+  octopus_entity my_ide = new_id->_new_name;
+
+log(astring("new id is: ") + my_ide.text_form());
+
+  if (my_ide.blank())
+    deadly_error(class_name(), "retrieving id",
+        astring("the new entity id is blank."));
+
+
+  log("octopus:: identity works for those functions tested.");
+
+  return 0;
+}
+
+HOOPLE_MAIN(test_octopus_identity, )
+
diff --git a/octopi/library/tests_octopus/t_security.cpp b/octopi/library/tests_octopus/t_security.cpp
new file mode 100644 (file)
index 0000000..9531ab6
--- /dev/null
@@ -0,0 +1,184 @@
+/*****************************************************************************\
+*                                                                             *
+*  Name   : octopus security test                                             *
+*  Author : Chris Koeritz                                                     *
+*                                                                             *
+*  Purpose:                                                                   *
+*                                                                             *
+*    Checks out the login support for octopus.  This just exercises the base  *
+*  support which doesn't perform any extra verification on the user.          *
+*                                                                             *
+*******************************************************************************
+* 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 <basis/mutex.h>
+#include <structures/static_memory_gremlin.h>
+#include <octopus/entity_defs.h>
+#include <octopus/infoton.h>
+#include <octopus/octopus.h>
+#include <octopus/tentacle.h>
+#include <application/application_shell.h>
+#include <loggers/console_logger.h>
+#include <structures/static_memory_gremlin.h>
+#include <sockets/internet_address.h>
+#include <tentacles/login_tentacle.h>
+#include <tentacles/simple_entity_registry.h>
+
+//////////////
+
+astring base_list[] = { "cli", "simp" };
+
+SAFE_STATIC_CONST(string_array, simp_classifier, (2, base_list))
+
+class simple_infoton : public infoton
+{
+public:
+  astring futzle;
+
+  simple_infoton() : infoton(simp_classifier()) {}
+
+  virtual void pack(byte_array &packed_form) const {
+    futzle.pack(packed_form);
+  }
+  virtual bool unpack(byte_array &packed_form) {
+    if (!futzle.unpack(packed_form)) return false;
+    return true;
+  }
+  virtual int packed_size() const { return futzle.length() + 1; }
+  virtual clonable *clone() const { return new simple_infoton(*this); }
+
+private:
+};
+
+//////////////
+
+// provides a simple service to allow us to test whether the security is
+// working or not.
+
+class simple_tentacle : public tentacle
+{
+public:
+  simple_tentacle() : tentacle(simp_classifier(), true) {}
+
+  virtual outcome reconstitute(const string_array &classifier,
+          byte_array &packed_form, infoton * &reformed) {
+    reformed = NIL;
+    if (classifier != simp_classifier()) return NO_HANDLER;
+    reformed = new simple_infoton;
+    if (!reformed->unpack(packed_form)) {
+      WHACK(reformed);
+      return GARBAGE;
+    }
+    return OKAY;
+  }
+
+  virtual outcome consume(infoton &to_chow,
+          const octopus_request_id &formal(item_id), byte_array &transformed) {
+    transformed.reset();
+    if (to_chow.classifier() != simp_classifier()) return NO_HANDLER;
+    // consume without doing anything.
+    return OKAY;
+  }
+
+  virtual void expunge(const octopus_entity &formal(to_zap)) {}
+};
+
+//////////////
+
+//hmmm: this test should do a sample login octopus and do a login, reside for
+//      a while, log out, do another one, let it time out, try to access
+//      something with dead id hoping to be rejected, etc.
+
+class test_octopus_security : public application_shell
+{
+public:
+  test_octopus_security() : application_shell(class_name()) {}
+  DEFINE_CLASS_NAME("test_octopus_security");
+  virtual int execute();
+};
+
+int test_octopus_security::execute()
+{
+  octopus logos("local", 18 * MEGABYTE);
+  simple_tentacle *tenty = new simple_tentacle;
+  logos.add_tentacle(tenty);
+  tenty = NIL;  // octopus has charge of this now.
+
+  // turn on security in logos.
+  simple_entity_registry *guardian = new simple_entity_registry;
+  logos.add_tentacle(new login_tentacle(*guardian), true);
+
+  // create an entity to work with.
+  octopus_entity jimbo("localhost", application_configuration::process_id(), 128, 982938);
+  octopus_request_id req1(jimbo, 1);
+
+  // add the user jimbo.
+  guardian->add_entity(jimbo, byte_array());
+
+  // create a piece of data to try running on tentacle.
+  simple_infoton testose;
+  simple_infoton *testose_copy = new simple_infoton(testose);
+
+  // test that the simple tentacle allows the op.
+  outcome ret = logos.evaluate(testose_copy, req1);
+  if (ret != tentacle::OKAY)
+    deadly_error(class_name(), "first test",
+        astring("the operation failed with an error ")
+            + tentacle::outcome_name(ret));
+
+  // create another entity to work with.
+  octopus_entity burfo("localhost", application_configuration::process_id(), 372, 2989);
+  octopus_request_id req2(burfo, 1);
+
+  // try with an unlicensed user burfo...
+  testose_copy = new simple_infoton(testose);
+  ret = logos.evaluate(testose_copy, req2);
+  if (ret == tentacle::OKAY)
+    deadly_error(class_name(), "second test",
+        astring("the operation didn't fail when it should have."));
+  else if (ret != tentacle::DISALLOWED)
+    deadly_error(class_name(), "second test",
+        astring("the operation didn't provide the proper outcome, it gave: ")
+            + tentacle::outcome_name(ret));
+
+  // remove the user jimbo.
+  guardian->zap_entity(jimbo);
+
+  // test that jimbo fails too now.
+  testose_copy = new simple_infoton(testose);
+  ret = logos.evaluate(testose_copy, req1);
+  if (ret == tentacle::OKAY)
+    deadly_error(class_name(), "third test",
+        astring("the operation didn't fail when it should have."));
+  else if (ret != tentacle::DISALLOWED)
+    deadly_error(class_name(), "third test",
+        astring("the operation didn't provide the proper outcome, it gave: ")
+            + tentacle::outcome_name(ret));
+
+  // add the user burfo in now instead.
+  guardian->add_entity(burfo, byte_array());
+
+  // test that burfo works.
+  testose_copy = new simple_infoton(testose);
+  ret = logos.evaluate(testose_copy, req2);
+  if (ret != tentacle::OKAY)
+    deadly_error(class_name(), "fourth test",
+        astring("the operation failed with an error ")
+        + tentacle::outcome_name(ret));
+
+  log("octopus:: security works for those functions tested.");
+
+  WHACK(guardian); 
+
+  return 0;
+}
+
+HOOPLE_MAIN(test_octopus_security, )
+
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, )
+