]> feistymeow.org Git - feisty_meow.git/commitdiff
breaking changes - tests for octopus and cromp
authorFred T. Hamster <fred@feistymeow.org>
Sat, 14 Feb 2026 01:17:53 +0000 (20:17 -0500)
committerFred T. Hamster <fred@feistymeow.org>
Sat, 14 Feb 2026 01:17:53 +0000 (20:17 -0500)
these test sets have been out in the cold for a while, and now we're bringing them back in for a defrost.  they will not build for a bit at least.

23 files changed:
octopi/library/makefile
octopi/library/tests_cromp/cromp_decoder.cpp [new file with mode: 0644]
octopi/library/tests_cromp/crompish_pax.h [new file with mode: 0644]
octopi/library/tests_cromp/makefile [new file with mode: 0644]
octopi/library/tests_cromp/makefile.decoder [new file with mode: 0644]
octopi/library/tests_cromp/test_cromp_client.cpp [new file with mode: 0644]
octopi/library/tests_cromp/test_cromp_server.cpp [new file with mode: 0644]
octopi/library/tests_cromp/test_many_cromp.cpp [new file with mode: 0644]
octopi/library/tests_cromp/version.ini [new file with mode: 0644]
octopi/library/tests_octopus/t_bin.cpp [deleted file]
octopi/library/tests_octopus/t_bin_threaded.cpp [deleted file]
octopi/library/tests_octopus/t_entity.cpp [deleted file]
octopi/library/tests_octopus/t_file_transfer.cpp [deleted file]
octopi/library/tests_octopus/t_identity.cpp [deleted file]
octopi/library/tests_octopus/t_security.cpp [deleted file]
octopi/library/tests_octopus/t_unpacker.cpp [deleted file]
octopi/library/tests_octopus/test_bin.cpp [new file with mode: 0644]
octopi/library/tests_octopus/test_bin_threaded.cpp [new file with mode: 0644]
octopi/library/tests_octopus/test_entity.cpp [new file with mode: 0644]
octopi/library/tests_octopus/test_file_transfer.cpp [new file with mode: 0644]
octopi/library/tests_octopus/test_identity.cpp [new file with mode: 0644]
octopi/library/tests_octopus/test_security.cpp [new file with mode: 0644]
octopi/library/tests_octopus/test_unpacker.cpp [new file with mode: 0644]

index 29af249d83c2ec3ff56ef875126dd3885ac7b18e..7f4a2053df77eaafce44684c2fb7edd4dd00935c 100644 (file)
@@ -6,9 +6,9 @@ BUILD_BEFORE = octopus \
   tentacles \
   cromp \
   synchronic \
-  tests_sockets 
-
-#  tests_octopus
+  tests_cromp \
+  tests_octopus \
+  tests_sockets
 
 include rules.def
 
diff --git a/octopi/library/tests_cromp/cromp_decoder.cpp b/octopi/library/tests_cromp/cromp_decoder.cpp
new file mode 100644 (file)
index 0000000..6e9ff9e
--- /dev/null
@@ -0,0 +1,112 @@
+/*****************************************************************************\
+*                                                                             *
+*  Name   : cromp_decoder app                                                 *
+*  Author : Chris Koeritz                                                     *
+*                                                                             *
+*******************************************************************************
+* 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/istring.h>
+#include <cromp/cromp_common.h>
+#include <octopus/entity_defs.h>
+#include <opsystem/application_shell.h>
+#include <loggers/console_logger.h>
+#include <loggers/file_logger.h>
+#include <data_struct/static_memory_gremlin.h>
+#include <sockets/machine_uid.h>
+#include <textual/byte_format.h>
+
+#include <stdio.h>
+
+#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger(), s)
+#define BASE_LOG(s) EMERGENCY_LOG(program_wide_logger(), s)
+
+const int MAX_LINE = 2048;
+  // the longest line we'll bother to try to process.
+
+class cromp_decoder : public application_shell
+{
+public:
+  cromp_decoder();
+  ~cromp_decoder();
+
+  virtual int execute();
+
+  IMPLEMENT_CLASS_NAME("cromp_decoder");
+};
+
+////////////////////////////////////////////////////////////////////////////
+
+cromp_decoder::cromp_decoder() : application_shell(class_name()) {}
+
+cromp_decoder::~cromp_decoder() {}
+
+int cromp_decoder::execute()
+{
+  FUNCDEF("execute");
+
+  BASE_LOG("\
+This application will decode a cromp entity and report the different values");
+  BASE_LOG("\
+that are encoded into it.");
+
+  istring buffer;  // we'll read input from the user into this.
+
+  while (true) {
+    BASE_LOG("Please enter the entity (or hit just enter to exit).")
+    
+    buffer = istring('\0', MAX_LINE + 10);  // reset the buffer.
+    char *buf2 = fgets(buffer.s(), MAX_LINE, stdin);
+    if (buf2 != buffer.s()) {
+      deadly_error(class_name(), func,
+          "memory was allocated when we didn't want it to be.");
+    }
+
+    buffer.shrink();
+    buffer.strip("\r\n", istring::FROM_END);
+    if (!buffer.length()) break;
+
+    // make sure they didn't actually give us a request id.
+    int indy = buffer.find('/');
+    if (non_negative(indy)) buffer.zap(indy, buffer.end());
+
+    octopus_entity ent = octopus_entity::from_text(buffer);
+    if (ent.blank()) {
+      BASE_LOG("That entity was blank or invalid.");
+      BASE_LOG("");
+      continue;
+    }
+
+    BASE_LOG("");
+    BASE_LOG("");
+    BASE_LOG("Entity contains:");
+    BASE_LOG("");
+    BASE_LOG(isprintf("\tProcess ID=%d", ent.process_id()));
+    BASE_LOG(isprintf("\tSequencer=%d", ent.sequencer()));
+    BASE_LOG(isprintf("\tChaotic Addin=%d", ent.add_in()));
+
+    istring host;
+    machine_uid machine;
+    bool worked = cromp_common::decode_host(ent.hostname(), host, machine);
+    if (!worked) {
+      BASE_LOG("Failed to decode the hostname!  Was it a valid entity?");
+      continue;
+    }
+    BASE_LOG(istring("\tPartial Hostname=") + host);
+    BASE_LOG(istring("\tMachine UID=") + machine.text_form());
+    BASE_LOG("");
+  }
+
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+HOOPLE_MAIN(cromp_decoder, )
+
diff --git a/octopi/library/tests_cromp/crompish_pax.h b/octopi/library/tests_cromp/crompish_pax.h
new file mode 100644 (file)
index 0000000..23974b6
--- /dev/null
@@ -0,0 +1,99 @@
+#ifndef CROMPISH_PAX_GROUP
+#define CROMPISH_PAX_GROUP
+
+/*****************************************************************************\
+*                                                                             *
+*  Name   : crompish packets for tester                                       *
+*  Author : Chris Koeritz                                                     *
+*                                                                             *
+*  Purpose:                                                                   *
+*                                                                             *
+*    Some simple transactions that can be used for the CROMP tester.          *
+*                                                                             *
+*******************************************************************************
+* 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/chaos.h>
+#include <basis/string_array.h>
+#include <cromp/cromp_transaction.h>
+#include <geometric/screen_rectangle.h>
+#include <octopus/infoton.h>
+#include <octopus/tentacle_helper.h>
+
+using namespace geometric;
+
+class bubble : public infoton
+{
+public:
+  bubble(int data_segment_size = 0, const screen_rectangle &boundaries
+      = screen_rectangle(), int color = 0)
+    // constructs a bubble within the "boundaries" that has "color" and a data
+    // segment size specified by "data_segment_size".  the color definitions
+    // reside elsewhere.
+  : infoton(bubble_classing()), _data()
+  { reset(data_segment_size, boundaries, color); }
+
+  const string_array &bubble_classing() {
+    static istring bubbs[2] = { "bubble", "rubble" };
+    static string_array barray(2, bubbs);
+    return barray;
+  }
+
+  void reset(int data_segment_size, const screen_rectangle &boundaries,
+      int color) {
+    _color = color;
+    _bounds = boundaries;
+    _data.reset(data_segment_size);
+  }
+
+  int data_length() const { return _data.length(); }
+
+  clonable *clone() const { return cloner<bubble>(*this); }
+
+  byte_array &data() { return _data; }
+
+  int non_data_overhead() const { return packed_size() - _data.length(); }
+
+  virtual void pack(byte_array &packed_form) const {
+    basis::attach(packed_form, _color);
+    _bounds.pack(packed_form);
+    basis::attach(packed_form, _data);
+  }
+
+  int packed_size() const {
+    return _data.length() + 2 * sizeof(int)       // packed byte array.
+        + sizeof(int)                         // packed color.
+        + 4 * sizeof(int);                    // packed screen rectangle.
+  }
+
+  virtual bool unpack(byte_array &packed_form) {
+    if (!basis::detach(packed_form, _color)) return false;
+    if (!_bounds.unpack(packed_form)) return false;
+    if (!basis::detach(packed_form, _data)) return false;
+    return true;
+  }
+
+private:
+  screen_rectangle _bounds;
+  int _color;
+  byte_array _data;
+};
+
+////////////////////////////////////////////////////////////////////////////
+
+class bubbles_tentacle : public tentacle_helper<bubble>
+{
+public:
+  bubbles_tentacle(bool backgrounded)
+      : tentacle_helper<bubble>(bubble().classifier(), backgrounded)
+  {}
+};
+
+#endif
+
diff --git a/octopi/library/tests_cromp/makefile b/octopi/library/tests_cromp/makefile
new file mode 100644 (file)
index 0000000..391d36f
--- /dev/null
@@ -0,0 +1,19 @@
+CONSOLE_MODE = t
+
+include cpp/variables.def
+
+PROJECT = test_cromp
+TYPE = test
+TARGETS = test_cromp_client.exe test_cromp_server.exe test_many_cromp.exe
+#DEFINITIONS += USE_HOOPLE_DLLS
+LAST_TARGETS = create_decoder_ring
+#LOCAL_LIBS_USED = basis i_library i_communication
+USE_SSL = t
+#VCPP_USE_SOCK = t
+RUN_TARGETS = $(ACTUAL_TARGETS)
+
+include cpp/rules.def
+
+create_decoder_ring:
+       $(MAKE) -f makefile.decoder
+
diff --git a/octopi/library/tests_cromp/makefile.decoder b/octopi/library/tests_cromp/makefile.decoder
new file mode 100644 (file)
index 0000000..a54e4ae
--- /dev/null
@@ -0,0 +1,17 @@
+CONSOLE_MODE = t
+
+include cpp/variables.def
+
+PROJECT = decoder_ring
+TYPE = application
+TARGETS = cromp_decoder.exe
+LOCAL_LIBS_USED = basis 
+#ifeq "$(COMPILER)" "VISUAL_CPP"
+#  SOURCE += cromp_decoder_version.rc
+#endif
+#USE_SSL = t
+#VCPP_USE_SOCK = t
+#RUN_TARGETS = $(ACTUAL_TARGETS)
+
+include cpp/rules.def
+
diff --git a/octopi/library/tests_cromp/test_cromp_client.cpp b/octopi/library/tests_cromp/test_cromp_client.cpp
new file mode 100644 (file)
index 0000000..b65d87f
--- /dev/null
@@ -0,0 +1,798 @@
+/*****************************************************************************\
+*                                                                             *
+*  Name   : test_cromp_client                                                 *
+*  Author : Chris Koeritz                                                     *
+*                                                                             *
+*******************************************************************************
+* 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 "crompish_pax.h"
+
+#include <basis/chaos.h>
+#include <basis/istring.h>
+#include <basis/portable.h>
+#include <basis/set.h>
+#include <cromp/cromp_client.h>
+#include <mechanisms/ithread.h>
+#include <mechanisms/thread_cabinet.h>
+#include <mechanisms/throughput_counter.h>
+#include <octopus/entity_data_bin.h>
+#include <octopus/entity_defs.h>
+#include <octopus/infoton.h>
+#include <opsystem/application_shell.h>
+#include <opsystem/command_line.h>
+#include <loggers/console_logger.h>
+#include <loggers/file_logger.h>
+#include <opsystem/filename.h>
+#include <opsystem/rendezvous.h>
+#include <data_struct/static_memory_gremlin.h>
+#include <sockets/address.h>
+
+#include <stdlib.h>
+
+#undef LOG
+#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger(), s)
+
+#define DEBUG_TESTER
+  // uncomment for noisier version.
+
+// the number of transactions to send during a test.  if timing connection
+// duration, then use a maximum of 1.  if timing speed of operation once
+// connected, use a large number.
+//const int MAXIMUM_SENDS = 10008;
+const int MAXIMUM_SENDS = 100008;
+//const int MAXIMUM_SENDS = 10000008;
+  // have had success with up to 10000000 sends using small data segments.
+
+const int NUMBER_OF_THREADS = 1;
+//const int NUMBER_OF_THREADS = 10;
+//const int NUMBER_OF_THREADS = 20;
+  // the number of simultaneous actors on the single cromp_client.
+
+//const int GRABBER_THREADS = 5;
+const int GRABBER_THREADS = 0;
+  // the number of threads that just pluck at the cromp_client trying to
+  // interfere with the testing threads.
+
+//const int MAX_SEND_TRIES = 0;  // don't pause.
+const int MAX_SEND_TRIES = 1;  // try to get stuff out but don't wait long.
+//const int MAX_SEND_TRIES = 5;  // wait a reasonable amount of times to send.
+//const int MAX_SEND_TRIES = 10000;  // force it to get out, hopefully.
+  // the number of times we try to push the sends out.  zero means never
+  // try to push anything, just add it to the buffer.  1 or more is that
+  // many tries to push the send.
+
+//const int CHECKPOINT_SIZE = 1000;
+//const int CHECKPOINT_SIZE = 100;
+const int CHECKPOINT_SIZE = 100;
+  // prints a counter out when we reach a multiple of this many sends.
+
+//const int DATA_SEGMENT_SIZE = 0;
+const int DATA_SEGMENT_SIZE = 64;
+//const int DATA_SEGMENT_SIZE = 128 * KILOBYTE;
+//const int DATA_SEGMENT_SIZE = 1 * MEGABYTE;
+  // the chunk size that we attach.
+
+const int REPORTING_INTERVAL = 10 * SECOND_ms;
+  // this is the period between reports on how the test is going.
+
+//***this is where we are in testing the faux dual cpu problem.
+//***the longer delay shows the problem more easily.  the shorter delay
+//***is being used for a long test.
+const int MAXIMUM_ACQUISITION_DELAY = 8 * SECOND_ms;
+//const int MAXIMUM_ACQUISITION_DELAY = int(0.5 * SECOND_ms);
+  // the longest we'll snooze off waiting for pending receptions to occur.
+
+//const int MAXIMUM_PENDING_REQUESTS = 1;
+const int MAXIMUM_PENDING_REQUESTS = 108;
+  // this is a threshold for the number of requests; once hit, we start
+  // awaiting the responses.
+
+//const int PENDING_REQUESTS_FORCED = 3;
+//const int PENDING_REQUESTS_FORCED = 80;
+const int PENDING_REQUESTS_FORCED = MAXIMUM_PENDING_REQUESTS;
+  // when we've been forced to gather some pending responses to previous
+  // requests, this is how many we'll try to get at once.  numbers closer
+  // to the MAXIMUM_PENDING_REQUESTS will force more synchrony.
+
+const int CHANCE_OF_RECONSTRUCT = 14;
+  // how frequently a bus reconstruction occurs, in 1000.
+
+#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger(), s)
+#define BASE_LOG(s) EMERGENCY_LOG(program_wide_logger(), s)
+
+class cromp_client_tester : public application_shell
+{
+public:
+  cromp_client_tester();
+  ~cromp_client_tester();
+
+  virtual int execute();
+
+  IMPLEMENT_CLASS_NAME("cromp_client_tester");
+
+  void bite_server(basis::set<octopus_request_id> &ids,
+          basis::set<octopus_request_id> &delinquents, void *originator);
+    // performs the big chunk of testing.  the "ids" are the history of the
+    // sends that were made and they're managed by this method.  the
+    // "originator" is a tag we can use to generate unique print outs.
+
+  int print_instructions();
+    //!< shows how to use the command line options for this tester.
+
+  void grab_items();
+    //!< tries to retrieve things from the socket.
+
+  void cause_object_reconstruction();
+    //!< makes the cromp client object be zapped and redone.
+
+  void increment_thread_count() {
+    FUNCDEF("increment_thread_count");
+    auto_synchronizer l(*_lock);
+    _threads_active++;
+//LOG(isprintf("count now %d", _threads_active));
+  }
+
+  void decrement_thread_count() {
+    FUNCDEF("decrement_thread_count");
+    auto_synchronizer l(*_lock);
+    _threads_active--;
+//LOG(isprintf("count now %d", _threads_active));
+  }
+
+  void report(const time_stamp &start_time, double bytes_transmitted,
+          double conversations);
+    // describes how the test is going.
+
+private:
+  cromp_client *_uplink;  // provides the connection and transmission services.
+
+  mutex *_lock;  // protects the objects below.
+  int _threads_active;  // the number of transmitter threads running.
+  time_stamp _last_report;  // when we last reported on progress.
+  double _finished_loops;  // counts number of loops we've achieved.
+  bool _encryption;  // true if we're encrypting.
+  int _send_count;  //!< number of sends in this test.
+  int _thread_count;  //!< number of threads in test.
+  int _grabber_count;  //!< number of threads interfering with retrieve.
+  int _send_tries;  //!< how many times to try send if not all data got out.
+  int _checkpoint_count;  //!< items seen between printing of counter.
+  int _dataseg_size;  //!< size of bytes added to test packets.
+  int _report_interval;  //!< how frequently to show the report.
+  int _snooze_duration;  //!< time wasted between each send.
+  bool _rpc_style;  //!< true if emulating RPC and waiting for each item.
+  bool _reconstruct_object;  //!< true if we periodically tear down object.
+  internet_address _server_loc;  //!< holds onto the requested address.
+
+  void look_for_receipts(int count, basis::set<octopus_request_id> &ids,
+          basis::set<octopus_request_id> &delinquents, bool wait = false);
+    // attempts to get "count" items from the list of "ids".
+};
+
+////////////////////////////////////////////////////////////////////////////
+
+class bitey_thread : public ithread
+{
+public:
+  bitey_thread(cromp_client_tester &parent)
+      : ithread(), _parent(parent) {}
+
+  void perform_activity(void *formal(ptr)) {
+    FUNCDEF("perform_activity");
+    _parent.increment_thread_count();
+    _parent.bite_server(_ids, _delinquents, this);
+    _parent.decrement_thread_count();
+  }
+
+private:
+  cromp_client_tester &_parent;
+  basis::set<octopus_request_id> _ids;  // the ids for commands we've sent.
+  basis::set<octopus_request_id> _delinquents;  // missing ids during rcv.
+};
+
+////////////////////////////////////////////////////////////////////////////
+
+//hmmm: next stop; inject the types of items they're expecting in grab_items.
+
+class grabby_thread : public ithread
+{
+public:
+  grabby_thread(cromp_client_tester &parent)
+  : ithread(), _parent(parent) {}
+
+  void perform_activity(void *formal(ptr)) {
+    while (!should_stop()) {
+      _parent.grab_items();
+      if (_rando.inclusive(0, 100) > 10)
+        portable::sleep_ms(_rando.inclusive(5, 38));
+    }
+  }
+
+private:
+  cromp_client_tester &_parent;
+  chaos _rando;
+};
+
+////////////////////////////////////////////////////////////////////////////
+
+cromp_client_tester::cromp_client_tester()
+: application_shell("cromp_client_tester"),
+  _uplink(NIL),
+  _lock(new mutex),
+  _threads_active(0),
+  _finished_loops(0.0),
+  _encryption(false),
+  _send_count(0),
+  _thread_count(0),
+  _grabber_count(0),
+  _send_tries(0),
+  _checkpoint_count(0),
+  _dataseg_size(0),
+  _report_interval(0),
+  _snooze_duration(0),
+  _rpc_style(false),
+  _reconstruct_object(false),
+  _server_loc()
+{
+  FUNCDEF("constructor");
+  LOG("");
+  LOG("");
+
+  command_line args(__argc, __argv);
+//LOG(isprintf("argc is %d and first is %s", __argc, __argv[0]));
+
+  int indy = 0;
+  if (args.find("help", indy, false)
+      || (args.find("?", indy, false))
+      || (args.find('?', indy, false)) ) {
+    print_instructions();
+    exit(0);
+  }
+
+  // check for a port on the command line.
+  istring port_text;
+  int port = 5678;
+  if (args.get_value("port", port_text, false)) {
+    LOG(istring("using port: ") + port_text);
+    port = port_text.convert(5678);
+  }
+  _server_loc.port = port;
+
+//hmmm:normalize host so this can take either name or IP.
+
+  indy = 0;
+  if (args.find("encrypt", indy, false) || (args.find('e', indy, true)) ) {
+    // they're saying that we should encrypt the communication.
+    LOG("turning on encryption.");
+    _encryption = true;
+  }
+
+  indy = 0;
+  if (args.find("rpc", indy, false) || (args.find('R', indy, true)) ) {
+    // this is telling us to turn on RPC mode.  we will make each request and
+    // reply pair synchronous, i.e., each reply will be awaited for when a
+    // request has been made.
+    LOG("turning on RPC style requests.");
+    _rpc_style = true;
+  }
+
+  // check for a hostname on the command line.
+  istring hostname("local");
+  istring host_temp;
+  if (args.get_value("host", host_temp, false)) {
+    LOG(istring("using host: ") + host_temp);
+    hostname = host_temp;
+  }
+LOG(istring("using host: ") + hostname);
+  strcpy(_server_loc.hostname, hostname.s());
+
+  istring send_temp;
+  int send_count = MAXIMUM_SENDS;
+  if (args.get_value("sends", send_temp, false)) {
+    LOG(istring("using send count: ") + send_temp);
+    send_count = send_temp.convert(send_count);
+    if (send_count <= 0) send_count = 1;
+  }
+  _send_count = send_count;
+
+  istring thread_temp;
+  int thread_count = NUMBER_OF_THREADS;
+  if (args.get_value("threads", thread_temp, false)) {
+    LOG(istring("using thread count: ") + thread_temp);
+    thread_count = thread_temp.convert(thread_count);
+    if (thread_count <= 0) thread_count = 1;
+  }
+  _thread_count = thread_count;
+
+  istring grabber_temp;
+  int grabber_count = GRABBER_THREADS;
+  if (args.get_value("grab", grabber_temp, false)) {
+    LOG(istring("using grabber count: ") + grabber_temp);
+    grabber_count = grabber_temp.convert(grabber_count);
+    if (grabber_count < 0) grabber_count = 0;
+  }
+  _grabber_count = grabber_count;
+
+  istring send_tries_temp;
+  int send_tries = MAX_SEND_TRIES;
+  if (args.get_value("trysend", send_tries_temp, false)) {
+    LOG(istring("using send tries: ") + send_tries_temp);
+    send_tries = send_tries_temp.convert(send_tries);
+    if (send_tries < 0) send_tries = 0;
+  }
+  _send_tries = send_tries;
+
+//hmmm: how tiresome.  how about a macro here?  could help in general
+//      with command_line also.
+
+  istring checkpoint_temp;
+  int checkpoint_count = CHECKPOINT_SIZE;
+  if (args.get_value("print", checkpoint_temp, false)) {
+    LOG(istring("using checkpoint count: ") + checkpoint_temp);
+    checkpoint_count = checkpoint_temp.convert(checkpoint_count);
+    if (checkpoint_count <= 0) checkpoint_count = 1;
+  }
+  _checkpoint_count = checkpoint_count;
+
+  istring dataseg_temp;
+  int dataseg_size = DATA_SEGMENT_SIZE;
+  if (args.get_value("dataseg", dataseg_temp, false)) {
+    LOG(istring("using dataseg size: ") + dataseg_temp);
+    dataseg_size = dataseg_temp.convert(dataseg_size);
+    if (dataseg_size < 0) dataseg_size = 0;
+  }
+  _dataseg_size = dataseg_size;
+
+  istring report_temp;
+  int report_interval = REPORTING_INTERVAL;
+  if (args.get_value("report", report_temp, false)) {
+    LOG(istring("using report interval: ") + report_temp);
+    report_interval = report_temp.convert(report_interval);
+    if (report_interval <= 0) report_interval = 1;
+    report_interval *= SECOND_ms;  // convert to milliseconds.
+  }
+  _report_interval = report_interval;
+
+  istring snooze_temp;
+  int snooze_duration = 0;  // no snooze by default.
+  if (args.get_value("snooze", snooze_temp, false)) {
+    LOG(istring("using snooze duration: ") + snooze_temp);
+    snooze_duration = snooze_temp.convert(snooze_duration);
+    if (snooze_duration < 0) snooze_duration = 0;
+  }
+  _snooze_duration = snooze_duration;
+
+  if (args.find("reconstruct", indy, false)) {
+    LOG("saw reconstruct flag; will periodically tear down object.");
+    _reconstruct_object = true;
+  }
+
+LOG(istring("opening at ") + _server_loc.text_form());
+  _uplink = new cromp_client(_server_loc);
+
+  _uplink->add_tentacle(new bubbles_tentacle(false));
+//we don't need backgrounding right now.
+}
+
+cromp_client_tester::~cromp_client_tester()
+{
+  WHACK(_lock);
+  WHACK(_uplink);
+}
+
+int cromp_client_tester::print_instructions()
+{
+  istring name = filename(__argv[0]).basename().raw();
+  log(isprintf("%s usage:", name.s()));
+  log("");
+  log(isprintf("\
+This program connects to a cromp test server and exchanges packets to test\n\
+the performance of the cromp protocol.  All command line flags are optional\n\
+but can be added to specify how the test should be performed.  Currently,\n\
+the valid options are:\n\
+   --help\tShow this set of command-line help.\n\
+   -?\t\tditto.\n\
+   --port N\tConnect to the server on the port specified.\n\
+   --host X\tConnect to server at IP address or hostname X.\n\
+   --encrypt\tEncrypt the connection.  Server must do this also.\n\
+   -e\t\tditto.\n\
+   --sends N\tThe number of sends to perform.\n\
+   --threads N\tNumber of threads competing for single cromp link.\n\
+   --grab N\tNumber of additional threads stressing retrievals.\n\
+   --trysend N\tCount of tries for sending if not all data went out.\n\
+   --print N\tItems handled in between showing send counter.\n\
+   --dataseg N\tSize of extra data packed in each test packet.\n\
+   --report N\tDuration of time between reports, in seconds.\n\
+   --snooze N\tSleep N ms between each send; this invalidates timing info.\n\
+   --rpc\tEmulate Remote Procedure Call by awaiting each response.\n\
+   -R\t\tditto\n\
+"));
+  return -3;
+}
+
+void cromp_client_tester::look_for_receipts(int count,
+    basis::set<octopus_request_id> &ids,
+    basis::set<octopus_request_id> &delinquents, bool wait)
+{
+  FUNCDEF("look_for_receipts");
+  infoton *received = NIL;
+  while (count--) {
+    if (!ids.length()) break;  // nothing to check on.
+    octopus_request_id the_id = ids[0];
+    ids.zap(0, 0);  // take out the one we're inspecting right now.
+
+    time_stamp start_acquire;
+    int delay = MAXIMUM_ACQUISITION_DELAY;
+    if (wait) delay = 2 * MINUTE_ms;  // force a long delay.
+    outcome ret = _uplink->acquire(received, the_id, delay);
+    int acquire_duration = int(time_stamp().value() - start_acquire.value());
+    if (acquire_duration >= MAXIMUM_ACQUISITION_DELAY - 1) {
+      LOG("passed time limit for acquire!  this is the faux dual-cpu bug!");
+      LOG(isprintf("there were %d items left to acquire.", count));
+      LOG(isprintf("pending %d bytes to send, %d bytes accumulated.",
+          _uplink->pending_sends(), _uplink->accumulated_bytes()));
+      LOG(isprintf("the data bin had %d items awaiting pickup.",
+          _uplink->octo()->responses().items_held()));
+      if (ret != cromp_client::TIMED_OUT) {
+        LOG("cromp client lied about outcome??  didn't call this timed out!!");
+      }
+    }
+
+    if (ret != cromp_client::OKAY) {
+      if (ret != cromp_client::TIMED_OUT) {
+        LOG(istring("failed to acquire the response--got error ")
+                + cromp_client::outcome_name(ret));
+        // give it another chance later.
+        ids += the_id;
+LOG(isprintf("moved %s back to main id queue.", the_id.text_form().s()));
+      } else {
+        if (delinquents.member(the_id))
+          continuable_error(class_name(), func,
+              istring("a delinquent response is still missing: ")
+              + the_id.text_form());
+        // if we hadn't already seen it, we'll watch for it next time.
+        delinquents += the_id;
+LOG(isprintf("added %s to delinquents.", the_id.text_form().s()));
+      }
+      return;
+    }
+
+if (!received) {
+deadly_error(class_name(), func,
+"received packet was NIL even though outcome was OKAY!");
+}
+
+    // check that the right type is coming back to us.
+    bubble *cast = dynamic_cast<bubble *>(received);
+    if (!cast) {
+      continuable_error(class_name(), func, istring("got the wrong type "
+          "of response: ") + received->classifier().text_form());
+    }
+    // if we had a problem with this item earlier, we remove it since it
+    // succeeded this time.
+    if (delinquents.member(the_id))
+      delinquents.remove(the_id);
+    WHACK(received);
+  }
+}
+
+void cromp_client_tester::bite_server(basis::set<octopus_request_id> &ids,
+    basis::set<octopus_request_id> &delinquents,
+    void *originator)
+{
+  FUNCDEF("bite_server");
+  octopus_request_id cmd_id;
+
+///  LOG(timestamp(true, true) + " starting...");
+
+  outcome ret;
+
+  double overall_sent = 0;
+
+  // this computes the size of the exchange object with no extra data attached.
+  byte_array temp;
+  bubble test_size(_dataseg_size, screen_rectangle(0, 120, 220, 280),
+      238843);
+  test_size.data().reset();
+    // set the data segment to zero length.
+  test_size.pack(temp);
+  int base_length = temp.length();
+    // this is the base packed length of the bubble object.
+
+  int failure_count = 0;
+
+  time_stamp start;  // record when our testing started.
+
+  for (int sends = 1; sends <= _send_count; sends++) {
+    bubble to_send(_dataseg_size, screen_rectangle(0, 120, 220, 280),
+        238843);
+    int curr_sending = to_send.data_length() + base_length * 2;
+    overall_sent += curr_sending;
+      // we compute the overall sent by what's sent in the request (which is
+      // of the base length plus the attached array size) and the reply (which
+      // is the base length only since the server resets the data attachment).
+      // we go ahead and count it as sent before the send, since we're going
+      // to bomb out if the send doesn't work.
+    ret = _uplink->submit(to_send, cmd_id, _send_tries);
+    switch (ret.value()) {
+      case cromp_client::OKAY: {
+        // complete success in sending that chunk out.
+        ids.add(cmd_id);  // record it.
+        if (_rpc_style) {
+          // this call is used to force single requests and replies RPC style.
+          look_for_receipts(1, ids, delinquents, true);
+        }
+        // sleep if we were asked to.
+        if (_snooze_duration) {
+          _uplink->keep_alive_pause(_snooze_duration, 60);
+          look_for_receipts(1, ids, delinquents);
+        }
+        break;
+      }
+      case cromp_client::TOO_FULL: {
+//treating as failure right now.
+BASE_LOG("got too full outcome!");
+        sends--;
+        overall_sent -= curr_sending;
+        continue;
+        break;
+      }
+      case cromp_client::TIMED_OUT: {
+//treating as failure right now.
+BASE_LOG("got timed out outcome!");
+        sends--;
+        overall_sent -= curr_sending;
+        continue;
+        break;
+      }
+      default: {
+        // a failure case that we have no other handling for.
+        if (failure_count++ < 20) {
+          sends--;  // skip back for the failed one.
+          overall_sent -= curr_sending;  // remove unsent portion.
+          LOG(istring("got failure outcome ") + cromp_client::outcome_name(ret)
+              + " from attempt to submit request.");
+          if (_snooze_duration) {
+            _uplink->keep_alive_pause(_snooze_duration, 60);
+          }
+          continue;  // try again.
+        }
+        continuable_error(class_name(), func,
+            istring("failed to submit the request--got error ")
+                + cromp_client::outcome_name(ret));
+        break;
+      }
+    }
+
+    _finished_loops += 1.0;
+
+    if (ids.elements() > MAXIMUM_PENDING_REQUESTS) {
+      // grab some of the items waiting.  hopefully they're back by now.
+      look_for_receipts(PENDING_REQUESTS_FORCED, ids, delinquents);
+    }
+
+    if (! (sends % _checkpoint_count)) {
+      BASE_LOG(isprintf("%x send #%d", originator, sends));
+    }
+  }
+  BASE_LOG(isprintf("%x final send #%d", originator, _send_count));
+
+///  LOG(timestamp(true, true) + " done.");
+///  LOG(isprintf("sent %d items.", _send_count));
+
+  look_for_receipts(ids.elements(), ids, delinquents);
+
+  LOG(isprintf("concluded %d test requests and responses.", _send_count));
+}
+
+void cromp_client_tester::grab_items()
+{
+  FUNCDEF("grab_items");
+  octopus_request_id id(_uplink->entity(), -12);
+    // look for an id we don't expect to have any thing waiting for.
+  infoton *found = NIL;
+  outcome ret = _uplink->retrieve_and_restore(found, id, 0);
+  WHACK(found);
+}
+
+void cromp_client_tester::report(const time_stamp &start_time,
+    double bytes_transmitted, double conversations)
+{
+  FUNCDEF("report");
+  throughput_counter bandwidth;  // calculator for communication speed.
+  double duration = time_stamp().value() - start_time.value();
+    // the elapsed duration so far.
+  bandwidth.add_run(bytes_transmitted, duration, conversations * 2);
+    // create a portrait of how the run has progressed.  we multiply the
+    // conversations by two since we are counting both the request and the
+    // response (send and receive) as a transfer.
+
+  // calculate the number of bytes per item for real as it plays out in
+  // cromp sending.
+  double bytes_per_item = bandwidth.bytes_sent() / bandwidth.number_of_sends();
+
+  bubble my_bubble(_dataseg_size);  // an exemplar for our sends.
+
+  // calculate how much space bubble's naming takes up.
+  byte_array packed_classifier;
+  basis::pack(packed_classifier, my_bubble.classifier());
+  double classifier_size = packed_classifier.length() - sizeof(int);
+    // that's how much space is used by our goofy classifier name.  there are
+    // a few bytes extra overhead for packing a string array and we remove
+    // them from consideration; we only want credit for the name, since that
+    // is not truly overhead, given that the bubble infoton chose it.
+  double payload_portion = my_bubble.packed_size() + classifier_size;
+    // calculate the portion of our transmissions that are solely the
+    // result of what we are putting into the package.
+  double overhead = bytes_per_item - payload_portion;
+    // okay, this is how many bytes per item is cromp noise, rather than
+    // something the user is responsible for.
+  double percent_overhead = overhead / bytes_per_item;
+
+// change 0 to 1 to enable this section of information.
+#if 0
+  // get additional facts about how much of a packed infoton is wasted.
+  byte_array packed_infote;
+  infoton::fast_pack(packed_infote, my_bubble);
+  log(isprintf("sane? -- overhead for just packed infoton is %d bytes.",
+      packed_infote.length() - payload_portion));
+  octopus_request_id example_request(_uplink->entity(), 23982);
+  byte_array packed_req_id;
+  example_request.pack(packed_req_id);
+  log(isprintf("  -- overhead for octo request id is %d bytes.",
+      packed_req_id.length()));
+  byte_array packed_transa;
+  cromp_transaction::flatten(packed_transa, my_bubble,
+      octopus_request_id(_uplink->entity(), 23982));
+  log(isprintf("  -- overhead for cromp transation is %d bytes.",
+      packed_transa.length() - payload_portion));
+#endif
+
+  BASE_LOG(isprintf("sent %.0f items, %.0f bytes, %.0f bytes per item,%s"
+      "payload %.0f bytes, overhead %.0f bytes, percent overhead %.1f%%,%s"
+      "in %.2f seconds is %f ms/item%s"
+      "at %.2f %cb/sec & %.2f items/sec.",
+      bandwidth.number_of_sends(), bandwidth.bytes_sent(),
+      bytes_per_item,
+      log_base::platform_ending(),
+      payload_portion, overhead, percent_overhead * 100.0,
+      log_base::platform_ending(),
+      bandwidth.total_time() / SECOND_ms,
+      bandwidth.total_time() / bandwidth.number_of_sends(),
+      log_base::platform_ending(),
+      (bandwidth.kilobytes_per_second() < 1024.0? 
+        bandwidth.kilobytes_per_second()
+        : bandwidth.megabytes_per_second()),
+      (bandwidth.kilobytes_per_second() < 1024.0? 'K' : 'M'),
+      bandwidth.number_of_sends() / (bandwidth.total_time() / SECOND_ms)));
+}
+
+void cromp_client_tester::cause_object_reconstruction()
+{
+  FUNCDEF("cause_object_reconstruction");
+  int rando = chaos().inclusive(1, 100);
+  if (rando > CHANCE_OF_RECONSTRUCT) return;  // not doing it this time.
+  LOG(istring("reconstructing client at ") + _server_loc.text_form());
+//below is not good when multiple threads are allowed to romp on client.
+////  WHACK(_uplink);
+////  _uplink = new cromp_client(_server_loc);
+  
+  _uplink->disconnect();
+  outcome ret = common::INVALID;
+  int counter = 100;  // allowed this many times to try to reconnect.
+  while ( (ret != common::OKAY) && (counter-- >= 0) ) {
+    ret = _uplink->connect();
+    if (ret != cromp_client::OKAY) {
+      LOG(istring("couldn't reconnect this time: ")
+          + cromp_client::outcome_name(ret));
+      portable::sleep_ms(420);
+    }
+  }
+}
+
+int cromp_client_tester::execute()
+{
+  FUNCDEF("execute");
+
+  // testing that crompish pax are done right.
+  bubble fud(randomizer().inclusive(12, 2829));
+  byte_array packed_fud;
+  fud.pack(packed_fud);
+  if (packed_fud.length() != fud.packed_size())
+    deadly_error(class_name(), func, "bubble's packed size method is wrong.");
+
+  if (_encryption) _uplink->enable_encryption();
+
+  outcome ret = _uplink->connect();
+  if (ret != cromp_client::OKAY) {
+    deadly_error(class_name(), func, istring("connection failed with error: ")
+        + cromp_client::outcome_name(ret));
+  }
+
+  thread_cabinet cab;  // we store a bunch of threads here.
+
+  LOG(isprintf("adding %d grabber threads to test.", _grabber_count));
+
+  // create the extra grabber threads.
+  for (int i = 0; i < _grabber_count; i++) {
+    grabby_thread *to_add = new grabby_thread(*this);
+    cab.add_thread(to_add, false, NIL);
+  }
+
+  LOG(isprintf("adding %d transmitter threads to test.", _thread_count));
+
+  // create the specified number of threads.
+  for (int j = 0; j < _thread_count; j++) {
+    bitey_thread *to_add = new bitey_thread(*this);
+    cab.add_thread(to_add, false, NIL);
+  }
+
+//LOG("starting all threads...");
+  time_stamp start;
+  cab.start_all(NIL);
+//LOG("done starting threads...");
+
+  portable::sleep_ms(400);  // wait until a few get cranked up.
+
+//LOG("did our initial sleep...");
+
+  while (cab.any_running()) {
+    portable::sleep_ms(30);
+    if (!_threads_active) {
+      break;
+    }
+//LOG("main loop...");
+    if (time_stamp(-_report_interval) > _last_report) {
+      report(start, cromp_common::total_bytes_sent()
+          + cromp_common::total_bytes_received(),
+          _finished_loops);
+      _last_report.reset();
+    }
+    if (_reconstruct_object) {
+      cause_object_reconstruction();
+    }
+    if (!_uplink->connected()) {
+      LOG("connection dropped.  trying to connect again.");
+      outcome ret = _uplink->connect();
+      if (ret != cromp_client::OKAY) {
+        // snooze a bit so as not to drive server crazy or log too much noise.
+        portable::sleep_ms(10 * SECOND_ms);
+      }
+    }
+  }
+
+  LOG("- done testing -");
+
+  if (_finished_loops != double(_thread_count) * _send_count)
+    LOG(isprintf("number of loops was calculated differently: wanted %d, "
+        "got %d", _thread_count * _send_count, _finished_loops));
+
+  report(start, cromp_common::total_bytes_sent()
+      + cromp_common::total_bytes_received(),
+      _thread_count * _send_count);
+
+//LOG("stopping all threads...");
+  cab.stop_all();
+  LOG("all threads exited.");
+
+#ifdef DEBUG_TESTER
+///  LOG("hit a key to continue...");
+///  int char_read = fgetc(stdin);
+#endif
+
+  BASE_LOG("cromp_client:: works for those functions tested.");
+
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+HOOPLE_MAIN(cromp_client_tester, )
+
diff --git a/octopi/library/tests_cromp/test_cromp_server.cpp b/octopi/library/tests_cromp/test_cromp_server.cpp
new file mode 100644 (file)
index 0000000..e2a5da9
--- /dev/null
@@ -0,0 +1,220 @@
+/*****************************************************************************\
+*                                                                             *
+*  Name   : test_cromp_server                                                 *
+*  Author : Chris Koeritz                                                     *
+*                                                                             *
+*******************************************************************************
+* 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 "crompish_pax.h"
+
+#include <basis/byte_array.h>
+#include <basis/function.h>
+#include <basis/istring.h>
+#include <basis/log_base.h>
+#include <basis/portable.h>
+#include <cromp/cromp_server.h>
+#include <mechanisms/time_stamp.h>
+#include <octopus/tentacle.h>
+#include <opsystem/application_shell.h>
+#include <opsystem/command_line.h>
+#include <loggers/console_logger.h>
+#include <loggers/file_logger.h>
+#include <data_struct/static_memory_gremlin.h>
+#include <sockets/address.h>
+#include <sockets/machine_uid.h>
+#include <sockets/tcpip_stack.h>
+
+#define LOG(a) CLASS_EMERGENCY_LOG(program_wide_logger(), a)
+
+const int REPORTING_INTERVAL = 28 * SECOND_ms;  // how often to squawk.
+
+//const int ACCEPTING_THREADS = 5;
+const int ACCEPTING_THREADS = 1;
+
+// specifies whether to support lazy evaluation by tentacles.
+//const bool SUPPORT_BACKGROUNDING = true;
+const bool SUPPORT_BACKGROUNDING = false;
+
+// determines whether the octopus will do immediate evaluation or not.
+//const bool IMMEDIATE_EVALUATION = true;
+const bool IMMEDIATE_EVALUATION = false;
+
+////////////////////////////////////////////////////////////////////////////
+
+// forward.
+class cromp_server_tester;
+
+class our_cromp_server : public cromp_server
+{
+public:
+  our_cromp_server(cromp_server_tester &parent, const internet_address &addr)
+  : cromp_server(addr, ACCEPTING_THREADS, IMMEDIATE_EVALUATION),
+    _parent(parent) {}
+
+  ~our_cromp_server() {}
+
+  IMPLEMENT_CLASS_NAME("our_cromp_server");
+
+private:
+  cromp_server_tester &_parent;
+};
+
+////////////////////////////////////////////////////////////////////////////
+
+class cromp_server_tester : public application_shell
+{
+public:
+  bool _saw_clients;  // true if we ever got a connection.
+
+  cromp_server_tester();
+  ~cromp_server_tester();
+
+  virtual int execute();
+
+  IMPLEMENT_CLASS_NAME("cromp_server_tester");
+
+private:
+  our_cromp_server *_uplink;
+    // provides the connection and transmission services.
+  bool _leave_when_no_clients;  // true if we should just do one run.
+  bool _encryption;  // true if we should do an encrypted connection.
+  internet_address c_address;
+};
+
+////////////////////////////////////////////////////////////////////////////
+
+class real_bubbles_tentacle : public bubbles_tentacle
+{
+public:
+  real_bubbles_tentacle(cromp_server_tester &parent, bool backgrounding)
+      : bubbles_tentacle(backgrounding), _parent(parent) {}
+
+  virtual outcome consume(infoton &to_chow, const octopus_request_id &item_id,
+      byte_array &transformed)
+  {
+    transformed.reset();
+    _parent._saw_clients = true;  // we saw someone.
+//LOG("got to our cromp's bubble tentacle.");
+    bubble *inf = dynamic_cast<bubble *>(&to_chow);
+    if (!inf) return NO_HANDLER;
+//LOG("caching product!  success getting unpacked etc.");
+    bubble *junk = (bubble *)inf->clone();
+    store_product(junk, item_id);
+    return OKAY;
+  }
+
+private:
+  cromp_server_tester &_parent;
+};
+
+////////////////////////////////////////////////////////////////////////////
+
+cromp_server_tester::cromp_server_tester()
+: application_shell("cromp_server_tester"),
+  _saw_clients(false),
+  _uplink(NIL),
+  _leave_when_no_clients(false),
+  _encryption(false)
+{
+  FUNCDEF("constructor");
+  SET_DEFAULT_COMBO_LOGGER;
+  LOG("");
+  LOG("");
+
+  command_line args(__argc, __argv);
+  // check for a port on the command line.
+  istring port_text;
+  int port = 5678;
+  if (args.get_value("port", port_text, false)) {
+    LOG(istring("using port: ") + port_text);
+    port = port_text.convert(5678);
+  }
+  int posn = 0;
+  if (args.find("exit", posn)) {
+    LOG("seeing the 'exit without clients' flag set.");
+    _leave_when_no_clients = true;
+  }
+
+//hmmm:normalize host so this can take either name or IP.
+
+  // check for a hostname on the command line.
+  istring hostname("local");
+  istring host_temp;
+  if (args.get_value("host", host_temp, false)) {
+    LOG(istring("using host: ") + host_temp);
+    hostname = host_temp;
+  }
+  strcpy(c_address.hostname, hostname.s());
+
+//LOG(istring("here's the command line:") + log_base::platform_ending() + args.text_form());
+
+  int indy = 0;
+  if (args.find("encrypt", indy) || (args.find('e', indy)) ) {
+    // they're saying that we should encrypt the communication.
+    LOG("turning on encryption.");
+    _encryption = true;
+  }
+
+//  tcpip_stack stack;
+//  machine_uid host = stack.this_host(machine_uid::TCPIP_LOCATION);
+  c_address.port = port;
+//  host.native().stuff(internet_address::ADDRESS_SIZE, c_address.ip_address);
+  LOG("starting cromp_server");
+  _uplink = new our_cromp_server(*this, c_address);
+  _uplink->add_tentacle(new real_bubbles_tentacle(*this, SUPPORT_BACKGROUNDING));
+  _uplink->enable_servers(_encryption);
+}
+
+cromp_server_tester::~cromp_server_tester()
+{
+  WHACK(_uplink);
+}
+
+int cromp_server_tester::execute()
+{
+  FUNCDEF("execute");
+
+  time_stamp next_report(REPORTING_INTERVAL);
+
+  while (true) {
+    // make sure we didn't see our exit condition.
+    if (!_uplink->clients() && _leave_when_no_clients && _saw_clients) {
+      LOG("exiting now");
+      break;
+    }
+
+    if (time_stamp() > next_report) {
+      int client_count = _uplink->clients();
+      const char *verb = "are";
+      if (client_count == 1) verb = "is";
+      const char *ending = "s";
+      if (client_count == 1) ending = "";
+      LOG(isprintf("There %s %d client%s.", verb, client_count, ending));
+      next_report.reset(REPORTING_INTERVAL);
+    }
+
+// new test case; rip out server while still possibly connected.
+if (randomizer().inclusive(1, 200) == 32) {
+LOG("tearing down entire server and re-creating.");
+WHACK(_uplink);
+_uplink = new our_cromp_server(*this, c_address);
+_uplink->add_tentacle(new real_bubbles_tentacle(*this, SUPPORT_BACKGROUNDING));
+_uplink->enable_servers(_encryption);
+}
+
+    portable::sleep_ms(100); 
+  }
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+HOOPLE_MAIN(cromp_server_tester, )
+
diff --git a/octopi/library/tests_cromp/test_many_cromp.cpp b/octopi/library/tests_cromp/test_many_cromp.cpp
new file mode 100644 (file)
index 0000000..bd64ff7
--- /dev/null
@@ -0,0 +1,181 @@
+/*****************************************************************************\
+*                                                                             *
+*  Name   : test_many_cromp                                                   *
+*  Author : Chris Koeritz                                                     *
+*                                                                             *
+*  Purpose:                                                                   *
+*                                                                             *
+*    Sees how a cromp server does with a large number of connections.         *
+*                                                                             *
+*******************************************************************************
+* 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 "crompish_pax.h"
+
+#include <basis/chaos.h>
+#include <basis/istring.h>
+#include <basis/portable.h>
+#include <data_struct/amorph.h>
+#include <cromp/cromp_client.h>
+#include <octopus/entity_defs.h>
+#include <octopus/infoton.h>
+#include <opsystem/application_shell.h>
+#include <opsystem/command_line.h>
+#include <loggers/console_logger.h>
+#include <loggers/file_logger.h>
+#include <data_struct/static_memory_gremlin.h>
+#include <sockets/address.h>
+
+#include <stdio.h>
+
+#define DEBUG_TESTER
+  // uncomment for noisier version.
+
+const int REPORTING_INTERVAL = 20 * SECOND_ms;
+  // how frequently we tell about bad crompers.
+
+#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger(), s)
+#define BASE_LOG(s) EMERGENCY_LOG(program_wide_logger(), s)
+
+class many_cromp_tester : public application_shell
+{
+public:
+  many_cromp_tester();
+  ~many_cromp_tester();
+
+  virtual int execute();
+
+  IMPLEMENT_CLASS_NAME("many_cromp_tester");
+
+private:
+  amorph<cromp_client> _uplinks;  // a list of cromp clients.
+  bool _encryption;  // true if we're encrypting.
+  int _count;  // number of cromps.
+};
+
+////////////////////////////////////////////////////////////////////////////
+
+many_cromp_tester::many_cromp_tester()
+: application_shell("many_cromp_tester"),
+  _uplinks(),
+  _encryption(false),
+  _count(1)
+{
+  FUNCDEF("constructor");
+  LOG("");
+  LOG("");
+
+  internet_address server_loc;
+
+  command_line args(__argc, __argv);
+//LOG(isprintf("argc is %d and first is %s", __argc, __argv[0]));
+
+  // check for a port on the command line.
+  istring port_text;
+  int port = 5678;
+  if (args.get_value("port", port_text, false)) {
+    LOG(istring("using port: ") + port_text);
+    port = port_text.convert(5678);
+  }
+  server_loc.port = port;
+
+  istring count_text;
+  if (args.get_value("count", count_text, false)) {
+    LOG(istring("using count: ") + count_text);
+    _count = count_text.convert(_count);
+  }
+
+//hmmm: normalize host so this can take either name or IP.
+
+  int indy = 0;
+  if (args.find("encrypt", indy, false)
+      || (args.find('e', indy, false)) ) {
+    // they're saying that we should encrypt the communication.
+    _encryption = true;
+  }
+
+  // check for a hostname on the command line.
+  istring hostname("local");
+  istring host_temp;
+  if (args.get_value("host", host_temp, false)) {
+    LOG(istring("using host: ") + host_temp);
+    hostname = host_temp;
+  }
+LOG(istring("using host: ") + hostname);
+  strcpy(server_loc.hostname, hostname.s());
+
+LOG(istring("opening at ") + server_loc.text_form());
+
+LOG(isprintf("count of %d cromps will be created.", _count));
+
+  for (int i = 0; i < _count; i++) {
+LOG(isprintf("%d. A", i));
+    cromp_client *uplink = new cromp_client(server_loc);
+LOG(isprintf("%d. B", i));
+    uplink->add_tentacle(new bubbles_tentacle(false));
+LOG(isprintf("%d. C", i));
+    _uplinks.append(uplink);
+  }
+
+}
+
+many_cromp_tester::~many_cromp_tester()
+{
+}
+
+int many_cromp_tester::execute()
+{
+  FUNCDEF("execute");
+
+  if (_encryption) {
+    for (int i = 0; i < _uplinks.elements(); i++) {
+      _uplinks.borrow(i)->enable_encryption();
+    }
+  }
+
+  for (int i = 0; i < _uplinks.elements(); i++) {
+    outcome ret = _uplinks.borrow(i)->connect();
+    if (ret != cromp_client::OKAY) {
+      deadly_error(class_name(), func, istring("connection failed with error: ")
+          + cromp_client::outcome_name(ret));
+    }
+  }
+
+time_stamp when_to_leave(10 * HOUR_ms);
+
+  time_stamp next_report(REPORTING_INTERVAL);
+
+  while (time_stamp() < when_to_leave) {
+    int unconnected = 0;
+    for (int i = 0; i < _uplinks.elements(); i++) {
+      if (!_uplinks.borrow(i)->connected())
+        unconnected++;
+    }
+
+    if (time_stamp() > next_report) {
+      int connected = _uplinks.elements() - unconnected;
+      LOG(isprintf("[ %d connected and %d did not ]", connected, unconnected));
+      next_report.reset(REPORTING_INTERVAL);
+    }
+
+//do something with uplinks.
+
+    portable::sleep_ms(100);
+  }
+
+
+  BASE_LOG("many cromp_client:: works for those functions tested.");
+
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+HOOPLE_MAIN(many_cromp_tester, )
+
diff --git a/octopi/library/tests_cromp/version.ini b/octopi/library/tests_cromp/version.ini
new file mode 100644 (file)
index 0000000..4cf4fbc
--- /dev/null
@@ -0,0 +1,6 @@
+[version]
+description=CROMP Entity Decoder
+root=cromp_decoder
+name=CROMP Decoder
+extension=exe
+
diff --git a/octopi/library/tests_octopus/t_bin.cpp b/octopi/library/tests_octopus/t_bin.cpp
deleted file mode 100644 (file)
index 74c8509..0000000
+++ /dev/null
@@ -1,187 +0,0 @@
-/*****************************************************************************\
-*                                                                             *
-*  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(NULL_POINTER);
-      }
-    }
-    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(NULL_POINTER);
-        }
-        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(NULL_POINTER);
-        }
-        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(NULL_POINTER);
-        }
-        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
deleted file mode 100644 (file)
index e939c9b..0000000
+++ /dev/null
@@ -1,337 +0,0 @@
-/*****************************************************************************\
-*                                                                             *
-*  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 = NULL_POINTER;
-    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(NULL_POINTER);
-  }
-
-  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
deleted file mode 100644 (file)
index d924098..0000000
+++ /dev/null
@@ -1,133 +0,0 @@
-/*****************************************************************************\
-*                                                                             *
-*  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
deleted file mode 100644 (file)
index 9deb90a..0000000
+++ /dev/null
@@ -1,187 +0,0 @@
-/*****************************************************************************\
-*                                                                             *
-*  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
deleted file mode 100644 (file)
index eb7dd35..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-/*****************************************************************************\
-*                                                                             *
-*  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
deleted file mode 100644 (file)
index 4ce733b..0000000
+++ /dev/null
@@ -1,184 +0,0 @@
-/*****************************************************************************\
-*                                                                             *
-*  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 = NULL_POINTER;
-    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 = NULL_POINTER;  // 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
deleted file mode 100644 (file)
index 1ad69a8..0000000
+++ /dev/null
@@ -1,396 +0,0 @@
-/*****************************************************************************\
-*                                                                             *
-*  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 = NULL_POINTER;
-    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 NULL_POINTER;
-      }
-      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 NULL_POINTER;
-      }
-      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 = NULL_POINTER;
-    _addron = NULL_POINTER;
-  }
-
-  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 = NULL_POINTER;
-//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, )
-
diff --git a/octopi/library/tests_octopus/test_bin.cpp b/octopi/library/tests_octopus/test_bin.cpp
new file mode 100644 (file)
index 0000000..74c8509
--- /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(NULL_POINTER);
+      }
+    }
+    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(NULL_POINTER);
+        }
+        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(NULL_POINTER);
+        }
+        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(NULL_POINTER);
+        }
+        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/test_bin_threaded.cpp b/octopi/library/tests_octopus/test_bin_threaded.cpp
new file mode 100644 (file)
index 0000000..e939c9b
--- /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 = NULL_POINTER;
+    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(NULL_POINTER);
+  }
+
+  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/test_entity.cpp b/octopi/library/tests_octopus/test_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/test_file_transfer.cpp b/octopi/library/tests_octopus/test_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/test_identity.cpp b/octopi/library/tests_octopus/test_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/test_security.cpp b/octopi/library/tests_octopus/test_security.cpp
new file mode 100644 (file)
index 0000000..4ce733b
--- /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 = NULL_POINTER;
+    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 = NULL_POINTER;  // 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/test_unpacker.cpp b/octopi/library/tests_octopus/test_unpacker.cpp
new file mode 100644 (file)
index 0000000..1ad69a8
--- /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 = NULL_POINTER;
+    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 NULL_POINTER;
+      }
+      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 NULL_POINTER;
+      }
+      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 = NULL_POINTER;
+    _addron = NULL_POINTER;
+  }
+
+  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 = NULL_POINTER;
+//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, )
+