Merge branch 'master' of feistymeow.org:feisty_meow
[feisty_meow.git] / nucleus / library / tests_basis / test_array.cpp
1 /*****************************************************************************\
2 *                                                                             *
3 *  Name   : test_array                                                        *
4 *  Author : Chris Koeritz                                                     *
5 *                                                                             *
6 *******************************************************************************
7 * Copyright (c) 1991-$now By Author.  This program is free software; you can  *
8 * redistribute it and/or modify it under the terms of the GNU General Public  *
9 * License as published by the Free Software Foundation; either version 2 of   *
10 * the License or (at your option) any later version.  This is online at:      *
11 *     http://www.fsf.org/copyleft/gpl.html                                    *
12 * Please send any updates to: fred@gruntose.com                               *
13 \*****************************************************************************/
14
15 //#define DEBUG_ARRAY
16   // when this flag is turned on, extra checking will be done in the allocator.
17
18 #include <application/hoople_main.h>
19 #include <basis/array.h>
20 #include <basis/byte_array.h>
21 #include <basis/enhance_cpp.h>
22 #include <basis/functions.h>
23 #include <basis/astring.h>
24 #include <loggers/console_logger.h>
25 #include <loggers/critical_events.h>
26 #include <mathematics/chaos.h>
27 #include <mathematics/double_plus.h>
28 #include <timely/time_stamp.h>
29 #include <unit_test/unit_base.h>
30
31 ///#include <stdio.h>//temp
32
33 using namespace application;
34 using namespace basis;
35 using namespace loggers;
36 using namespace mathematics;
37 using namespace timely;
38 using namespace unit_test;
39
40 //const float MAX_TEST_DURATION_ms = 28 * SECOND_ms;
41 const float MAX_TEST_DURATION_ms = 200;
42   // each of the tests calling on the templated tester will take this long.
43
44 const int MAX_SIMULTANEOUS_OBJECTS = 42;  // the maximum arrays tested.
45
46 const int MIN_OBJECT = -30; // the smallest array we'll create.
47 const int MAX_OBJECT = 98; // the largest array we'll create.
48
49 const int MIN_BLOCK = 100;  // the smallest exemplar we'll use.
50 const int MAX_BLOCK = MAX_OBJECT * 2;  // the largest exempler.
51
52 //#define DEBUG_TEST_ARRAY
53   // uncomment for noisy version.
54
55 #define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s)
56
57 static chaos a_randomizer;
58
59 //////////////
60
61 class test_array : public application_shell, public unit_base
62 {
63 public:
64   test_array()
65   : application_shell(),
66     unit_base()
67   {
68 //hmmm: should go into app shell
69 ////SETUP_CONSOLE_LOGGER;
70 }
71   DEFINE_CLASS_NAME("test_array");
72   virtual int execute();
73   void dump_array(array<void *> &ar, const char *name);
74   void test_arrays_of_void_pointer();
75   void test_iteration_speed();
76
77   template <class contents>
78   void array_tester(test_array &ta, const contents &formal(bogus), basis::un_short flags);
79
80 };
81
82 //////////////
83
84 void test_array::dump_array(array<void *> &ar, const char *name)
85 {
86 #ifdef DEBUG_TEST_ARRAY
87   FUNCDEF("dump_array");
88   LOG(a_sprintf("array named \"%s\" has:", name)); 
89   for (int i = 0; i < ar.length(); i++) {
90     LOG(a_sprintf("\t%4d: %d", i, (int)ar[i]));
91   }
92 #else
93   if (ar.length() && name) {}
94 #endif
95 }
96
97 void test_array::test_arrays_of_void_pointer()
98 {
99   FUNCDEF("void pointer test");
100   const int MAX_VOID_ARRAY = 20;
101   array<void *> argh(MAX_VOID_ARRAY, NULL_POINTER, byte_array::SIMPLE_COPY
102       | byte_array::EXPONE | byte_array::FLUSH_INVISIBLE);
103   array<void *> argh2(argh);
104   ASSERT_EQUAL(argh.length(), MAX_VOID_ARRAY, "check first array length");
105   ASSERT_EQUAL(argh2.length(), MAX_VOID_ARRAY, "check copied array length");
106   int wrong_counter = 0;
107   for (int o = 0; o < MAX_VOID_ARRAY; o++)
108     if (argh[o] != argh2[o]) wrong_counter++;
109   ASSERT_EQUAL(wrong_counter, 0, "compare array contents");
110
111   // fill it with values.
112   int starter;
113   for (int i = 0; i < MAX_VOID_ARRAY; i++)
114     argh[i] = (void *)(&starter + i);
115   dump_array(argh, "first version");
116
117   // check the values.
118   wrong_counter = 0;
119   for (int j = 0; j < MAX_VOID_ARRAY; j++)
120     if (argh[j] != (void *)(&starter + j) ) wrong_counter++;
121   ASSERT_EQUAL(wrong_counter, 0, "compare values that were set");
122
123   // add a constant to the values.
124   for (int k = 0; k < MAX_VOID_ARRAY; k++)
125     argh[k] = (void *)((int *)argh[k] + 23);
126   dump_array(argh, "second version");
127
128   // check assignment.
129   argh2 = argh;
130   wrong_counter = 0;
131   for (int n = 0; n < MAX_VOID_ARRAY; n++)
132     if (argh2[n] != (void *)(&starter + n + 23)) wrong_counter++;
133   ASSERT_EQUAL(wrong_counter, 0, "compare values that were assigned");
134
135   // now test that values are kept in place after rearrangement.
136   argh.zap(3, 4);
137   dump_array(argh, "third version");
138   wrong_counter = 0;
139   for (int l = 0; l < 3; l++)
140     if (argh[l] != (void *)(&starter + l + 23)) wrong_counter++;
141   ASSERT_EQUAL(wrong_counter, 0, "zap low values test");
142   wrong_counter = 0;
143   for (int m = 3; m < MAX_VOID_ARRAY - 2; m++)
144     if (argh[m] != (void *)(&starter + m + 2 + 23)) wrong_counter++;
145   ASSERT_EQUAL(wrong_counter, 0, "zap high values test");
146 //hmmm: add more tests if void pointer arrays seem in question.
147 }
148
149 //////////////
150
151 static astring blank_string;
152
153 // jethro is a simple object for containment below.
154 class jethro
155 {
156 public:
157   jethro(const astring &i = blank_string) : _truck(i) {}
158
159   astring _truck;
160
161   bool operator ==(const jethro &tc) const { return tc._truck == _truck; }
162 };
163
164 //////////////
165
166 // the test_content object is an object with proper copy constructor
167 // and assignment operator that also has deep contents.
168 class test_content
169 {
170 public:
171   abyte _q;
172   astring _ted;
173   astring _jed;
174   int_array _ned;
175
176   test_content(abyte q = 3)
177   : _q(q), _ted("bl"), _jed("orp"),
178     _ned(12, NULL_POINTER)
179 /*    _med(3, 2),
180     _red(2, 4) */
181   {
182     for (int i = 0; i < _ned.length(); i++)
183       _ned[i] = -i;
184 /*
185     for (int r = 0; r < _med.rows(); r++)
186       for (int c = 0; c < _med.columns(); c++)
187         _med[r][c] = jethro(astring((r*c) % 256, 1));
188     for (int j = 0; j < _red.rows(); j++)
189       for (int k = 0; k < _red.columns(); k++)
190         _red[j][k] = j * k;
191 */
192   }
193
194   bool operator ==(const test_content &tc) const {
195     if (tc._q != _q) return false;
196     if (tc._ted != _ted) return false;
197     if (tc._jed != _jed) return false;
198     if (tc._ned.length() != _ned.length()) return false;
199     for (int i = 0; i < _ned.length(); i++)
200       if (tc._ned[i] != _ned[i]) return false;
201     
202 /*
203     if (tc._med.rows() != _med.rows()) return false;
204     if (tc._med.columns() != _med.columns()) return false;
205     for (int c = 0; c < _med.columns(); c++)
206       for (int r = 0; r < _med.rows(); r++)
207         if (tc._med.get(r, c) != _med.get(r, c)) return false;
208
209     if (tc._red.rows() != _red.rows()) return false;
210     if (tc._red.columns() != _red.columns()) return false;
211     for (int j = 0; j < _red.rows(); j++)
212       for (int k = 0; k < _red.columns(); k++)
213         if (tc._red.get(j, k) != _red.get(j, k)) return false;
214 */
215
216     return true;
217   }
218
219   operator abyte() const { return _q; }
220 };
221
222 //////////////
223
224 template <class contents>
225 bool compare_arrays(const array<contents> &a, const array<contents> &b)
226 {
227   if (a.length() != b.length()) return false;
228   for (int i = 0; i < a.length(); i++)
229     if (a[i] != b[i]) return false;
230   return true;
231 }
232
233 //////////////
234
235 // this is a templated test for arrays, with some requirements.  the contents
236 // object must support a constructor that takes a simple byte, whether
237 // that's meaningful for the object or not.  if your type to test doesn't
238 // have that, derive a simple object from it, give it that constructor, and
239 // then it can be used below.  the object must also support comparison with
240 // == and != for this test to be used.  it must also provide a conversion
241 // to integer that returns the value passed to the constructor.
242
243 template <class contents>
244 void test_array::array_tester(test_array &ta, const contents &formal(bogus), basis::un_short flags)
245 {
246   FUNCDEF("array_tester");
247   // the space that all training for arrays comes from.
248   contents *junk_space = new contents[MAX_OBJECT + MAX_BLOCK];
249   for (int i = 0; i < MAX_OBJECT - 1; i++)
250     junk_space[i] = contents(a_randomizer.inclusive('a', 'z'));
251   junk_space[MAX_OBJECT + MAX_BLOCK - 1] = '\0';
252
253   array<contents> *testers[MAX_SIMULTANEOUS_OBJECTS];
254   for (int c = 0; c < MAX_SIMULTANEOUS_OBJECTS; c++) {
255     // set up the initial array guys.
256     testers[c] = new array<contents>(a_randomizer.inclusive(MIN_OBJECT, MAX_OBJECT),
257         NULL_POINTER, flags);
258     // copy the randomized junk space into the new object.
259     for (int i = 0; i < testers[c]->length(); i++)
260       testers[c]->put(i, junk_space[i]);
261   }
262
263   // these are the actions we try out with the array during the test.
264   // the first and last elements must be identical to the first and last
265   // tests to perform.
266   enum actions { first, do_train = first, do_size, do_assign,
267       do_access, do_zap, do_resizer, do_shrink, do_reset, do_indices,
268       do_concatenating, do_concatenating2, do_subarray, do_insert,
269       do_overwrite, do_stuff, do_memory_paring, do_snarf, do_flush,
270       do_observe, last = do_observe };
271
272   time_stamp exit_time(MAX_TEST_DURATION_ms);
273   while (time_stamp() < exit_time) {
274     int index = a_randomizer.inclusive(0, MAX_SIMULTANEOUS_OBJECTS - 1);
275     int choice = a_randomizer.inclusive(first, last);
276     switch (choice) {
277       case do_train: {
278 #ifdef DEBUG_TEST_ARRAY
279         LOG(a_sprintf("do_train"));
280 #endif
281         int new_size = a_randomizer.inclusive(MIN_OBJECT, MAX_OBJECT);
282         testers[index]->retrain(new_size, (contents *)junk_space);
283         int wrong_counter = 0;
284         for (int i = 0; i < new_size; i++)
285           if (junk_space[i] != (*testers[index])[i]) wrong_counter++;
286         ASSERT_EQUAL(wrong_counter, 0, "test contents after retrain");
287         break;
288       }
289       case do_size: {
290 #ifdef DEBUG_TEST_ARRAY
291         LOG(a_sprintf("do_size"));
292 #endif
293         array<contents> old_version = *testers[index];
294         bool at_front = bool(a_randomizer.inclusive(0, 1));
295         int new_size = a_randomizer.inclusive(MIN_OBJECT, MAX_OBJECT);
296         bool smaller = new_size < old_version.length();
297         int difference = absolute_value(new_size - old_version.length());
298         testers[index]->resize(new_size,
299             at_front? old_version.NEW_AT_BEGINNING
300                 : old_version.NEW_AT_END);
301         if (!smaller && difference) {
302           // neuter the contents in the new section so we can compare.  this
303           // space is filled with whatever the object's constructor chooses.
304           if (at_front) {
305             for (int i = 0; i < difference; i++)
306               testers[index]->put(i, 'Q');
307           } else {
308             for (int i = old_version.length();
309                 i < old_version.length() + difference; i++)
310               testers[index]->put(i, 'Q');
311           }
312         }
313         // now compute an equivalent form of what the state should be.
314         array<contents> equivalent(0, NULL_POINTER, flags);
315         if (at_front) {
316           if (smaller) {
317             equivalent = old_version.subarray(difference,
318                 old_version.length() - 1);
319           } else {
320             array<contents> blank(difference, NULL_POINTER, flags);
321             for (int i = 0; i < blank.length(); i++)
322               blank[i] = 'Q';
323             equivalent = blank + old_version;
324           }
325         } else {
326           if (smaller) {
327             equivalent = old_version.subarray(0, old_version.length()
328                 - difference - 1);
329           } else {
330             array<contents> blank(difference, NULL_POINTER, flags);
331             for (int i = 0; i < blank.length(); i++)
332               blank[i] = 'Q';
333             equivalent = old_version + blank;
334           }
335         }
336         ASSERT_EQUAL(equivalent.length(), testers[index]->length(),
337             "check length of resized form");
338         ASSERT_TRUE(compare_arrays(*testers[index], equivalent),
339             "check contents of resized form");
340         break;
341       }
342       case do_assign: {
343 #ifdef DEBUG_TEST_ARRAY
344         LOG(a_sprintf("do_assign"));
345 #endif
346         array<contents> arrh = *testers[index];  // copy old value.
347         int to_assign = a_randomizer.inclusive(0, MAX_SIMULTANEOUS_OBJECTS - 1);
348         *testers[index] = *testers[to_assign];
349         ASSERT_TRUE(compare_arrays(*testers[index], *testers[to_assign]),
350             "check result of assign copying array");
351         *testers[to_assign] = arrh;  // recopy contents to new place.
352         ASSERT_TRUE(compare_arrays(*testers[to_assign], arrh),
353             "check result of second assign");
354         break;
355       }
356       case do_access: {
357 #ifdef DEBUG_TEST_ARRAY
358         LOG(a_sprintf("do_access"));
359 #endif
360         int start = a_randomizer.inclusive(0, testers[index]->length());
361         int end = a_randomizer.inclusive(0, testers[index]->length());
362         flip_increasing(start, end);
363         array<contents> accumulator(0, NULL_POINTER, flags);
364         for (int i = start; i < end; i++) {
365           contents c = contents(a_randomizer.inclusive(1, 255));
366           testers[index]->access()[i] = c;
367           accumulator += c;
368         }
369         for (int j = start; j < end; j++)
370           ASSERT_EQUAL(accumulator[j - start], (*testers[index])[j],
371               "comparison accessing at index must be equal");
372         break;
373       }
374       case do_indices: {
375 #ifndef CATCH_ERRORS  // only works if not catching errors.
376   #ifdef DEBUG_TEST_ARRAY
377         LOG(a_sprintf("do_indices"));
378   #endif
379         // does some tests on bad indices.
380         contents c1 = testers[index]->operator[](-50);
381         contents c2 = testers[index]->operator[](-MAX_OBJECT);
382         bool test = (c1 == c2);
383         ASSERT_TRUE(test, "invalid values should be the same");
384         int tests = a_randomizer.inclusive(100, 500);
385         for (int i = 0; i < tests; i++) {
386           int indy = a_randomizer.inclusive(-1000, MAX_OBJECT * 3);
387           // testing if we can access without explosions.
388           contents c3 = testers[index]->operator[](indy);
389           contents c4 = c3;
390           ASSERT_EQUAL(c3, c4, "for do_indices, values should be the same");
391         }
392 #endif
393         break;
394       }
395       case do_observe: {
396 #ifdef DEBUG_TEST_ARRAY
397         LOG(a_sprintf("do_observe"));
398 #endif
399         // tests contents returned by observe.
400         int start = a_randomizer.inclusive(0, testers[index]->length());
401         int end = a_randomizer.inclusive(0, testers[index]->length());
402         flip_increasing(start, end);
403         double total_1 = 0;
404         for (int i = start; i < end; i++)
405           total_1 += testers[index]->observe()[i];
406         double total_2 = 0;
407         for (int j = end - 1; j >= start; j--)
408           total_2 += testers[index]->observe()[j];
409         ASSERT_EQUAL(total_1, total_2, "totals should match up");
410         break;
411       }
412       case do_resizer: {
413 #ifdef DEBUG_TEST_ARRAY
414         LOG(a_sprintf("do_resizer"));
415 #endif
416         // tests whether the array will reuse space when it should be able to.
417         array<contents> &arrh = *testers[index];
418         // fill with known data.
419         int i;
420         for (i = 0; i < arrh.length(); i++)
421           arrh[i] = contents((i + 23) % 256);
422         // record the old starting point.
423         const contents *start = arrh.internal_block_start();
424         int zap_amount = a_randomizer.inclusive(1, arrh.length() - 1);
425         // now take out a chunk from the array at the beginning.
426         arrh.zap(0, zap_amount - 1);
427         // test the contents.
428         for (i = 0; i < arrh.length(); i++)
429           ASSERT_EQUAL(arrh[i], contents((i + 23 + zap_amount) % 256),
430               "the resized form should have same contents after zap");
431         // now add back in the space we ate.
432         arrh.resize(arrh.length() + zap_amount, arrh.NEW_AT_END);
433         // check the pointer; it should not have changed.
434         ASSERT_EQUAL(start, arrh.internal_block_start(),
435             "the resized form should have same start address");
436         // test the contents again.  they should start at zero and have the
437         // same zap_amount offset.  the stuff past the original point shouldn't
438         // be tested since we haven't set it.
439         for (i = 0; i < arrh.length() - zap_amount; i++)
440           ASSERT_EQUAL(arrh[i], contents((i + 23 + zap_amount) % 256),
441               "the resized form should still have same contents");
442         // now a test of all values through the zap_amount.
443         arrh.zap(0, zap_amount - 1);
444         for (i = 0; i < zap_amount; i++) {
445           arrh.resize(arrh.length() + 1, arrh.NEW_AT_END);
446           ASSERT_EQUAL(start, arrh.internal_block_start(),
447               "the slowly resized form should have same start address");
448         }
449         // test the contents once more.  they should start at zero and have
450         // double the zap_amount offset.  the stuff past the original point
451         // shouldn't be tested since we haven't set it.
452         for (i = 0; i < arrh.length() - 2 * zap_amount; i++)
453           ASSERT_EQUAL(arrh[i], contents((i + 23 + 2 * zap_amount) % 256),
454               "the slowly resized form should have same contents");
455
456         // the tests below should be nearly identical to the ones above, but
457         // they use the NEW_AT_BEGINNING style of copying.
458
459         // fill with known data.
460         for (i = 0; i < arrh.length(); i++)
461           arrh[i] = contents((i + 23) % 256);
462         // record the old starting point.
463         start = arrh.internal_block_start();
464         zap_amount = a_randomizer.inclusive(1, arrh.length() - 1);
465         // now take out a chunk from the array at the beginning.
466         arrh.zap(0, zap_amount - 1);
467         // test the contents.
468         for (i = 0; i < arrh.length(); i++)
469           ASSERT_EQUAL(arrh[i], contents((i + 23 + zap_amount) % 256),
470               "the resized form with known data should have right contents");
471         // now add back in the space we ate.
472         arrh.resize(arrh.length() + zap_amount,
473             arrh.NEW_AT_BEGINNING);
474         // check the pointer; it should not have changed.
475         ASSERT_EQUAL(start, arrh.internal_block_start(),
476             "the resized form with known data should have same start address");
477         // test the contents again.  they should start at zap_amount but have
478         // the same zap_amount offset.  the stuff before the original point
479         // shouldn't be tested since we haven't set it.
480         for (i = zap_amount; i < arrh.length(); i++)
481           ASSERT_EQUAL(arrh[i], contents((i + 23) % 256),
482               "the known data resized form should have same contents");
483         // now a test of all values through the zap_amount.
484         arrh.zap(0, zap_amount - 1);
485         for (i = 0; i < zap_amount; i++) {
486           arrh.resize(arrh.length() + 1, arrh.NEW_AT_BEGINNING);
487           ASSERT_EQUAL(start, arrh.internal_block_start(),
488               "the known data slowly resized form should have same start address");
489         }
490         // test the contents once more.  the zap_amount offset should stay the
491         // same since we clobbered the place we added originally, then added
492         // more space in.  so we skip the first part still.
493         for (i = zap_amount; i < arrh.length(); i++)
494           ASSERT_EQUAL(arrh[i], contents((i + 23) % 256),
495               "the known data slowly resized form should have same contents");
496         break;
497       }
498       case do_zap: {
499 #ifdef DEBUG_TEST_ARRAY
500         LOG(a_sprintf("do_zap"));
501 #endif
502         int start;
503         int end;
504         bool erroneous = false;
505         int chose = a_randomizer.inclusive(1, 100);
506         if (chose <= 90) {
507           // there's a ninety percent chance we pick a range that's valid.
508           start = a_randomizer.inclusive(0, testers[index]->length() - 1);
509           end = a_randomizer.inclusive(0, testers[index]->length() - 1);
510         } else if (chose <= 95) {
511           // and a 5 percent chance we pick to choose the zero index as our
512           // start; this gets at the code for fast zapping.
513           start = 0;
514           end = a_randomizer.inclusive(0, testers[index]->length() - 1);
515         } else {
516           // and a 5 percent chance we'll allow picking a bad index.  the
517           // actual chance of picking a bad one is less.
518           erroneous = true;
519           start = a_randomizer.inclusive(-2, testers[index]->length() + 3);
520           end = a_randomizer.inclusive(-2, testers[index]->length() + 3);
521         }
522         flip_increasing(start, end);
523         array<contents> old_version = *testers[index];
524         testers[index]->zap(start, end);
525         if (!erroneous) {
526           array<contents> old_head = old_version.subarray(0, start - 1);
527           array<contents> old_tail = old_version.subarray(end + 1,
528               old_version.length() - 1);
529           array<contents> equivalent = old_head + old_tail;
530           ASSERT_EQUAL(equivalent.length(), testers[index]->length(),
531               "the zapped form should not have erroneous length");
532           ASSERT_TRUE(compare_arrays(*testers[index], equivalent),
533               "the zapped form should not have erroneous contents");
534         }
535         break;
536       }
537       case do_reset: {
538 #ifdef DEBUG_TEST_ARRAY
539         LOG(a_sprintf("do_reset"));
540 #endif
541         int junk_start = a_randomizer.inclusive(MIN_BLOCK, MAX_BLOCK);
542         int junk_end = a_randomizer.inclusive(MIN_BLOCK, MAX_BLOCK);
543         flip_increasing(junk_start, junk_end);
544         int len = junk_end - junk_start + 1;
545         testers[index]->reset(len, (contents *)&junk_space[junk_start]);
546         ASSERT_EQUAL(testers[index]->length(), len, "reset should have proper length");
547         for (int i = 0; i < len; i++)
548           ASSERT_EQUAL(testers[index]->get(i), junk_space[junk_start + i],
549               "reset should copy data");
550         break;
551       }
552       case do_shrink: {
553 #ifdef DEBUG_TEST_ARRAY
554         LOG(a_sprintf("do_shrink"));
555 #endif
556         array<contents> garp = *testers[index];
557         testers[index]->shrink();
558         int new_diff = testers[index]->internal_real_length()
559             - testers[index]->length();
560         ASSERT_TRUE(compare_arrays(garp, *testers[index]), "shrink should keep contents");
561         // now force a real shrinkage.
562         if (testers[index]->length() < 5) continue;  // need some room.
563         // pick an element to keep that is not first or last.
564         int kept = a_randomizer.inclusive(1, testers[index]->last() - 1);
565         // whack all but the lucky element.
566         testers[index]->zap(kept + 1, testers[index]->last());
567         testers[index]->zap(0, kept - 1);
568         // shrink it again, now a big shrink.
569         testers[index]->shrink();
570         // check the difference now.
571         new_diff = testers[index]->internal_real_length()
572             - testers[index]->length();
573         ASSERT_FALSE(new_diff > 1, "massive shrink size should be correct");
574
575         // restore the original contents and block size.
576         *testers[index] = garp;
577         break;
578       }
579       case do_concatenating: {
580 #ifdef DEBUG_TEST_ARRAY
581         LOG(a_sprintf("do_concatenating"));
582 #endif
583         for (int i = 0; i < a_randomizer.inclusive(1, 20); i++) {
584           contents new_c = contents(a_randomizer.inclusive('a', 'z'));
585           testers[index]->concatenate(new_c);
586           ASSERT_EQUAL(new_c, testers[index]->get(testers[index]->last()),
587               "value should be equal after concatenate");
588         }
589         int indy = a_randomizer.inclusive(0, MAX_SIMULTANEOUS_OBJECTS - 1);
590         array<contents> flirpan = *testers[indy];
591         int start = a_randomizer.inclusive(0, flirpan.length() - 1);
592         int end = a_randomizer.inclusive(0, flirpan.length() - 1);
593         flip_increasing(start, end);
594         flirpan = flirpan.subarray(start, end);
595         array<contents> grumzor = *testers[index];  // old copy.
596         testers[index]->concatenate(flirpan);
597         array<contents> bubula = grumzor + flirpan;
598         ASSERT_TRUE(compare_arrays(bubula, *testers[index]),
599             "contents should be correct after concatenate or concatenation");
600         contents first_value;
601         contents second_value;
602         if (testers[index]->length() >= 1)
603           first_value = testers[index]->get(0);
604         if (testers[index]->length() >= 2)
605           second_value = testers[index]->get(1);
606         const int max_iters = a_randomizer.inclusive(1, 42);
607         for (int j = 0; j < max_iters; j++) {
608           contents new_c = contents(a_randomizer.inclusive('a', 'z'));
609           *testers[index] = *testers[index] + new_c;
610           // correct our value checks if new indices became available.
611           if (testers[index]->length() == 1) {
612             first_value = testers[index]->get(0);
613           } else if (testers[index]->length() == 2) {
614             second_value = testers[index]->get(1);
615           }
616
617           ASSERT_EQUAL(new_c, testers[index]->get(testers[index]->last()),
618               "value should not be wrong after concatenation");
619
620           ASSERT_FALSE((testers[index]->length() >= 1) && (first_value != testers[index]->get(0)),
621               "first value should not be corrupted");
622           ASSERT_FALSE((testers[index]->length() >= 2) && (second_value != testers[index]->get(1)),
623               "second value should not be corrupted");
624
625           *testers[index] += new_c;
626           // we don't have to correct the first value any more, but might have
627           // to correct the second.
628           if (testers[index]->length() == 2) {
629             second_value = testers[index]->get(1);
630           }
631           ASSERT_EQUAL(new_c, testers[index]->get(testers[index]->last()),
632               "value should be right after second concatenation");
633           ASSERT_FALSE( (testers[index]->length() >= 1) && (first_value != testers[index]->get(0)),
634               "first value should be uncorrupted after second concat");
635           ASSERT_FALSE((testers[index]->length() >= 2) && (second_value != testers[index]->get(1)),
636               "second value should be uncorrupted after second concat");
637         }
638         break;
639       }
640       case do_concatenating2: {
641         // this tests the new concatenate content array method for array.
642 #ifdef DEBUG_TEST_ARRAY
643         LOG(a_sprintf("do_concatenating2"));
644 #endif
645         array<contents> &flirpan = *testers[index];
646         int new_len = a_randomizer.inclusive(0, 140);
647         contents *to_add = new contents[new_len];
648         for (int i = 0; i < new_len; i++) {
649           int rando = a_randomizer.inclusive(0, MAX_BLOCK - 1);
650           to_add[i] = junk_space[rando];
651         }
652         int old_len = flirpan.length();
653         flirpan.concatenate(to_add, new_len);
654         for (int i = 0; i < new_len; i++) {
655           ASSERT_EQUAL(flirpan[i + old_len], to_add[i],
656               "value should not be wrong after content array concatenation");
657         }
658         delete [] to_add;  // clean up.
659         break;
660       }
661       case do_subarray: {
662 #ifdef DEBUG_TEST_ARRAY
663         LOG(a_sprintf("do_subarray"));
664 #endif
665         array<contents> flirpan = *testers[index];
666         int start = a_randomizer.inclusive(0, flirpan.length() - 1);
667         int end = a_randomizer.inclusive(0, flirpan.length() - 1);
668         flip_increasing(start, end);
669         flirpan = flirpan.subarray(start, end);
670         for (int i = 0; i < end - start; i++)
671           ASSERT_EQUAL(flirpan[i], testers[index]->get(i + start),
672               "subarray should produce right array");
673         break;
674       }
675       case do_memory_paring: {
676 #ifdef DEBUG_TEST_ARRAY
677         LOG(a_sprintf("do_memory_paring"));
678 #endif
679         for (int i = 0; i < MAX_SIMULTANEOUS_OBJECTS; i++) {
680           // zap extra junk off so we bound the memory usage.
681           if (testers[i]->length() > MAX_OBJECT) {
682             testers[i]->zap(MAX_OBJECT, testers[i]->length() - 1);
683             testers[i]->shrink();
684           }
685         }
686         break;
687       }
688       case do_snarf: {
689 #ifdef DEBUG_TEST_ARRAY
690         LOG(a_sprintf("do_snarf"));
691 #endif
692         array<contents> flirpan = *testers[index];
693 //        int start = a_randomizer.inclusive(0, flirpan.length() - 1);
694 //        int end = a_randomizer.inclusive(0, flirpan.length() - 1);
695 //        flip_increasing(start, end);
696 //        flirpan = flirpan.subarray(start, end);
697         int rand_index = a_randomizer.inclusive(0, MAX_SIMULTANEOUS_OBJECTS - 1);
698         if (index == rand_index) continue;  // skip it; try again later.
699         array<contents> nugwort = *testers[rand_index];
700         // perform a swap between two of our arrays.
701         array<contents> temp_hold(0, NULL_POINTER, flags);
702         temp_hold.snarf(*testers[index]);
703         testers[index]->snarf(*testers[rand_index]);
704         testers[rand_index]->snarf(temp_hold);
705         // the copies should have flipped places now.  check them.
706         ASSERT_EQUAL(flirpan.length(), testers[rand_index]->length(),
707             "snarf needs to produce right length at A");
708         for (int i = 0; i < flirpan.length(); i++)
709           ASSERT_EQUAL(flirpan[i], testers[rand_index]->get(i),
710               "snarf needs to produce right array at A");
711         ASSERT_EQUAL(nugwort.length(), testers[index]->length(),
712             "snarf needs to produce right length at B");
713         for (int j = 0; j < nugwort.length(); j++)
714           ASSERT_EQUAL(nugwort[j], testers[index]->get(j),
715               "snarf needs to produce right array at B");
716         break;
717       }
718       case do_flush: {
719 #ifdef DEBUG_TEST_ARRAY
720         LOG(a_sprintf("do_flush"));
721 #endif
722         array<contents> flirpan = *testers[index];
723
724 ///fix up it up in it.
725
726 ///        flirpan.reset(
727
728
729         break;
730       }
731
732       case do_insert: {
733 #ifdef DEBUG_TEST_ARRAY
734         LOG(a_sprintf("do_insert"));
735 #endif
736         array<contents> hold = *testers[index];
737         int where = a_randomizer.inclusive(0, hold.last());
738         int how_many = a_randomizer.inclusive(0, 25);
739         testers[index]->insert(where, how_many);
740         for (int i = 0; i < where; i++)
741           ASSERT_EQUAL(hold[i], testers[index]->get(i),
742               "should have good contents on left after insert");
743         for (int j = where + how_many; j < testers[index]->length(); j++)
744           ASSERT_EQUAL(hold[j - how_many], testers[index]->get(j),
745               "should have good contents on right after insert");
746         break;
747       }
748       case do_overwrite: {
749 #ifdef DEBUG_TEST_ARRAY
750         LOG(a_sprintf("do_overwrite"));
751 #endif
752         if (!testers[index]->length()) continue;
753         array<contents> hold = *testers[index];
754         int index2 = index;
755         while (index2 == index)
756           index2 = a_randomizer.inclusive(0, MAX_SIMULTANEOUS_OBJECTS - 1);
757         array<contents> &hold2 = *testers[index2];
758         if (!hold2.length()) continue;
759         int write_indy = a_randomizer.inclusive(0, hold.last());
760         int write_len = minimum(hold2.length(), (hold.length() - write_indy));
761 // LOG(a_sprintf("len1 = %d len2 = %d wrindy=%d wrlen=%d", hold.length(), hold2.length(), write_indy, write_len));
762         outcome ret = testers[index]->overwrite(write_indy,
763             *testers[index2], write_len);
764         ASSERT_EQUAL(ret.value(), common::OKAY,
765             astring("should not have had outcome=") + common::outcome_name(ret));
766         for (int i = 0; i < write_indy; i++)
767           ASSERT_EQUAL(hold[i], testers[index]->get(i),
768               "should have good contents on left after overwrite");
769         for (int j = write_indy; j < write_indy + write_len; j++)
770           ASSERT_EQUAL(hold2[j - write_indy], testers[index]->get(j),
771               "should have good contents in middle after overwrite");
772         for (int k = write_indy + write_len; k < testers[index]->length(); k++)
773           ASSERT_EQUAL(hold[k], testers[index]->get(k),
774               "should have good contents on right after overwrite");
775         break;
776       }
777       case do_stuff: {
778 #ifdef DEBUG_TEST_ARRAY
779         LOG(a_sprintf("do_stuff"));
780 #endif
781         array<contents> &hold = *testers[index];
782         contents burgers[100];
783         int stuff_len = a_randomizer.inclusive(0, hold.length());
784         stuff_len = minimum(stuff_len, 100);
785         outcome ret = hold.stuff(stuff_len, burgers);
786         ASSERT_EQUAL(ret.value(), common::OKAY,
787             astring("should not have had outcome=") + common::outcome_name(ret));
788         for (int i = 0; i < stuff_len; i++)
789           ASSERT_EQUAL(burgers[i], hold[i],
790               "should have good contents after stuff");
791         break;
792       }
793       default: {
794         ASSERT_FALSE(true, "test cases should have no invalid choices!");
795         break;
796       }
797     }
798   }
799
800   // clean up.
801   delete [] junk_space;
802   for (int d = 0; d < MAX_SIMULTANEOUS_OBJECTS; d++) delete testers[d];
803 }
804
805 //////////////
806
807 struct gerkin { int l; abyte *p; char *r; void *pffttt; };
808
809 gerkin borgia;
810
811 class foop
812 {
813 public:
814   virtual ~foop() {}
815   virtual gerkin *boorba() = 0;
816 };
817
818 class boop : public foop
819 {
820 public:
821   virtual gerkin *boorba() { return &borgia; }
822 };
823
824 void test_array::test_iteration_speed()
825 {
826   FUNCDEF("test_iteration_speed");
827   const int MAX_CHUNK = 100000;
828   const int MAX_REPS = 20;
829   byte_array chunky(MAX_CHUNK);
830
831   {
832     time_stamp start;
833     int sum = 0;
834     for (int j = 0; j < MAX_REPS; j++) {
835       for (int i = 0; i < MAX_CHUNK; i++) {
836         sum += chunky[i];
837       }
838     }
839     int duration = int(time_stamp().value() - start.value());
840
841     a_sprintf message("iteration over %d elements took %d ms,\t"
842         "or %f ms per 1000 iters.",
843         MAX_CHUNK * MAX_REPS, duration,
844         double(duration) / double(MAX_CHUNK * MAX_REPS) * 1000.0);
845 //base_logger &b = program_wide_logger::get();
846     LOG(message);
847   }
848
849   {
850     time_stamp start;
851     int sum = 0;
852     const abyte *head = chunky.observe();
853     for (int j = 0; j < MAX_REPS; j++) {
854       for (int i = 0; i < MAX_CHUNK; i++) {
855         sum += head[i];
856       }
857     }
858     int duration = int(time_stamp().value() - start.value());
859
860     LOG(a_sprintf("less-safe iteration over %d elements took %d ms,\tor %f ms per 1000 iters.",
861         MAX_CHUNK * MAX_REPS, duration,
862         double(duration) / double(MAX_CHUNK * MAX_REPS) * 1000.0));
863   }
864   {
865     time_stamp start;
866     boop tester;
867 //    int sum = 0;
868     for (int j = 0; j < MAX_REPS; j++) {
869       for (int i = 0; i < MAX_CHUNK; i++) {
870 //        chunky.modus().sampler();
871         tester.boorba();
872       }
873     }
874     int duration = int(time_stamp().value() - start.value());
875
876     LOG(a_sprintf("virtual-function-only over %d elements took %d ms,\tor %f ms per 1000 iters.",
877         MAX_CHUNK * MAX_REPS, duration,
878         double(duration) / double(MAX_CHUNK * MAX_REPS) * 1000.0));
879   }
880 }
881
882 //////////////
883
884 int test_array::execute()
885 {
886   FUNCDEF("execute");
887 #if 0
888 // if enabled for the abusive type tests, then allow this one...
889   test_iteration_speed();
890 //  LOG(a_sprintf("did iteration test.")); 
891 #endif
892
893   int_array checking_start;
894   ASSERT_FALSE(checking_start.length(),
895       "int_array should not have contents from empty constructor");
896
897   double_plus bogus4 = float(12.32);
898   array_tester<double_plus >(*this, bogus4,
899       byte_array::SIMPLE_COPY | byte_array::EXPONE
900           | byte_array::FLUSH_INVISIBLE);
901 //  LOG(a_sprintf("did float array test.")); 
902
903   int bogus2 = 39;
904   array_tester<int>(*this, bogus2,
905       byte_array::SIMPLE_COPY | byte_array::EXPONE
906           | byte_array::FLUSH_INVISIBLE);
907 //  LOG(a_sprintf("did int array test.")); 
908
909   test_content bogus3(12);
910   array_tester<test_content>(*this, bogus3, byte_array::EXPONE
911       | byte_array::FLUSH_INVISIBLE);
912 //  LOG(a_sprintf("did test_content array test.")); 
913
914   test_arrays_of_void_pointer();
915 //  LOG(a_sprintf("did void * array test.")); 
916
917   abyte bogus1 = 'c';
918   array_tester<abyte>(*this, bogus1,
919       byte_array::SIMPLE_COPY | byte_array::EXPONE
920           | byte_array::FLUSH_INVISIBLE);
921 //  LOG(a_sprintf("did byte array test.")); 
922
923 ////  LOG("array:: works for those functions tested.");
924
925   return final_report();
926 }
927
928 HOOPLE_MAIN(test_array, )
929