Main Page | Namespace List | Class Hierarchy | Class List | Directories | File List | Namespace Members | Class Members | File Members

TestBufStream.C

Go to the documentation of this file.
00001 // Copyright (C) 2004, Kenneth C. Schalk
00002 
00003 // This file is part of Vesta.
00004 
00005 // Vesta is free software; you can redistribute it and/or
00006 // modify it under the terms of the GNU Lesser General Public
00007 // License as published by the Free Software Foundation; either
00008 // version 2.1 of the License, or (at your option) any later version.
00009 
00010 // Vesta is distributed in the hope that it will be useful,
00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013 // Lesser General Public License for more details.
00014 
00015 // You should have received a copy of the GNU Lesser General Public
00016 // License along with Vesta; if not, write to the Free Software
00017 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00018 
00019 // Last modified on Thu Aug 19 09:52:17 EDT 2004 by ken@xorian.net        
00020 
00021 // ----------------------------------------------------------------------
00022 
00023 // TestBufStream.C
00024 
00025 // Tests for the Basics::OBufStream class.
00026 
00027 #include <assert.h>
00028 #include <string.h>
00029 #include <stdlib.h>
00030 #include <stdio.h>
00031 #include <time.h>
00032 
00033 #include "BufStream.H"
00034 #include "Basics.H"
00035 
00036 #include <iostream>
00037 #include <iomanip>
00038 #include <sstream>
00039 
00040 using std::cout;
00041 using std::cerr;
00042 using std::endl;
00043 using std::ios;
00044 using std::hex;
00045 using std::dec;
00046 using std::setw;
00047 using std::setfill;
00048 using std::ostringstream;
00049 using std::string;
00050 
00051 // ----------------------------------------------------------------------
00052 // Support code for generating strings similar to what goes in the
00053 // repository's transaction log.
00054 // ----------------------------------------------------------------------
00055 
00056 // A toy version of the repository LongId class, just used for
00057 // formatting messages like what the repository puts in its
00058 // transaction log.
00059 
00060 struct LongId
00061 {
00062   struct { unsigned char byte[32]; } value;
00063 
00064   int length() const throw()
00065   {
00066     unsigned int len = 1;
00067     while((len < sizeof(value.byte)) && (value.byte[len] != 0))
00068       {
00069         len++;
00070       }
00071     return len;
00072   }
00073 
00074   void append(unsigned int index) throw ()
00075   {
00076     unsigned char* start = &value.byte[0];
00077     unsigned char* end =
00078       (unsigned char*) memchr((const void*) (start + 1), 0, sizeof(value)-1);
00079     while (index > 0) {
00080         if (end >= &value.byte[sizeof(value)]) {
00081             // This index won't fit
00082             return;
00083         }
00084         int newbyte = index & 0x7f;
00085         index >>= 7;
00086         if (index > 0) newbyte |= 0x80;
00087         *end++ = newbyte;
00088     }       
00089   }
00090 
00091   LongId()
00092   {
00093     memset(&(this->value), 0, sizeof(this->value));
00094 
00095     // Fill this LongId with random stuff.
00096     int target_bytes = (random() % 16) + 8;
00097     while(length() < target_bytes)
00098       append(random() % 8192);
00099   }
00100 
00101 };
00102 
00103 using std::ostream;
00104 
00105 // Functions from VLogHelp.C in the repository.
00106 
00107 ostream&
00108 operator<<(ostream& s, const LongId& longid)
00109 {
00110     int i;
00111     int len = longid.length();
00112     for (i=0; i<len; i++) {
00113         s << setw(2) << setfill('0') << hex
00114           << (int) (longid.value.byte[i] & 0xff);
00115     }
00116     s << setfill(' ') << dec;
00117     return s;
00118 }
00119 
00120 ostream&
00121 PutQuotedString(ostream& s, const char* string)
00122 {
00123     s << '\"';
00124     while (*string) {
00125         if (*string == '\"' || *string == '\\') {
00126             s << '\\';
00127         }
00128         s << *string++;
00129     }
00130     s << '\"';
00131     return s;
00132 }
00133 
00134 // Generate a random arc for a repository transaction
00135 string random_arc()
00136 {
00137   string result;
00138   int length = 4 + (random() % 20);
00139   static const char *letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
00140   static const char *digits = "0123456789";
00141   static const char *specials = "_.";
00142   while(result.size() < length)
00143     {
00144       switch(random() % 8)
00145         {
00146         case 0:
00147           result += specials[random() % 2];
00148           break;
00149         case 1:
00150         case 2:
00151           result += digits[random() % 10];
00152           break;
00153         default:
00154           result += letters[random() % 52];
00155           break;
00156         }
00157     }
00158 
00159   return result;
00160 }
00161 
00162 // ----------------------------------------------------------------------
00163 
00164 void print_stream_state(unsigned int state)
00165 {
00166   bool first = true;
00167   if(state & ios::eofbit)
00168     {
00169       cerr << "eof";
00170       state &= ~((unsigned int) ios::eofbit);
00171       first = false;
00172     }
00173   if(state & ios::failbit)
00174     {
00175       if(!first)
00176         cerr << "|";
00177       cerr << "fail";
00178       state &= ~((unsigned int) ios::failbit);
00179       first = false;
00180     }
00181   if(state & ios::badbit)
00182     {
00183       if(!first)
00184         cerr << "|";
00185       cerr << "bad";
00186       state &= ~((unsigned int) ios::badbit);
00187       first = false;
00188     }
00189   if(state != 0)
00190     {
00191       if(!first)
00192         cerr << "|";
00193       cerr << "0x" << hex << state << dec;
00194     }
00195 }
00196 
00197 void print_state_if_not_good(const ios &stream,
00198                              const char *name, const char *marker)
00199 {
00200   if(!stream.good())
00201     {
00202       cerr << name << " state (" << marker << "): ";
00203       print_stream_state(stream.rdstate());
00204       cerr << endl;
00205       abort();
00206     }  
00207 }
00208 
00209 // ('time' longid timestamp)
00210 void format_time(LongId longid, time_t timestamp, std::ostream &out)
00211 {
00212   out << "(time " << longid << " " << timestamp << ")\n";
00213 }
00214 
00215 // ('del' longid arc timestamp)
00216 void format_del(LongId longid, time_t timestamp, string &arc, std::ostream &out)
00217 {
00218   out << "(del " << longid << " ";
00219   PutQuotedString(out, arc.c_str());
00220   out << " " << timestamp << ")\n";
00221 }
00222 
00223 // ('insf' longid arc sid master timestamp)
00224 void format_insf(LongId longid, time_t timestamp, string &arc,
00225                  unsigned int sid, bool mast,
00226                  // FP::Tag *forceFPTag,
00227                  std::ostream &out)
00228 {
00229   out << "(insf " << longid << " ";
00230   PutQuotedString(out, arc.c_str());
00231   out << " 0x" << hex << sid << dec << " " 
00232            << (int) mast << " " << timestamp;
00233   /*
00234     if (forceFPTag) {
00235     out << " ";
00236     PutFPTag(out, *forceFPTag);
00237     }
00238   */
00239   out << ")\n";
00240 }
00241 
00242 // ('insu' longid arc sid master timestamp)
00243 void format_insu(LongId longid, time_t timestamp, string &arc,
00244                  unsigned int sid, bool mast,
00245                  std::ostream &out)
00246 {
00247   out << "(insu " << longid << " ";
00248   PutQuotedString(out, arc.c_str());
00249   out << " 0x" << hex << sid << dec << " " 
00250            << (int) mast << " " << timestamp << ")\n";
00251 }
00252 
00253 // ----------------------------------------------------------------------
00254 
00255 void output_tests()
00256 {
00257   {
00258     Basics::OBufStream foo;
00259 
00260     print_state_if_not_good(foo, "foo", "start");
00261     foo << "Here's a simple test.";
00262     print_state_if_not_good(foo, "foo", "1");
00263 
00264     const char *foo_str = foo.str();
00265 
00266     if(strcmp(foo_str, "Here's a simple test.") != 0)
00267       {
00268         cerr << "foo.str() = \"" << foo.str() << "\"" << endl
00269              << "strlen(foo.str()) = " << strlen(foo.str()) << endl;
00270         abort();
00271       }
00272   }
00273 
00274   {
00275     char foo2_buf[256];
00276     Basics::OBufStream foo2(foo2_buf, sizeof(foo2_buf));
00277 
00278     print_state_if_not_good(foo2, "foo2", "start");
00279     foo2 << "Here's another test, with some text that's a bit longer.";
00280     print_state_if_not_good(foo2, "foo2", "1");
00281     foo2 << endl;
00282     print_state_if_not_good(foo2, "foo2", "2");
00283     foo2 << "And a second sentence on another line.";
00284     print_state_if_not_good(foo2, "foo2", "3");
00285     foo2 << endl;
00286     print_state_if_not_good(foo2, "foo2", "4");
00287 
00288     const char *foo2_str = foo2.str();
00289 
00290     if(strcmp(foo2_str, ("Here's another test, with some text that's a bit longer.\n"
00291                          "And a second sentence on another line.\n")) != 0)
00292       {
00293         cerr << "foo2.str() = \"" << foo2.str() << "\"" << endl
00294              << "strlen(foo2.str()) = " << strlen(foo2.str()) << endl;
00295         abort();
00296       }
00297 
00298     assert(foo2.str() == foo2_buf);
00299   }
00300 
00301   {
00302     char logrec[512], logrec2[10];
00303     // Fixed buffer, no reallocation
00304     Basics::OBufStream time_tos(logrec, sizeof(logrec));
00305     // No buffer
00306     Basics::OBufStream time_tos2;
00307     // Small buffer, re-allocate when it fills
00308     Basics::OBufStream time_tos3(logrec2, sizeof(logrec2), 0, true);
00309     ostringstream time_oss;
00310 
00311     // Arguments
00312     LongId longid;
00313     time_t timestamp = time(0);
00314 
00315     format_time(longid, timestamp, time_tos);
00316     format_time(longid, timestamp, time_tos2);
00317     format_time(longid, timestamp, time_tos3);
00318     format_time(longid, timestamp, time_oss);
00319 
00320     assert(strcmp(time_oss.str().c_str(), time_tos.str()) == 0);
00321     assert(strcmp(time_oss.str().c_str(), time_tos2.str()) == 0);
00322     assert(strcmp(time_oss.str().c_str(), time_tos3.str()) == 0);
00323     assert(time_tos.str() == logrec);
00324     assert(time_tos3.str() != logrec2);
00325   }
00326 
00327   {
00328     char logrec[512], logrec2[10];
00329     // Fixed buffer, no reallocation
00330     Basics::OBufStream del_tos(logrec, sizeof(logrec));
00331     // No buffer
00332     Basics::OBufStream del_tos2;
00333     // Small buffer, re-allocate when it fills
00334     Basics::OBufStream del_tos3(logrec2, sizeof(logrec2), 0, true);
00335     ostringstream del_oss;
00336 
00337     // Arguments
00338     LongId longid;
00339     time_t timestamp = time(0);
00340     string arc = random_arc();
00341 
00342     // Fill the different streams
00343     format_del(longid, timestamp, arc, del_tos);
00344     format_del(longid, timestamp, arc, del_tos2);
00345     format_del(longid, timestamp, arc, del_tos3);
00346     format_del(longid, timestamp, arc, del_oss);
00347 
00348     // They should all have the same contents
00349     assert(strcmp(del_oss.str().c_str(), del_tos.str()) == 0);
00350     assert(strcmp(del_oss.str().c_str(), del_tos2.str()) == 0);
00351     assert(strcmp(del_oss.str().c_str(), del_tos3.str()) == 0);
00352 
00353     // The one with sufficient buffer space shouldn't have
00354     // re-allocated
00355     assert(del_tos.str() == logrec);
00356     assert(del_tos3.str() != logrec2);
00357   }
00358 
00359   {
00360     char logrec[512], logrec2[10];
00361     // Fixed buffer, no reallocation
00362     Basics::OBufStream insf_tos(logrec, sizeof(logrec));
00363     // No buffer
00364     Basics::OBufStream insf_tos2;
00365     // Small buffer, re-allocate when it fills
00366     Basics::OBufStream insf_tos3(logrec2, sizeof(logrec2), 0, true);
00367     ostringstream insf_oss;
00368 
00369     // Arguments
00370     LongId longid;
00371     time_t timestamp = time(0);
00372     string arc = random_arc();
00373     unsigned int sid = random();
00374     bool mast = ((random() % 2) == 0) ? true : false;
00375 
00376     format_insf(longid, timestamp, arc, sid, mast, insf_tos);
00377     format_insf(longid, timestamp, arc, sid, mast, insf_tos2);
00378     format_insf(longid, timestamp, arc, sid, mast, insf_tos3);
00379     format_insf(longid, timestamp, arc, sid, mast, insf_oss);
00380 
00381     assert(strcmp(insf_oss.str().c_str(), insf_tos.str()) == 0);
00382     assert(strcmp(insf_oss.str().c_str(), insf_tos2.str()) == 0);
00383     assert(strcmp(insf_oss.str().c_str(), insf_tos3.str()) == 0);
00384     assert(insf_tos.str() == logrec);
00385     assert(insf_tos3.str() != logrec2);
00386   }
00387 
00388   // ('insu' longid arc sid master timestamp)
00389   {
00390     char logrec[512], logrec2[10];
00391     // Fixed buffer, no reallocation
00392     Basics::OBufStream insu_tos(logrec, sizeof(logrec));
00393     // No buffer
00394     Basics::OBufStream insu_tos2;
00395     // Small buffer, re-allocate when it fills
00396     Basics::OBufStream insu_tos3(logrec2, sizeof(logrec2), 0, true);
00397     ostringstream insu_oss;
00398 
00399     LongId longid;
00400     time_t timestamp = time(0);
00401     string arc = random_arc();
00402     unsigned int sid = random();
00403     bool mast = ((random() % 2) == 0) ? true : false;
00404 
00405     format_insu(longid, timestamp, arc, sid, mast, insu_tos);
00406     format_insu(longid, timestamp, arc, sid, mast, insu_tos2);
00407     format_insu(longid, timestamp, arc, sid, mast, insu_tos3);
00408     format_insu(longid, timestamp, arc, sid, mast, insu_oss);
00409 
00410     assert(strcmp(insu_oss.str().c_str(), insu_tos.str()) == 0);
00411     assert(strcmp(insu_oss.str().c_str(), insu_tos2.str()) == 0);
00412     assert(strcmp(insu_oss.str().c_str(), insu_tos3.str()) == 0);
00413     assert(insu_tos.str() == logrec);
00414     assert(insu_tos3.str() != logrec2);
00415   }
00416 }
00417 
00418 void input_tests()
00419 {
00420   // Simple test: write an integer string into a character buffer, and
00421   // read it back using an IBufStream.  The integer we read back
00422   // should the the one we put in, and we should read the entire
00423   // string.
00424   {
00425     char buf[20];
00426     int r = random();
00427     sprintf(buf, "%d", r);
00428 
00429     Basics::IBufStream in_test(buf, sizeof(buf), strlen(buf));
00430 
00431     int r2;
00432     in_test >> r2;
00433 
00434     assert(r == r2);
00435     assert((unsigned int) in_test.tellg() == strlen(buf));
00436   }
00437 
00438   // Test based on evaluator unpickling code: reading binary data out
00439   // of an IBufStream.
00440   {
00441     char data[] = { 0x0, 0x0, 0x0, 0x2, 0x1, 0x4e, 0x0, 0x0,
00442                     0x0, 0x3, 0x4e, 0x0, 0x5, 0x45, 0x0, 0x1,
00443                     0x4c, 0x0, 0x6 };
00444     Basics::IBufStream in(data, sizeof(data), sizeof(data));
00445 
00446     // First four bytes are the pickle version
00447     Basics::uint32 pVers;
00448     in.read((char *) &pVers, sizeof(pVers));
00449     assert(in.gcount() == sizeof(pVers));
00450     assert(Basics::ntoh32(pVers) == 2);
00451 
00452     // Next byte is a boolean
00453     bool8 hasPath;
00454     in.read((char *) &hasPath, sizeof(hasPath));
00455     assert(in.gcount() == sizeof(hasPath));
00456     assert(hasPath == 1);
00457 
00458     // Next byte is a path kind
00459     char pk;
00460     in.read(&pk, sizeof(pk));
00461     assert(in.gcount() == sizeof(pk));
00462     assert(pk == 'N');
00463 
00464     // Next two bytes are an index
00465     Basics::uint16 index;
00466     in.read((char *) &index, sizeof(index));
00467     assert(in.gcount() == sizeof(index));
00468     assert(index == 0);
00469 
00470     // Next two bytes are the DPS length
00471     Basics::uint16 dpsLen;
00472     in.read((char *) &dpsLen, sizeof(dpsLen));
00473     assert(in.gcount() == sizeof(dpsLen));
00474     assert(Basics::ntoh16(dpsLen) == 3);
00475 
00476     // First DPS: N, 5
00477     in.read(&pk, sizeof(pk));
00478     assert(in.gcount() == sizeof(pk));
00479     assert(pk == 'N');
00480 
00481     in.read((char *) &index, sizeof(index));
00482     assert(in.gcount() == sizeof(index));
00483     assert(Basics::ntoh16(index) == 5);
00484 
00485     // Second DPS: E, 1
00486     in.read(&pk, sizeof(pk));
00487     assert(in.gcount() == sizeof(pk));
00488     assert(pk == 'E');
00489 
00490     in.read((char *) &index, sizeof(index));
00491     assert(in.gcount() == sizeof(index));
00492     assert(Basics::ntoh16(index) == 1);
00493 
00494     // Third DPS: L, 6
00495     in.read(&pk, sizeof(pk));
00496     assert(in.gcount() == sizeof(pk));
00497     assert(pk == 'L');
00498 
00499     in.read((char *) &index, sizeof(index));
00500     assert(in.gcount() == sizeof(index));
00501     assert(Basics::ntoh16(index) == 6);
00502 
00503     // At the end, we should have read the whole thing.
00504     assert((unsigned int) in.tellg() == sizeof(data));
00505   }
00506 }
00507 
00508 void io_tests()
00509 {
00510   // Test using a bi-directional BufStream: write a random integer,
00511   // then read it back.
00512   for(unsigned int i = 0; i < 100; i++)
00513     {
00514       int r = random();
00515       Basics::BufStream test;
00516       test << r;
00517       int r2;
00518       test >> r2;
00519       assert(r == r2);
00520       assert(test.tellp() == test.tellg());
00521     }
00522 
00523   // Similar test, but reading and writing many with one stream.
00524   {
00525     Basics::BufStream test(10);
00526     for(unsigned int i = 0; i < 100; i++)
00527       {
00528         int r = random();
00529         test << r << endl;
00530         int r2;
00531         test >> r2;
00532         char c;
00533         test.get(c);
00534         assert(r == r2);
00535         assert(c == '\n');
00536         assert(test.tellp() == test.tellg());
00537       }
00538   }
00539 }
00540 
00541 int main(int argc, char* argv[])
00542 {
00543   srandom(time(0));
00544 
00545   output_tests();
00546 
00547   input_tests();
00548 
00549   io_tests();
00550 
00551   cout << "All tests passed!" << endl;
00552 
00553   return 0;
00554 }

Generated on Mon May 8 00:48:29 2006 for Vesta by  doxygen 1.4.2