]> git.saurik.com Git - wxWidgets.git/blobdiff - tests/archive/archivetest.cpp
Reviewed and cleaned up the rest of the graphics.h interface header.
[wxWidgets.git] / tests / archive / archivetest.cpp
index b6b20c7aca5903dec03c4bec42a4e4b404f1e1cf..1d8853945a9468d1720c964d5b4b1ebbaa8f9f79 100644 (file)
@@ -17,9 +17,7 @@
 #   include "wx/wx.h"
 #endif
 
-#if wxUSE_STREAMS
-
-#define WX_TEST_ARCHIVE_ITERATOR
+#if wxUSE_STREAMS && wxUSE_ARCHIVE_STREAMS
 
 // VC++ 6 warns that the list iterator's '->' operator will not work whenever
 // std::list is used with a non-pointer, so switch it off.
@@ -27,9 +25,7 @@
 #pragma warning (disable:4284)
 #endif
 
-#include "wx/zipstrm.h"
-#include "wx/mstream.h"
-#include "wx/wfstream.h"
+#include "archivetest.h"
 #include "wx/dir.h"
 #include <string>
 #include <list>
@@ -46,7 +42,7 @@ using std::auto_ptr;
     (__GNUC__ >= 3 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95))
 #   define WXARC_MEMBER_TEMPLATES
 #endif
-#if defined _MSC_VER && _MSC_VER >= 1310
+#if defined _MSC_VER && _MSC_VER >= 1310 && !defined __WIN64__
 #   define WXARC_MEMBER_TEMPLATES
 #endif
 #if defined __BORLANDC__ && __BORLANDC__ >= 0x530
@@ -66,74 +62,9 @@ using std::auto_ptr;
 #endif
 
 
-///////////////////////////////////////////////////////////////////////////////
-// Bit flags for options for the tests
-
-enum Options
-{
-    PipeIn      = 0x01,     // input streams are non-seekable
-    PipeOut     = 0x02,     // output streams are non-seekable
-    Stub        = 0x04,     // the archive should be appended to a stub
-    AllOptions  = 0x07
-};
-
-
-///////////////////////////////////////////////////////////////////////////////
-// These structs are passed as the template parameter of the test case to
-// specify a set of classes to use in the test. This allows either the generic
-// wxArchiveXXX interface to be exercised or the specific interface for a
-// particular archive format e.g. wxZipXXX.
-
-struct ArchiveClasses
-{
-    typedef wxArchiveEntry EntryT;
-    typedef wxArchiveInputStream InputStreamT;
-    typedef wxArchiveOutputStream OutputStreamT;
-    typedef wxArchiveClassFactory ClassFactoryT;
-    typedef wxArchiveNotifier NotifierT;
-    typedef wxArchiveIter IterT;
-    typedef wxArchivePairIter PairIterT;
-};
-
-struct ZipClasses
-{
-    typedef wxZipEntry EntryT;
-    typedef wxZipInputStream InputStreamT;
-    typedef wxZipOutputStream OutputStreamT;
-    typedef wxZipClassFactory ClassFactoryT;
-    typedef wxZipNotifier NotifierT;
-    typedef wxZipIter IterT;
-    typedef wxZipPairIter PairIterT;
-};
-
-
 ///////////////////////////////////////////////////////////////////////////////
 // A class to hold a test entry
 
-class TestEntry
-{
-public:
-    TestEntry(const wxDateTime& dt, int len, const char *data);
-    ~TestEntry() { delete [] m_data; }
-
-    wxDateTime GetDateTime() const  { return m_dt; }
-    wxFileOffset GetLength() const  { return m_len; }
-    size_t GetSize() const          { return m_len; }
-    const char *GetData() const     { return m_data; }
-    wxString GetComment() const     { return m_comment; }
-    bool IsText() const             { return m_isText; }
-
-    void SetComment(const wxString& comment) { m_comment = comment; }
-    void SetDateTime(const wxDateTime& dt)   { m_dt = dt; }
-
-private:
-    wxDateTime m_dt;
-    size_t m_len;
-    char *m_data;
-    wxString m_comment;
-    bool m_isText;
-};
-
 TestEntry::TestEntry(const wxDateTime& dt, int len, const char *data)
   : m_dt(dt),
     m_len(len),
@@ -151,34 +82,9 @@ TestEntry::TestEntry(const wxDateTime& dt, int len, const char *data)
 // TestOutputStream and TestInputStream are memory streams which can be
 // seekable or non-seekable.
 
-class TestOutputStream : public wxOutputStream
-{
-public:
-    TestOutputStream(int options);
-
-    ~TestOutputStream() { delete [] m_data; }
-
-    int GetOptions() const { return m_options; }
-    wxFileOffset GetLength() const { return m_size; }
-
-    // gives away the data, this stream is then empty, and can be reused
-    void GetData(char*& data, size_t& size);
-
-    enum { STUB_SIZE = 2048, INITIAL_SIZE = 0x18000, SEEK_LIMIT = 0x100000 };
-
-private:
-    void Init();
-
-    wxFileOffset OnSysSeek(wxFileOffset pos, wxSeekMode mode);
-    wxFileOffset OnSysTell() const;
-    size_t OnSysWrite(const void *buffer, size_t size);
-
-    int m_options;
-    size_t m_pos;
-    size_t m_capacity;
-    size_t m_size;
-    char *m_data;
-};
+const size_t STUB_SIZE = 2048;
+const size_t INITIAL_SIZE = 0x18000;
+const wxFileOffset SEEK_LIMIT = 0x100000;
 
 TestOutputStream::TestOutputStream(int options)
   : m_options(options)
@@ -281,34 +187,17 @@ void TestOutputStream::GetData(char*& data, size_t& size)
     Reset();
 }
 
-class TestInputStream : public wxInputStream
-{
-public:
-    // ctor takes the data from the output stream, which is then empty
-    TestInputStream(TestOutputStream& out) : m_data(NULL) { SetData(out); }
-    // this ctor 'dups'
-    TestInputStream(const TestInputStream& in);
-    ~TestInputStream() { delete [] m_data; }
-
-    void Rewind();
-    wxFileOffset GetLength() const { return m_size; }
-    void SetData(TestOutputStream& out);
 
-private:
-    wxFileOffset OnSysSeek(wxFileOffset pos, wxSeekMode mode);
-    wxFileOffset OnSysTell() const;
-    size_t OnSysRead(void *buffer, size_t size);
-
-    int m_options;
-    size_t m_pos;
-    size_t m_size;
-    char *m_data;
-};
+///////////////////////////////////////////////////////////////////////////////
+// TestOutputStream and TestInputStream are memory streams which can be
+// seekable or non-seekable.
 
 TestInputStream::TestInputStream(const TestInputStream& in)
-  : m_options(in.m_options),
+  : wxInputStream(),
+    m_options(in.m_options),
     m_pos(in.m_pos),
-    m_size(in.m_size)
+    m_size(in.m_size),
+    m_eoftype(in.m_eoftype)
 {
     m_data = new char[m_size];
     memcpy(m_data, in.m_data, m_size);
@@ -317,7 +206,7 @@ TestInputStream::TestInputStream(const TestInputStream& in)
 void TestInputStream::Rewind()
 {
     if ((m_options & Stub) && (m_options & PipeIn))
-        m_pos = TestOutputStream::STUB_SIZE * 2;
+        m_pos = STUB_SIZE * 2;
     else
         m_pos = 0;
 
@@ -327,6 +216,8 @@ void TestInputStream::Rewind()
         m_wbacksize = 0;
         m_wbackcur = 0;
     }
+
+    Reset();
 }
 
 void TestInputStream::SetData(TestOutputStream& out)
@@ -335,7 +226,6 @@ void TestInputStream::SetData(TestOutputStream& out)
     m_options = out.GetOptions();
     out.GetData(m_data, m_size);
     Rewind();
-    Reset();
 }
 
 wxFileOffset TestInputStream::OnSysSeek(wxFileOffset pos, wxSeekMode mode)
@@ -346,7 +236,7 @@ wxFileOffset TestInputStream::OnSysSeek(wxFileOffset pos, wxSeekMode mode)
             case wxFromCurrent: pos += m_pos; break;
             case wxFromEnd:     pos += m_size; break;
         }
-        if (pos < 0 || pos > TestOutputStream::SEEK_LIMIT)
+        if (pos < 0 || pos > SEEK_LIMIT)
             return wxInvalidOffset;
         m_pos = (size_t)pos;
         return m_pos;
@@ -363,16 +253,30 @@ size_t TestInputStream::OnSysRead(void *buffer, size_t size)
 {
     if (!IsOk() || !size)
         return 0;
-    if (m_size <= m_pos) {
-        m_lasterror = wxSTREAM_EOF;
-        return 0;
+
+    size_t count;
+
+    if (m_pos >= m_size)
+        count = 0;
+    else if (m_size - m_pos < size)
+        count = m_size - m_pos;
+    else
+        count = size;
+
+    if (count) {
+        memcpy(buffer, m_data + m_pos, count);
+        m_pos += count;
     }
 
-    if (m_size - m_pos < size)
-        size = m_size - m_pos;
-    memcpy(buffer, m_data + m_pos, size);
-    m_pos += size;
-    return size;
+    if (((m_eoftype & AtLast) != 0 && m_pos >= m_size) || count < size)
+    {
+        if ((m_eoftype & WithError) != 0)
+            m_lasterror = wxSTREAM_READ_ERROR;
+        else
+            m_lasterror = wxSTREAM_EOF;
+    }
+
+    return count;
 }
 
 
@@ -432,7 +336,7 @@ private:
 TempDir::TempDir()
 {
     wxString tmp = wxFileName::CreateTempFileName(_T("arctest-"));
-    if (tmp != wxEmptyString) {
+    if (!tmp.empty()) {
         wxRemoveFile(tmp);
         m_original = wxGetCwd();
         CPPUNIT_ASSERT(wxMkdir(tmp, 0700));
@@ -443,7 +347,7 @@ TempDir::TempDir()
 
 TempDir::~TempDir()
 {
-    if (m_tmp != wxEmptyString) {
+    if (!m_tmp.empty()) {
         wxSetWorkingDirectory(m_original);
         RemoveDir(m_tmp);
     }
@@ -509,143 +413,59 @@ void TempDir::RemoveDir(wxString& path)
 #   define WXARC_b
 #endif
 
-class PFileInputStream : public wxFFileInputStream
+PFileInputStream::PFileInputStream(const wxString& cmd)
+  : wxFFileInputStream(WXARC_popen(cmd.mb_str(), "r" WXARC_b))
 {
-public:
-    PFileInputStream(const wxString& cmd) :
-        wxFFileInputStream(WXARC_popen(cmd.mb_str(), "r" WXARC_b)) { }
-    ~PFileInputStream()
-        { WXARC_pclose(m_file->fp()); m_file->Detach(); }
-};
+}
 
-class PFileOutputStream : public wxFFileOutputStream
+PFileInputStream::~PFileInputStream()
 {
-public:
-    PFileOutputStream(const wxString& cmd) :
-        wxFFileOutputStream(WXARC_popen(cmd.mb_str(), "w" WXARC_b)) { }
-    ~PFileOutputStream()
-        { WXARC_pclose(m_file->fp()); m_file->Detach(); }
-};
-
-
-///////////////////////////////////////////////////////////////////////////////
-// The test case
+    WXARC_pclose(m_file->fp()); m_file->Detach();
+}
 
-template <class Classes>
-class ArchiveTestCase : public CppUnit::TestCase
+PFileOutputStream::PFileOutputStream(const wxString& cmd)
+: wxFFileOutputStream(WXARC_popen(cmd.mb_str(), "w" WXARC_b))
 {
-public:
-    ArchiveTestCase(string name,
-                    int id,
-                    wxArchiveClassFactory *factory,
-                    int options,
-                    const wxString& archiver = wxEmptyString,
-                    const wxString& unarchiver = wxEmptyString);
-
-    ~ArchiveTestCase();
-
-protected:
-    // the classes to test
-    typedef typename Classes::EntryT EntryT;
-    typedef typename Classes::InputStreamT InputStreamT;
-    typedef typename Classes::OutputStreamT OutputStreamT;
-    typedef typename Classes::ClassFactoryT ClassFactoryT;
-    typedef typename Classes::NotifierT NotifierT;
-    typedef typename Classes::IterT IterT;
-    typedef typename Classes::PairIterT PairIterT;
-
-    // the entry point for the test
-    void runTest();
-
-    // create the test data
-    void CreateTestData();
-    TestEntry& Add(const char *name, const char *data, int len = -1);
-    TestEntry& Add(const char *name, int len = 0, int value = EOF);
+}
 
-    // 'archive up' the test data
-    void CreateArchive(wxOutputStream& out);
-    void CreateArchive(wxOutputStream& out, const wxString& archiver);
+PFileOutputStream::~PFileOutputStream()
+{
+    WXARC_pclose(m_file->fp()); m_file->Detach();
+}
 
-    // perform various modifications on the archive
-    void ModifyArchive(wxInputStream& in, wxOutputStream& out);
 
-    // extract the archive and verify its contents
-    void ExtractArchive(wxInputStream& in);
-    void ExtractArchive(wxInputStream& in, const wxString& unarchiver);
-    void VerifyDir(wxString& path, size_t rootlen = 0);
-
-    // tests for the iterators
-    void TestIterator(wxInputStream& in);
-    void TestPairIterator(wxInputStream& in);
-    void TestSmartIterator(wxInputStream& in);
-    void TestSmartPairIterator(wxInputStream& in);
-
-    // try reading two entries at the same time
-    void ReadSimultaneous(TestInputStream& in);
-
-    // overridables
-    virtual void OnCreateArchive(OutputStreamT& WXUNUSED(arc)) { }
-    virtual void OnSetNotifier(EntryT& entry);
-
-    virtual void OnArchiveExtracted(InputStreamT& WXUNUSED(arc),
-                                    int WXUNUSED(expectedTotal)) { }
-
-    virtual void OnCreateEntry(     OutputStreamT& WXUNUSED(arc),
-                                    TestEntry& WXUNUSED(testEntry),
-                                    EntryT *entry = NULL) { (void)entry; }
-
-    virtual void OnEntryExtracted(  EntryT& WXUNUSED(entry),
-                                    const TestEntry& WXUNUSED(testEntry),
-                                    InputStreamT *arc = NULL) { (void)arc; }
-
-    typedef std::map<wxString, TestEntry*> TestEntries;
-    TestEntries m_testEntries;              // test data
-    auto_ptr<ClassFactoryT> m_factory;      // factory to make classes
-    int m_options;                          // test options
-    wxDateTime m_timeStamp;                 // timestamp to give test entries
-    int m_id;                               // select between the possibilites
-    wxString m_archiver;                    // external archiver
-    wxString m_unarchiver;                  // external unarchiver
-};
+///////////////////////////////////////////////////////////////////////////////
+// The test case
 
-// Constructor
-// The only way I could get this to compile on VC++ 5.0 was to pass 'factory'
-// as a wxArchiveFactory* then cast it, even then only with some ifdefing.
-//
-template <class Classes>
-ArchiveTestCase<Classes>::ArchiveTestCase(
+template <class ClassFactoryT>
+ArchiveTestCase<ClassFactoryT>::ArchiveTestCase(
     string name,
-    int id,
-    wxArchiveClassFactory *factory,
+    ClassFactoryT *factory,
     int options,
     const wxString& archiver,
     const wxString& unarchiver)
   :
-    CppUnit::TestCase(name),
-#if defined _MSC_VER && _MSC_VER < 1300
-    m_factory(dynamic_cast<Classes::ClassFactoryT*>(factory)),
-#else
-    m_factory(dynamic_cast<typename Classes::ClassFactoryT*>(factory)),
-#endif
+    CppUnit::TestCase(TestId::MakeId() + name),
+    m_factory(factory),
     m_options(options),
-    m_timeStamp(1, wxDateTime::Mar, 2005, 12, 0),
-    m_id(id),
+    m_timeStamp(1, wxDateTime::Mar, 2004, 12, 0),
+    m_id(TestId::GetId()),
     m_archiver(archiver),
     m_unarchiver(unarchiver)
 {
     wxASSERT(m_factory.get() != NULL);
 }
-    
-template <class Classes>
-ArchiveTestCase<Classes>::~ArchiveTestCase()
+
+template <class ClassFactoryT>
+ArchiveTestCase<ClassFactoryT>::~ArchiveTestCase()
 {
     TestEntries::iterator it;
     for (it = m_testEntries.begin(); it != m_testEntries.end(); ++it)
         delete it->second;
 }
 
-template <class Classes>
-void ArchiveTestCase<Classes>::runTest()
+template <class ClassFactoryT>
+void ArchiveTestCase<ClassFactoryT>::runTest()
 {
     TestOutputStream out(m_options);
 
@@ -659,7 +479,7 @@ void ArchiveTestCase<Classes>::runTest()
     // check archive could be created
     CPPUNIT_ASSERT(out.GetLength() > 0);
 
-    TestInputStream in(out);
+    TestInputStream in(out, m_id % ((m_options & PipeIn) ? 4 : 3));
 
     TestIterator(in);
     in.Rewind();
@@ -682,13 +502,13 @@ void ArchiveTestCase<Classes>::runTest()
         ExtractArchive(in);
     else
         ExtractArchive(in, m_unarchiver);
-    
+
     // check that all the test entries were found in the archive
     CPPUNIT_ASSERT(m_testEntries.empty());
 }
 
-template <class Classes>
-void ArchiveTestCase<Classes>::CreateTestData()
+template <class ClassFactoryT>
+void ArchiveTestCase<ClassFactoryT>::CreateTestData()
 {
     Add("text/");
     Add("text/empty", "");
@@ -711,10 +531,10 @@ void ArchiveTestCase<Classes>::CreateTestData()
     Add("empty/");
 }
 
-template <class Classes>
-TestEntry& ArchiveTestCase<Classes>::Add(const char *name,
-                                         const char *data,
-                                         int len /*=-1*/)
+template <class ClassFactoryT>
+TestEntry& ArchiveTestCase<ClassFactoryT>::Add(const char *name,
+                                               const char *data,
+                                               int len /*=-1*/)
 {
     if (len == -1)
         len = strlen(data);
@@ -725,10 +545,10 @@ TestEntry& ArchiveTestCase<Classes>::Add(const char *name,
     return *entry;
 }
 
-template <class Classes>
-TestEntry& ArchiveTestCase<Classes>::Add(const char *name,
-                                         int len /*=0*/,
-                                         int value /*=EOF*/)
+template <class ClassFactoryT>
+TestEntry& ArchiveTestCase<ClassFactoryT>::Add(const char *name,
+                                               int len /*=0*/,
+                                               int value /*=EOF*/)
 {
     wxCharBuffer buf(len);
     for (int i = 0; i < len; i++)
@@ -738,8 +558,8 @@ TestEntry& ArchiveTestCase<Classes>::Add(const char *name,
 
 // Create an archive using the wx archive classes, write it to 'out'
 //
-template <class Classes>
-void ArchiveTestCase<Classes>::CreateArchive(wxOutputStream& out)
+template <class ClassFactoryT>
+void ArchiveTestCase<ClassFactoryT>::CreateArchive(wxOutputStream& out)
 {
     auto_ptr<OutputStreamT> arc(m_factory->NewStream(out));
     TestEntries::iterator it;
@@ -791,7 +611,7 @@ void ArchiveTestCase<Classes>::CreateArchive(wxOutputStream& out)
                                       testEntry.GetLength()));
         }
 
-        if (name.Last() != _T('/')) {
+        if (it->first.Last() != _T('/')) {
             // for non-dirs write the data
             arc->Write(testEntry.GetData(), testEntry.GetSize());
             CPPUNIT_ASSERT_MESSAGE("LastWrite check" + error_context,
@@ -812,9 +632,9 @@ void ArchiveTestCase<Classes>::CreateArchive(wxOutputStream& out)
 
 // Create an archive using an external archive program
 //
-template <class Classes>
-void ArchiveTestCase<Classes>::CreateArchive(wxOutputStream& out,
-                                             const wxString& archiver)
+template <class ClassFactoryT>
+void ArchiveTestCase<ClassFactoryT>::CreateArchive(wxOutputStream& out,
+                                                   const wxString& archiver)
 {
     // for an external archiver the test data need to be written to
     // temp files
@@ -850,7 +670,7 @@ void ArchiveTestCase<Classes>::CreateArchive(wxOutputStream& out,
     if ((m_options & PipeOut) == 0) {
         wxFileName fn(tmpdir.GetName());
         fn.SetExt(_T("arc"));
-        wxString tmparc = fn.GetFullPath();
+        wxString tmparc = fn.GetPath(wxPATH_GET_SEPARATOR) + fn.GetFullName();
 
         // call the archiver to create an archive file
         system(wxString::Format(archiver, tmparc.c_str()).mb_str());
@@ -876,9 +696,9 @@ void ArchiveTestCase<Classes>::CreateArchive(wxOutputStream& out,
 // Do a standard set of modification on an archive, delete an entry,
 // rename an entry and add an entry
 //
-template <class Classes>
-void ArchiveTestCase<Classes>::ModifyArchive(wxInputStream& in,
-                                             wxOutputStream& out)
+template <class ClassFactoryT>
+void ArchiveTestCase<ClassFactoryT>::ModifyArchive(wxInputStream& in,
+                                                   wxOutputStream& out)
 {
     auto_ptr<InputStreamT> arcIn(m_factory->NewStream(in));
     auto_ptr<OutputStreamT> arcOut(m_factory->NewStream(out));
@@ -954,8 +774,8 @@ void ArchiveTestCase<Classes>::ModifyArchive(wxInputStream& in,
 
 // Extract an archive using the wx archive classes
 //
-template <class Classes>
-void ArchiveTestCase<Classes>::ExtractArchive(wxInputStream& in)
+template <class ClassFactoryT>
+void ArchiveTestCase<ClassFactoryT>::ExtractArchive(wxInputStream& in)
 {
     typedef Ptr<EntryT> EntryPtr;
     typedef std::list<EntryPtr> Entries;
@@ -1049,9 +869,9 @@ void ArchiveTestCase<Classes>::ExtractArchive(wxInputStream& in)
 
 // Extract an archive using an external unarchive program
 //
-template <class Classes>
-void ArchiveTestCase<Classes>::ExtractArchive(wxInputStream& in,
-                                              const wxString& unarchiver)
+template <class ClassFactoryT>
+void ArchiveTestCase<ClassFactoryT>::ExtractArchive(wxInputStream& in,
+                                                    const wxString& unarchiver)
 {
     // for an external unarchiver, unarchive to a tempdir
     TempDir tmpdir;
@@ -1059,10 +879,10 @@ void ArchiveTestCase<Classes>::ExtractArchive(wxInputStream& in,
     if ((m_options & PipeIn) == 0) {
         wxFileName fn(tmpdir.GetName());
         fn.SetExt(_T("arc"));
-        wxString tmparc = fn.GetFullPath();
-        
+        wxString tmparc = fn.GetPath(wxPATH_GET_SEPARATOR) + fn.GetFullName();
+
         if (m_options & Stub)
-            in.SeekI(TestOutputStream::STUB_SIZE * 2);
+            in.SeekI(STUB_SIZE * 2);
 
         // write the archive to a temporary file
         {
@@ -1089,8 +909,9 @@ void ArchiveTestCase<Classes>::ExtractArchive(wxInputStream& in,
 
 // Verifies the files produced by an external unarchiver are as expected
 //
-template <class Classes>
-void ArchiveTestCase<Classes>::VerifyDir(wxString& path, size_t rootlen /*=0*/)
+template <class ClassFactoryT>
+void ArchiveTestCase<ClassFactoryT>::VerifyDir(wxString& path,
+                                               size_t rootlen /*=0*/)
 {
     wxDir dir;
     path += wxFileName::GetPathSeparator();
@@ -1123,7 +944,7 @@ void ArchiveTestCase<Classes>::VerifyDir(wxString& path, size_t rootlen /*=0*/)
 
             const TestEntry& testEntry = *it->second;
 
-#ifndef __WXMSW__
+#if 0 //ndef __WXMSW__
             CPPUNIT_ASSERT_MESSAGE("timestamp check" + error_context,
                                    testEntry.GetDateTime() ==
                                    wxFileName(path).GetModificationTime());
@@ -1155,8 +976,8 @@ void ArchiveTestCase<Classes>::VerifyDir(wxString& path, size_t rootlen /*=0*/)
 
 // test the simple iterators that give away ownership of an entry
 //
-template <class Classes>
-void ArchiveTestCase<Classes>::TestIterator(wxInputStream& in)
+template <class ClassFactoryT>
+void ArchiveTestCase<ClassFactoryT>::TestIterator(wxInputStream& in)
 {
     typedef std::list<EntryT*> ArchiveCatalog;
     typedef typename ArchiveCatalog::iterator CatalogIter;
@@ -1184,8 +1005,8 @@ void ArchiveTestCase<Classes>::TestIterator(wxInputStream& in)
 // test the pair iterators that can be used to load a std::map or wxHashMap
 // these also give away ownership of entries
 //
-template <class Classes>
-void ArchiveTestCase<Classes>::TestPairIterator(wxInputStream& in)
+template <class ClassFactoryT>
+void ArchiveTestCase<ClassFactoryT>::TestPairIterator(wxInputStream& in)
 {
     typedef std::map<wxString, EntryT*> ArchiveCatalog;
     typedef typename ArchiveCatalog::iterator CatalogIter;
@@ -1212,8 +1033,8 @@ void ArchiveTestCase<Classes>::TestPairIterator(wxInputStream& in)
 
 // simple iterators using smart pointers, no need to worry about ownership
 //
-template <class Classes>
-void ArchiveTestCase<Classes>::TestSmartIterator(wxInputStream& in)
+template <class ClassFactoryT>
+void ArchiveTestCase<ClassFactoryT>::TestSmartIterator(wxInputStream& in)
 {
     typedef std::list<Ptr<EntryT> > ArchiveCatalog;
     typedef typename ArchiveCatalog::iterator CatalogIter;
@@ -1237,8 +1058,8 @@ void ArchiveTestCase<Classes>::TestSmartIterator(wxInputStream& in)
 
 // pair iterator using smart pointers
 //
-template <class Classes>
-void ArchiveTestCase<Classes>::TestSmartPairIterator(wxInputStream& in)
+template <class ClassFactoryT>
+void ArchiveTestCase<ClassFactoryT>::TestSmartPairIterator(wxInputStream& in)
 {
 #if defined _MSC_VER && defined _MSC_VER < 1200
     // With VC++ 5.0 the '=' operator of std::pair breaks when the second
@@ -1269,8 +1090,8 @@ void ArchiveTestCase<Classes>::TestSmartPairIterator(wxInputStream& in)
 
 // try reading two entries at the same time
 //
-template <class Classes>
-void ArchiveTestCase<Classes>::ReadSimultaneous(TestInputStream& in)
+template <class ClassFactoryT>
+void ArchiveTestCase<ClassFactoryT>::ReadSimultaneous(TestInputStream& in)
 {
     typedef std::map<wxString, Ptr<EntryT> > ArchiveCatalog;
     typedef wxArchiveIterator<InputStreamT,
@@ -1342,8 +1163,8 @@ public:
     void OnEntryUpdated(EntryT& WXUNUSED(entry)) { }
 };
 
-template <class Classes>
-void ArchiveTestCase<Classes>::OnSetNotifier(EntryT& entry)
+template <class ClassFactoryT>
+void ArchiveTestCase<ClassFactoryT>::OnSetNotifier(EntryT& entry)
 {
     static ArchiveNotifier<NotifierT, EntryT> notifier;
     entry.SetNotifier(notifier);
@@ -1351,211 +1172,114 @@ void ArchiveTestCase<Classes>::OnSetNotifier(EntryT& entry)
 
 
 ///////////////////////////////////////////////////////////////////////////////
-// ArchiveTestCase<ZipClasses> could be used directly, but instead this
-// derived class is used so that zip specific features can be tested.
+// An additional case to check that reading corrupt archives doesn't crash
 
-class ZipTestCase : public ArchiveTestCase<ZipClasses>
+class CorruptionTestCase : public CppUnit::TestCase
 {
 public:
-    ZipTestCase(string name,
-                int id,
-                int options,
-                const wxString& archiver = wxEmptyString,
-                const wxString& unarchiver = wxEmptyString)
-    :
-        ArchiveTestCase<ZipClasses>(name, id, new wxZipClassFactory,
-                                    options, archiver, unarchiver),
-        m_count(0)
+    CorruptionTestCase(std::string name,
+                       wxArchiveClassFactory *factory,
+                       int options)
+      : CppUnit::TestCase(TestId::MakeId() + name),
+        m_factory(factory),
+        m_options(options)
     { }
 
 protected:
-    void OnCreateArchive(wxZipOutputStream& zip);
-    
-    void OnArchiveExtracted(wxZipInputStream& zip, int expectedTotal);
-    
-    void OnCreateEntry(wxZipOutputStream& zip,
-                       TestEntry& testEntry,
-                       wxZipEntry *entry);
-    
-    void OnEntryExtracted(wxZipEntry& entry,
-                          const TestEntry& testEntry,
-                          wxZipInputStream *arc);
-
-    void OnSetNotifier(EntryT& entry);
-
-    int m_count;
-    wxString m_comment;
-};
+    // the entry point for the test
+    void runTest();
 
-void ZipTestCase::OnCreateArchive(wxZipOutputStream& zip)
-{
-    m_comment << _T("Comment for test ") << m_id;
-    zip.SetComment(m_comment);
-}
+    void CreateArchive(wxOutputStream& out);
+    void ExtractArchive(wxInputStream& in);
 
-void ZipTestCase::OnArchiveExtracted(wxZipInputStream& zip, int expectedTotal)
-{
-    CPPUNIT_ASSERT(zip.GetComment() == m_comment);
-    CPPUNIT_ASSERT(zip.GetTotalEntries() == expectedTotal);
-}
+    auto_ptr<wxArchiveClassFactory> m_factory;  // factory to make classes
+    int m_options;                              // test options
+};
 
-void ZipTestCase::OnCreateEntry(wxZipOutputStream& zip,
-                                TestEntry& testEntry,
-                                wxZipEntry *entry)
+void CorruptionTestCase::runTest()
 {
-    zip.SetLevel((m_id + m_count) % 10);
-
-    if (entry) {
-        switch ((m_id + m_count) % 5) {
-            case 0:
-            {
-                wxString comment = _T("Comment for ") + entry->GetName();
-                entry->SetComment(comment);
-                // lowercase the expected result, and the notifier should do
-                // the same for the zip entries when ModifyArchive() runs
-                testEntry.SetComment(comment.Lower());
-                break;
-            }
-            case 2:
-                entry->SetMethod(wxZIP_METHOD_STORE);
-                break;
-            case 4:
-                entry->SetMethod(wxZIP_METHOD_DEFLATE);
-                break;
-        }
-        entry->SetIsText(testEntry.IsText());
+    TestOutputStream out(m_options);
+    CreateArchive(out);
+    TestInputStream in(out, 0);
+    wxFileOffset len = in.GetLength();
+
+    // try flipping one byte in the archive
+    int pos;
+    for (pos = 0; pos < len; pos++) {
+        char n = in[pos];
+        in[pos] = ~n;
+        ExtractArchive(in);
+        in.Rewind();
+        in[pos] = n;
     }
 
-    m_count++;
-}
+    // try zeroing one byte in the archive
+    for (pos = 0; pos < len; pos++) {
+        char n = in[pos];
+        in[pos] = 0;
+        ExtractArchive(in);
+        in.Rewind();
+        in[pos] = n;
+    }
 
-void ZipTestCase::OnEntryExtracted(wxZipEntry& entry,
-                                   const TestEntry& testEntry,
-                                   wxZipInputStream *arc)
-{
-    // provide some context for the error message so that we know which
-    // iteration of the loop we were on
-    wxString name = _T(" '") + entry.GetName() + _T("'");
-    string error_entry(name.mb_str());
-    string error_context(" failed for entry" + error_entry);
-
-    CPPUNIT_ASSERT_MESSAGE("GetComment" + error_context,
-        entry.GetComment() == testEntry.GetComment());
-
-    // for seekable streams, GetNextEntry() doesn't read the local header so
-    // call OpenEntry() to do it
-    if (arc && (m_options & PipeIn) == 0 && entry.IsDir())
-        arc->OpenEntry(entry);
-
-    CPPUNIT_ASSERT_MESSAGE("IsText" + error_context,
-                           entry.IsText() == testEntry.IsText());
-
-    CPPUNIT_ASSERT_MESSAGE("Extra/LocalExtra mismatch for entry" + error_entry,
-        (entry.GetExtraLen() != 0 && entry.GetLocalExtraLen() != 0) ||
-        (entry.GetExtraLen() == 0 && entry.GetLocalExtraLen() == 0));
+    // try chopping the archive off
+    for (int size = 1; size <= len; size++) {
+        in.Chop(size);
+        ExtractArchive(in);
+        in.Rewind();
+    }
 }
 
-// check the notifier mechanism by using it to fold the entry comments to
-// lowercase
-//
-class ZipNotifier : public wxZipNotifier
+void CorruptionTestCase::CreateArchive(wxOutputStream& out)
 {
-public:
-    void OnEntryUpdated(wxZipEntry& entry);
-};
+    auto_ptr<wxArchiveOutputStream> arc(m_factory->NewStream(out));
 
-void ZipNotifier::OnEntryUpdated(wxZipEntry& entry)
-{
-    entry.SetComment(entry.GetComment().Lower());
+    arc->PutNextDirEntry(_T("dir"));
+    arc->PutNextEntry(_T("file"));
+    arc->Write(_T("foo"), 3);
 }
 
-void ZipTestCase::OnSetNotifier(EntryT& entry)
+void CorruptionTestCase::ExtractArchive(wxInputStream& in)
 {
-    static ZipNotifier notifier;
-    entry.SetNotifier(notifier);
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-// 'zip - -' produces local headers without the size field set. This is a
-// case not covered by all the other tests, so this class tests it as a
-// special case
-
-class ZipPipeTestCase : public CppUnit::TestCase
-{
-public:
-    ZipPipeTestCase(string name, int options) :
-        CppUnit::TestCase(name), m_options(options) { }
-
-protected:
-    void runTest();
-    int m_options;
-};
+    auto_ptr<wxArchiveInputStream> arc(m_factory->NewStream(in));
+    auto_ptr<wxArchiveEntry> entry(arc->GetNextEntry());
 
-void ZipPipeTestCase::runTest()
-{
-    TestOutputStream out(m_options);
+    while (entry.get() != NULL) {
+        char buf[1024];
 
-    wxString testdata = _T("test data to pipe through zip");
-    wxString cmd = _T("echo ") + testdata + _T(" | zip -q - -");
+        while (arc->IsOk())
+            arc->Read(buf, sizeof(buf));
 
-    {
-        PFileInputStream in(cmd);
-        if (in.Ok())
-            out.Write(in);
+        auto_ptr<wxArchiveEntry> next(arc->GetNextEntry());
+        entry = next;
     }
+}
 
-    TestInputStream in(out);
-    wxZipInputStream zip(in);
-
-    auto_ptr<wxZipEntry> entry(zip.GetNextEntry());
-    CPPUNIT_ASSERT(entry.get() != NULL);
 
-    if ((m_options & PipeIn) == 0)
-        CPPUNIT_ASSERT(entry->GetSize() != wxInvalidOffset);
-
-    char buf[64];
-    size_t len = zip.Read(buf, sizeof(buf) - 1).LastRead();
+///////////////////////////////////////////////////////////////////////////////
+// Make the ids
 
-    while (len > 0 && buf[len - 1] <= 32)
-        --len;
-    buf[len] = 0;
+int TestId::m_seed = 6219;
 
-    CPPUNIT_ASSERT(zip.Eof());
-    CPPUNIT_ASSERT(wxString(buf, *wxConvCurrent) == testdata);
+// static
+string TestId::MakeId()
+{
+    m_seed = (m_seed * 171) % 30269;
+    return string(wxString::Format(_T("%-6d"), m_seed).mb_str());
 }
 
 
 ///////////////////////////////////////////////////////////////////////////////
-// The suite
+// Suite base
 
-class ArchiveTestSuite : public CppUnit::TestSuite
-{
-public:
-    ArchiveTestSuite();
-    static CppUnit::Test *suite()
-        { return (new ArchiveTestSuite)->makeSuite(); }
-
-private:
-    int m_id;
-    wxPathList m_path;
-
-    ArchiveTestSuite *makeSuite();
-    void AddCmd(wxArrayString& cmdlist, const wxString& cmd);
-    bool IsInPath(const wxString& cmd);
-
-    string Description(const wxString& type,
-                       int options,
-                       bool genericInterface = false,
-                       const wxString& archiver = wxEmptyString,
-                       const wxString& unarchiver = wxEmptyString);
-};
-
-ArchiveTestSuite::ArchiveTestSuite()
-  : CppUnit::TestSuite("ArchiveTestSuite"),
-    m_id(0)
+ArchiveTestSuite::ArchiveTestSuite(string name)
+  : CppUnit::TestSuite("archive/" + name),
+    m_name(name.c_str(), *wxConvCurrent)
 {
+    m_name = _T("wx") + m_name.Left(1).Upper() + m_name.Mid(1).Lower();
     m_path.AddEnvList(_T("PATH"));
+    m_archivers.push_back(_T(""));
+    m_unarchivers.push_back(_T(""));
 }
 
 // add the command for an external archiver to the list, testing for it in
@@ -1563,8 +1287,6 @@ ArchiveTestSuite::ArchiveTestSuite()
 //
 void ArchiveTestSuite::AddCmd(wxArrayString& cmdlist, const wxString& cmd)
 {
-    if (cmdlist.empty())
-        cmdlist.push_back(_T(""));
     if (IsInPath(cmd))
         cmdlist.push_back(cmd);
 }
@@ -1583,52 +1305,59 @@ bool ArchiveTestSuite::IsInPath(const wxString& cmd)
 ArchiveTestSuite *ArchiveTestSuite::makeSuite()
 {
     typedef wxArrayString::iterator Iter;
-    wxArrayString zippers;
-    wxArrayString unzippers;
-
-    AddCmd(zippers, _T("zip -qr %s *"));
-    AddCmd(unzippers, _T("unzip -q %s"));
 
-    for (int genInterface = 0; genInterface < 2; genInterface++)
-        for (Iter i = unzippers.begin(); i != unzippers.end(); ++i)
-            for (Iter j = zippers.begin(); j != zippers.end(); ++j)
+    for (int generic = 0; generic < 2; generic++)
+        for (Iter i = m_unarchivers.begin(); i != m_unarchivers.end(); ++i)
+            for (Iter j = m_archivers.begin(); j != m_archivers.end(); ++j)
                 for (int options = 0; options <= AllOptions; options++)
                 {
-                    // unzip doesn't support piping in the zip
+#ifdef WXARC_NO_POPEN
+                    // if no popen then can't pipe in/out of archiver
                     if ((options & PipeIn) && !i->empty())
                         continue;
-#ifdef WXARC_NO_POPEN
-                    // if no popen then can use piped output of zip
                     if ((options & PipeOut) && !j->empty())
                         continue;
 #endif
-                    string name = Description(_T("wxZip"), options, 
-                                              genInterface != 0, *j, *i);
-
-                    if (genInterface)
-                        addTest(new ArchiveTestCase<ArchiveClasses>(
-                                    name, m_id,
-                                    new wxZipClassFactory,
-                                    options, *j, *i));
-                    else
-                        addTest(new ZipTestCase(name, m_id, options, *j, *i));
-
-                    m_id++;
+                    string descr = Description(m_name, options,
+                                               generic != 0, *j, *i);
+
+                    CppUnit::Test *test = makeTest(descr, options,
+                                                   generic != 0, *j, *i);
+
+                    if (test)
+                        addTest(test);
                 }
 
-#ifndef WXARC_NO_POPEN
-    // if have popen then can check the piped output of 'zip - -'
-    if (IsInPath(_T("zip")))
-        for (int options = 0; options <= PipeIn; options += PipeIn) {
-            string name = Description(_T("ZipPipeTestCase"), options);
-            addTest(new ZipPipeTestCase(name, options));
-            m_id++;
+    for (int options = 0; options <= PipeIn; options += PipeIn)
+    {
+        wxObject *pObj = wxCreateDynamicObject(m_name + _T("ClassFactory"));
+        wxArchiveClassFactory *factory;
+        factory = wxDynamicCast(pObj, wxArchiveClassFactory);
+
+        if (factory) {
+            string descr(m_name.mb_str());
+            descr = "CorruptionTestCase (" + descr + ")";
+
+            if (options)
+                descr += " (PipeIn)";
+
+            addTest(new CorruptionTestCase(descr, factory, options));
         }
-#endif
+    }
 
     return this;
 }
 
+CppUnit::Test *ArchiveTestSuite::makeTest(
+    string WXUNUSED(descr),
+    int WXUNUSED(options),
+    bool WXUNUSED(genericInterface),
+    const wxString& WXUNUSED(archiver),
+    const wxString& WXUNUSED(unarchiver))
+{
+    return NULL;
+}
+
 // make a display string for the option bits
 //
 string ArchiveTestSuite::Description(const wxString& type,
@@ -1638,18 +1367,21 @@ string ArchiveTestSuite::Description(const wxString& type,
                                      const wxString& unarchiver)
 {
     wxString descr;
-    descr << m_id << _T(" ");
-    
+
     if (genericInterface)
         descr << _T("wxArchive (") << type << _T(")");
     else
         descr << type;
 
-    if (!archiver.empty())
-        descr << _T(" ") << archiver.BeforeFirst(_T(' '));
-    if (!unarchiver.empty())
-        descr << _T(" ") << unarchiver.BeforeFirst(_T(' '));
-    
+    if (!archiver.empty()) {
+        const wxChar *fn = (options & PipeOut) != 0 ? _T("-") : _T("file");
+        descr << _T(" (") << wxString::Format(archiver, fn) << _T(")");
+    }
+    if (!unarchiver.empty()) {
+        const wxChar *fn = (options & PipeIn) != 0 ? _T("-") : _T("file");
+        descr << _T(" (") << wxString::Format(unarchiver, fn) << _T(")");
+    }
+
     wxString optstr;
 
     if ((options & PipeIn) != 0)
@@ -1663,13 +1395,23 @@ string ArchiveTestSuite::Description(const wxString& type,
 
     descr << optstr;
 
-    return (const char*)descr.mb_str();
+    return string(descr.mb_str());
 }
 
-// register in the unnamed registry so that these tests are run by default
-CPPUNIT_TEST_SUITE_REGISTRATION(ArchiveTestSuite);
 
-// also include in it's own registry so that these tests can be run alone
-CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(ArchiveTestSuite, "ArchiveTestSuite");
+///////////////////////////////////////////////////////////////////////////////
+// Instantiations
+
+template class ArchiveTestCase<wxArchiveClassFactory>;
+
+#if wxUSE_ZIPSTREAM
+#include "wx/zipstrm.h"
+template class ArchiveTestCase<wxZipClassFactory>;
+#endif
+
+#if wxUSE_TARSTREAM
+#include "wx/tarstrm.h"
+template class ArchiveTestCase<wxTarClassFactory>;
+#endif
 
-#endif // wxUSE_STREAMS
+#endif // wxUSE_STREAMS && wxUSE_ARCHIVE_STREAMS