]> git.saurik.com Git - wxWidgets.git/blobdiff - tests/archive/archivetest.cpp
Check the return value of system() and pipe() in the test suite.
[wxWidgets.git] / tests / archive / archivetest.cpp
index 5a3a10aafd795ba5f402688b050fad0c5909f999..7e8262d0f8e878d32a2b2efc3899122e8a00ed62 100644 (file)
@@ -17,9 +17,7 @@
 #   include "wx/wx.h"
 #endif
 
 #   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.
 
 // 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
 
 #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>
 #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
     (__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
 #   define WXARC_MEMBER_TEMPLATES
 #endif
 #if defined __BORLANDC__ && __BORLANDC__ >= 0x530
@@ -55,9 +51,6 @@ using std::auto_ptr;
 #if defined __DMC__ && __DMC__ >= 0x832
 #   define WXARC_MEMBER_TEMPLATES
 #endif
 #if defined __DMC__ && __DMC__ >= 0x832
 #   define WXARC_MEMBER_TEMPLATES
 #endif
-#if defined __MWERKS__ && __MWERKS__ >= 0x2200
-#   define WXARC_MEMBER_TEMPLATES
-#endif
 #if defined __HP_aCC && __HP_aCC > 33300
 #   define WXARC_MEMBER_TEMPLATES
 #endif
 #if defined __HP_aCC && __HP_aCC > 33300
 #   define WXARC_MEMBER_TEMPLATES
 #endif
@@ -66,74 +59,9 @@ using std::auto_ptr;
 #endif
 
 
 #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
 
 ///////////////////////////////////////////////////////////////////////////////
 // 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),
 TestEntry::TestEntry(const wxDateTime& dt, int len, const char *data)
   : m_dt(dt),
     m_len(len),
@@ -151,34 +79,9 @@ TestEntry::TestEntry(const wxDateTime& dt, int len, const char *data)
 // TestOutputStream and TestInputStream are memory streams which can be
 // seekable or non-seekable.
 
 // 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)
 
 TestOutputStream::TestOutputStream(int options)
   : m_options(options)
@@ -231,7 +134,7 @@ size_t TestOutputStream::OnSysWrite(const void *buffer, size_t size)
     wxCHECK(newsize > m_pos, 0);
 
     if (m_capacity < newsize) {
     wxCHECK(newsize > m_pos, 0);
 
     if (m_capacity < newsize) {
-        size_t capacity = m_capacity ? m_capacity : (size_t)INITIAL_SIZE;
+        size_t capacity = m_capacity ? m_capacity : INITIAL_SIZE;
 
         while (capacity < newsize) {
             capacity <<= 1;
 
         while (capacity < newsize) {
             capacity <<= 1;
@@ -281,35 +184,17 @@ void TestOutputStream::GetData(char*& data, size_t& size)
     Reset();
 }
 
     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)
   : wxInputStream(),
     m_options(in.m_options),
     m_pos(in.m_pos),
 
 TestInputStream::TestInputStream(const TestInputStream& in)
   : 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);
 {
     m_data = new char[m_size];
     memcpy(m_data, in.m_data, m_size);
@@ -318,7 +203,7 @@ TestInputStream::TestInputStream(const TestInputStream& in)
 void TestInputStream::Rewind()
 {
     if ((m_options & Stub) && (m_options & PipeIn))
 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;
 
     else
         m_pos = 0;
 
@@ -328,6 +213,8 @@ void TestInputStream::Rewind()
         m_wbacksize = 0;
         m_wbackcur = 0;
     }
         m_wbacksize = 0;
         m_wbackcur = 0;
     }
+
+    Reset();
 }
 
 void TestInputStream::SetData(TestOutputStream& out)
 }
 
 void TestInputStream::SetData(TestOutputStream& out)
@@ -336,7 +223,6 @@ void TestInputStream::SetData(TestOutputStream& out)
     m_options = out.GetOptions();
     out.GetData(m_data, m_size);
     Rewind();
     m_options = out.GetOptions();
     out.GetData(m_data, m_size);
     Rewind();
-    Reset();
 }
 
 wxFileOffset TestInputStream::OnSysSeek(wxFileOffset pos, wxSeekMode mode)
 }
 
 wxFileOffset TestInputStream::OnSysSeek(wxFileOffset pos, wxSeekMode mode)
@@ -347,7 +233,7 @@ wxFileOffset TestInputStream::OnSysSeek(wxFileOffset pos, wxSeekMode mode)
             case wxFromCurrent: pos += m_pos; break;
             case wxFromEnd:     pos += m_size; break;
         }
             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;
             return wxInvalidOffset;
         m_pos = (size_t)pos;
         return m_pos;
@@ -364,16 +250,30 @@ size_t TestInputStream::OnSysRead(void *buffer, size_t size)
 {
     if (!IsOk() || !size)
         return 0;
 {
     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,8 +332,8 @@ private:
 
 TempDir::TempDir()
 {
 
 TempDir::TempDir()
 {
-    wxString tmp = wxFileName::CreateTempFileName(_T("arctest-"));
-    if (tmp != wxEmptyString) {
+    wxString tmp = wxFileName::CreateTempFileName(wxT("arctest-"));
+    if (!tmp.empty()) {
         wxRemoveFile(tmp);
         m_original = wxGetCwd();
         CPPUNIT_ASSERT(wxMkdir(tmp, 0700));
         wxRemoveFile(tmp);
         m_original = wxGetCwd();
         CPPUNIT_ASSERT(wxMkdir(tmp, 0700));
@@ -444,7 +344,7 @@ TempDir::TempDir()
 
 TempDir::~TempDir()
 {
 
 TempDir::~TempDir()
 {
-    if (m_tmp != wxEmptyString) {
+    if (!m_tmp.empty()) {
         wxSetWorkingDirectory(m_original);
         RemoveDir(m_tmp);
     }
         wxSetWorkingDirectory(m_original);
         RemoveDir(m_tmp);
     }
@@ -453,26 +353,26 @@ TempDir::~TempDir()
 void TempDir::RemoveDir(wxString& path)
 {
     wxCHECK_RET(!m_tmp.empty() && path.substr(0, m_tmp.length()) == m_tmp,
 void TempDir::RemoveDir(wxString& path)
 {
     wxCHECK_RET(!m_tmp.empty() && path.substr(0, m_tmp.length()) == m_tmp,
-                _T("remove '") + path + _T("' fails safety check"));
+                wxT("remove '") + path + wxT("' fails safety check"));
 
     const wxChar *files[] = {
 
     const wxChar *files[] = {
-        _T("text/empty"),
-        _T("text/small"),
-        _T("bin/bin1000"),
-        _T("bin/bin4095"),
-        _T("bin/bin4096"),
-        _T("bin/bin4097"),
-        _T("bin/bin16384"),
-        _T("zero/zero5"),
-        _T("zero/zero1024"),
-        _T("zero/zero32768"),
-        _T("zero/zero16385"),
-        _T("zero/newname"),
-        _T("newfile"),
+        wxT("text/empty"),
+        wxT("text/small"),
+        wxT("bin/bin1000"),
+        wxT("bin/bin4095"),
+        wxT("bin/bin4096"),
+        wxT("bin/bin4097"),
+        wxT("bin/bin16384"),
+        wxT("zero/zero5"),
+        wxT("zero/zero1024"),
+        wxT("zero/zero32768"),
+        wxT("zero/zero16385"),
+        wxT("zero/newname"),
+        wxT("newfile"),
     };
 
     const wxChar *dirs[] = {
     };
 
     const wxChar *dirs[] = {
-        _T("text/"), _T("bin/"), _T("zero/"), _T("empty/")
+        wxT("text/"), wxT("bin/"), wxT("zero/"), wxT("empty/")
     };
 
     wxString tmp = m_tmp + wxFileName::GetPathSeparator();
     };
 
     wxString tmp = m_tmp + wxFileName::GetPathSeparator();
@@ -485,7 +385,9 @@ void TempDir::RemoveDir(wxString& path)
         wxRmdir(tmp + wxFileName(dirs[i], wxPATH_UNIX).GetFullPath());
 
     if (!wxRmdir(m_tmp))
         wxRmdir(tmp + wxFileName(dirs[i], wxPATH_UNIX).GetFullPath());
 
     if (!wxRmdir(m_tmp))
-        wxLogSysError(_T("can't remove temporary dir '%s'"), m_tmp.c_str());
+    {
+        wxLogSysError(wxT("can't remove temporary dir '%s'"), m_tmp.c_str());
+    }
 }
 
 
 }
 
 
@@ -504,149 +406,65 @@ void TempDir::RemoveDir(wxString& path)
 #   define WXARC_pclose(fp)
 #endif
 
 #   define WXARC_pclose(fp)
 #endif
 
-#ifdef __WXMSW__
+#ifdef __WINDOWS__
 #   define WXARC_b "b"
 #else
 #   define WXARC_b
 #endif
 
 #   define WXARC_b "b"
 #else
 #   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,
     string name,
-    int id,
-    wxArchiveClassFactory *factory,
+    ClassFactoryT *factory,
     int options,
     const wxString& archiver,
     const wxString& unarchiver)
   :
     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_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);
 }
 
     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;
 }
 
 {
     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);
 
 {
     TestOutputStream out(m_options);
 
@@ -660,7 +478,7 @@ void ArchiveTestCase<Classes>::runTest()
     // check archive could be created
     CPPUNIT_ASSERT(out.GetLength() > 0);
 
     // 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();
 
     TestIterator(in);
     in.Rewind();
@@ -688,8 +506,8 @@ void ArchiveTestCase<Classes>::runTest()
     CPPUNIT_ASSERT(m_testEntries.empty());
 }
 
     CPPUNIT_ASSERT(m_testEntries.empty());
 }
 
-template <class Classes>
-void ArchiveTestCase<Classes>::CreateTestData()
+template <class ClassFactoryT>
+void ArchiveTestCase<ClassFactoryT>::CreateTestData()
 {
     Add("text/");
     Add("text/empty", "");
 {
     Add("text/");
     Add("text/empty", "");
@@ -712,10 +530,10 @@ void ArchiveTestCase<Classes>::CreateTestData()
     Add("empty/");
 }
 
     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);
 {
     if (len == -1)
         len = strlen(data);
@@ -726,10 +544,10 @@ TestEntry& ArchiveTestCase<Classes>::Add(const char *name,
     return *entry;
 }
 
     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++)
 {
     wxCharBuffer buf(len);
     for (int i = 0; i < len; i++)
@@ -739,8 +557,8 @@ TestEntry& ArchiveTestCase<Classes>::Add(const char *name,
 
 // Create an archive using the wx archive classes, write it to 'out'
 //
 
 // 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;
 {
     auto_ptr<OutputStreamT> arc(m_factory->NewStream(out));
     TestEntries::iterator it;
@@ -759,13 +577,13 @@ void ArchiveTestCase<Classes>::CreateArchive(wxOutputStream& out)
         // It should be possible to create a directory entry just by supplying
         // a name that looks like a directory, or alternatively any old name
         // can be identified as a directory using SetIsDir or PutNextDirEntry
         // It should be possible to create a directory entry just by supplying
         // a name that looks like a directory, or alternatively any old name
         // can be identified as a directory using SetIsDir or PutNextDirEntry
-        bool setIsDir = name.Last() == _T('/') && (choices & 1);
+        bool setIsDir = name.Last() == wxT('/') && (choices & 1);
         if (setIsDir)
             name.erase(name.length() - 1);
 
         // provide some context for the error message so that we know which
         // iteration of the loop we were on
         if (setIsDir)
             name.erase(name.length() - 1);
 
         // provide some context for the error message so that we know which
         // iteration of the loop we were on
-        string error_entry((_T(" '") + name + _T("'")).mb_str());
+        string error_entry((wxT(" '") + name + wxT("'")).mb_str());
         string error_context(" failed for entry" + error_entry);
 
         if ((choices & 2) || testEntry.IsText()) {
         string error_context(" failed for entry" + error_entry);
 
         if ((choices & 2) || testEntry.IsText()) {
@@ -792,7 +610,7 @@ void ArchiveTestCase<Classes>::CreateArchive(wxOutputStream& out)
                                       testEntry.GetLength()));
         }
 
                                       testEntry.GetLength()));
         }
 
-        if (name.Last() != _T('/')) {
+        if (it->first.Last() != wxT('/')) {
             // for non-dirs write the data
             arc->Write(testEntry.GetData(), testEntry.GetSize());
             CPPUNIT_ASSERT_MESSAGE("LastWrite check" + error_context,
             // for non-dirs write the data
             arc->Write(testEntry.GetData(), testEntry.GetSize());
             CPPUNIT_ASSERT_MESSAGE("LastWrite check" + error_context,
@@ -813,9 +631,9 @@ void ArchiveTestCase<Classes>::CreateArchive(wxOutputStream& out)
 
 // Create an archive using an external archive program
 //
 
 // 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
 {
     // for an external archiver the test data need to be written to
     // temp files
@@ -828,7 +646,7 @@ void ArchiveTestCase<Classes>::CreateArchive(wxOutputStream& out,
         TestEntry& entry = *i->second;
 
         if (fn.IsDir()) {
         TestEntry& entry = *i->second;
 
         if (fn.IsDir()) {
-            fn.Mkdir(0777, wxPATH_MKDIR_FULL);
+            wxFileName::Mkdir(fn.GetPath(), 0777, wxPATH_MKDIR_FULL);
         } else {
             wxFileName::Mkdir(fn.GetPath(), 0777, wxPATH_MKDIR_FULL);
             wxFFileOutputStream fileout(fn.GetFullPath());
         } else {
             wxFileName::Mkdir(fn.GetPath(), 0777, wxPATH_MKDIR_FULL);
             wxFFileOutputStream fileout(fn.GetFullPath());
@@ -840,7 +658,7 @@ void ArchiveTestCase<Classes>::CreateArchive(wxOutputStream& out,
         wxFileName fn(i->first, wxPATH_UNIX);
         TestEntry& entry = *i->second;
         wxDateTime dt = entry.GetDateTime();
         wxFileName fn(i->first, wxPATH_UNIX);
         TestEntry& entry = *i->second;
         wxDateTime dt = entry.GetDateTime();
-#ifdef __WXMSW__
+#ifdef __WINDOWS__
         if (fn.IsDir())
             entry.SetDateTime(wxDateTime());
         else
         if (fn.IsDir())
             entry.SetDateTime(wxDateTime());
         else
@@ -850,16 +668,19 @@ void ArchiveTestCase<Classes>::CreateArchive(wxOutputStream& out,
 
     if ((m_options & PipeOut) == 0) {
         wxFileName fn(tmpdir.GetName());
 
     if ((m_options & PipeOut) == 0) {
         wxFileName fn(tmpdir.GetName());
-        fn.SetExt(_T("arc"));
-        wxString tmparc = fn.GetFullPath();
+        fn.SetExt(wxT("arc"));
+        wxString tmparc = fn.GetPath(wxPATH_GET_SEPARATOR) + fn.GetFullName();
 
         // call the archiver to create an archive file
 
         // call the archiver to create an archive file
-        system(wxString::Format(archiver, tmparc.c_str()).mb_str());
+        if ( system(wxString::Format(archiver, tmparc.c_str()).mb_str()) == -1 )
+        {
+            wxLogError("Failed to run acrhiver command \"%s\"", archiver);
+        }
 
         // then load the archive file
         {
             wxFFileInputStream in(tmparc);
 
         // then load the archive file
         {
             wxFFileInputStream in(tmparc);
-            if (in.Ok())
+            if (in.IsOk())
                 out.Write(in);
         }
 
                 out.Write(in);
         }
 
@@ -868,8 +689,8 @@ void ArchiveTestCase<Classes>::CreateArchive(wxOutputStream& out,
     else {
         // for the non-seekable test, have the archiver output to "-"
         // and read the archive via a pipe
     else {
         // for the non-seekable test, have the archiver output to "-"
         // and read the archive via a pipe
-        PFileInputStream in(wxString::Format(archiver, _T("-")));
-        if (in.Ok())
+        PFileInputStream in(wxString::Format(archiver, wxT("-")));
+        if (in.IsOk())
             out.Write(in);
     }
 }
             out.Write(in);
     }
 }
@@ -877,18 +698,18 @@ 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
 //
 // 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));
     EntryT *pEntry;
 
 {
     auto_ptr<InputStreamT> arcIn(m_factory->NewStream(in));
     auto_ptr<OutputStreamT> arcOut(m_factory->NewStream(out));
     EntryT *pEntry;
 
-    const wxString deleteName = _T("bin/bin1000");
-    const wxString renameFrom = _T("zero/zero1024");
-    const wxString renameTo   = _T("zero/newname");
-    const wxString newName    = _T("newfile");
+    const wxString deleteName = wxT("bin/bin1000");
+    const wxString renameFrom = wxT("zero/zero1024");
+    const wxString renameTo   = wxT("zero/newname");
+    const wxString newName    = wxT("newfile");
     const char *newData       = "New file added as a test\n";
 
     arcOut->CopyArchiveMetaData(*arcIn);
     const char *newData       = "New file added as a test\n";
 
     arcOut->CopyArchiveMetaData(*arcIn);
@@ -900,7 +721,7 @@ void ArchiveTestCase<Classes>::ModifyArchive(wxInputStream& in,
 
         // provide some context for the error message so that we know which
         // iteration of the loop we were on
 
         // provide some context for the error message so that we know which
         // iteration of the loop we were on
-        string error_entry((_T(" '") + name + _T("'")).mb_str());
+        string error_entry((wxT(" '") + name + wxT("'")).mb_str());
         string error_context(" failed for entry" + error_entry);
 
         if (name == deleteName) {
         string error_context(" failed for entry" + error_entry);
 
         if (name == deleteName) {
@@ -955,8 +776,8 @@ void ArchiveTestCase<Classes>::ModifyArchive(wxInputStream& in,
 
 // Extract an archive using the wx archive classes
 //
 
 // 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;
 {
     typedef Ptr<EntryT> EntryPtr;
     typedef std::list<EntryPtr> Entries;
@@ -975,7 +796,7 @@ void ArchiveTestCase<Classes>::ExtractArchive(wxInputStream& in)
 
         // provide some context for the error message so that we know which
         // iteration of the loop we were on
 
         // provide some context for the error message so that we know which
         // iteration of the loop we were on
-        string error_entry((_T(" '") + name + _T("'")).mb_str());
+        string error_entry((wxT(" '") + name + wxT("'")).mb_str());
         string error_context(" failed for entry" + error_entry);
 
         TestEntries::iterator it = m_testEntries.find(name);
         string error_context(" failed for entry" + error_entry);
 
         TestEntries::iterator it = m_testEntries.find(name);
@@ -985,10 +806,14 @@ void ArchiveTestCase<Classes>::ExtractArchive(wxInputStream& in)
 
         const TestEntry& testEntry = *it->second;
 
 
         const TestEntry& testEntry = *it->second;
 
+#ifndef __WINDOWS__
+        // On Windows some archivers compensate for Windows DST handling, but
+        // other don't, so disable the test for now.
         wxDateTime dt = testEntry.GetDateTime();
         if (dt.IsValid())
             CPPUNIT_ASSERT_MESSAGE("timestamp check" + error_context,
                                    dt == entry->GetDateTime());
         wxDateTime dt = testEntry.GetDateTime();
         if (dt.IsValid())
             CPPUNIT_ASSERT_MESSAGE("timestamp check" + error_context,
                                    dt == entry->GetDateTime());
+#endif
 
         // non-seekable entries are allowed to have GetSize == wxInvalidOffset
         // until the end of the entry's data has been read past
 
         // non-seekable entries are allowed to have GetSize == wxInvalidOffset
         // until the end of the entry's data has been read past
@@ -999,7 +824,7 @@ void ArchiveTestCase<Classes>::ExtractArchive(wxInputStream& in)
             "arc->GetLength() == entry->GetSize()" + error_context,
             arc->GetLength() == entry->GetSize());
 
             "arc->GetLength() == entry->GetSize()" + error_context,
             arc->GetLength() == entry->GetSize());
 
-        if (name.Last() != _T('/'))
+        if (name.Last() != wxT('/'))
         {
             CPPUNIT_ASSERT_MESSAGE("!IsDir" + error_context,
                 !entry->IsDir());
         {
             CPPUNIT_ASSERT_MESSAGE("!IsDir" + error_context,
                 !entry->IsDir());
@@ -1050,37 +875,41 @@ void ArchiveTestCase<Classes>::ExtractArchive(wxInputStream& in)
 
 // Extract an archive using an external unarchive program
 //
 
 // 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;
 
     if ((m_options & PipeIn) == 0) {
         wxFileName fn(tmpdir.GetName());
 {
     // for an external unarchiver, unarchive to a tempdir
     TempDir tmpdir;
 
     if ((m_options & PipeIn) == 0) {
         wxFileName fn(tmpdir.GetName());
-        fn.SetExt(_T("arc"));
-        wxString tmparc = fn.GetFullPath();
+        fn.SetExt(wxT("arc"));
+        wxString tmparc = fn.GetPath(wxPATH_GET_SEPARATOR) + fn.GetFullName();
 
         if (m_options & Stub)
 
         if (m_options & Stub)
-            in.SeekI(TestOutputStream::STUB_SIZE * 2);
+            in.SeekI(STUB_SIZE * 2);
 
         // write the archive to a temporary file
         {
             wxFFileOutputStream out(tmparc);
 
         // write the archive to a temporary file
         {
             wxFFileOutputStream out(tmparc);
-            if (out.Ok())
+            if (out.IsOk())
                 out.Write(in);
         }
 
         // call unarchiver
                 out.Write(in);
         }
 
         // call unarchiver
-        system(wxString::Format(unarchiver, tmparc.c_str()).mb_str());
+        if ( system(wxString::Format(unarchiver, tmparc.c_str()).mb_str()) == -1 )
+        {
+            wxLogError("Failed to run unarchiver command \"%s\"", unarchiver);
+        }
+
         wxRemoveFile(tmparc);
     }
     else {
         // for the non-seekable test, have the archiver extract "-" and
         // feed it the archive via a pipe
         wxRemoveFile(tmparc);
     }
     else {
         // for the non-seekable test, have the archiver extract "-" and
         // feed it the archive via a pipe
-        PFileOutputStream out(wxString::Format(unarchiver, _T("-")));
-        if (out.Ok())
+        PFileOutputStream out(wxString::Format(unarchiver, wxT("-")));
+        if (out.IsOk())
             out.Write(in);
     }
 
             out.Write(in);
     }
 
@@ -1090,8 +919,9 @@ void ArchiveTestCase<Classes>::ExtractArchive(wxInputStream& in,
 
 // Verifies the files produced by an external unarchiver are as expected
 //
 
 // 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();
 {
     wxDir dir;
     path += wxFileName::GetPathSeparator();
@@ -1109,11 +939,11 @@ void ArchiveTestCase<Classes>::VerifyDir(wxString& path, size_t rootlen /*=0*/)
 
             bool isDir = wxDirExists(path);
             if (isDir)
 
             bool isDir = wxDirExists(path);
             if (isDir)
-                name += _T("/");
+                name += wxT("/");
 
             // provide some context for the error message so that we know which
             // iteration of the loop we were on
 
             // provide some context for the error message so that we know which
             // iteration of the loop we were on
-            string error_entry((_T(" '") + name + _T("'")).mb_str());
+            string error_entry((wxT(" '") + name + wxT("'")).mb_str());
             string error_context(" failed for entry" + error_entry);
 
             TestEntries::iterator it = m_testEntries.find(name);
             string error_context(" failed for entry" + error_entry);
 
             TestEntries::iterator it = m_testEntries.find(name);
@@ -1124,7 +954,7 @@ void ArchiveTestCase<Classes>::VerifyDir(wxString& path, size_t rootlen /*=0*/)
 
             const TestEntry& testEntry = *it->second;
 
 
             const TestEntry& testEntry = *it->second;
 
-#ifndef __WXMSW__
+#if 0 //ndef __WINDOWS__
             CPPUNIT_ASSERT_MESSAGE("timestamp check" + error_context,
                                    testEntry.GetDateTime() ==
                                    wxFileName(path).GetModificationTime());
             CPPUNIT_ASSERT_MESSAGE("timestamp check" + error_context,
                                    testEntry.GetDateTime() ==
                                    wxFileName(path).GetModificationTime());
@@ -1132,7 +962,7 @@ void ArchiveTestCase<Classes>::VerifyDir(wxString& path, size_t rootlen /*=0*/)
             if (!isDir) {
                 wxFFileInputStream in(path);
                 CPPUNIT_ASSERT_MESSAGE(
             if (!isDir) {
                 wxFFileInputStream in(path);
                 CPPUNIT_ASSERT_MESSAGE(
-                    "entry not found in archive" + error_entry, in.Ok());
+                    "entry not found in archive" + error_entry, in.IsOk());
 
                 size_t size = (size_t)in.GetLength();
                 wxCharBuffer buf(size);
 
                 size_t size = (size_t)in.GetLength();
                 wxCharBuffer buf(size);
@@ -1156,8 +986,8 @@ void ArchiveTestCase<Classes>::VerifyDir(wxString& path, size_t rootlen /*=0*/)
 
 // test the simple iterators that give away ownership of an entry
 //
 
 // 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;
 {
     typedef std::list<EntryT*> ArchiveCatalog;
     typedef typename ArchiveCatalog::iterator CatalogIter;
@@ -1185,8 +1015,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
 //
 // 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;
 {
     typedef std::map<wxString, EntryT*> ArchiveCatalog;
     typedef typename ArchiveCatalog::iterator CatalogIter;
@@ -1213,8 +1043,8 @@ void ArchiveTestCase<Classes>::TestPairIterator(wxInputStream& in)
 
 // simple iterators using smart pointers, no need to worry about ownership
 //
 
 // 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;
 {
     typedef std::list<Ptr<EntryT> > ArchiveCatalog;
     typedef typename ArchiveCatalog::iterator CatalogIter;
@@ -1238,8 +1068,8 @@ void ArchiveTestCase<Classes>::TestSmartIterator(wxInputStream& in)
 
 // pair iterator using smart pointers
 //
 
 // 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
 {
 #if defined _MSC_VER && defined _MSC_VER < 1200
     // With VC++ 5.0 the '=' operator of std::pair breaks when the second
@@ -1270,8 +1100,8 @@ void ArchiveTestCase<Classes>::TestSmartPairIterator(wxInputStream& in)
 
 // try reading two entries at the same time
 //
 
 // 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,
 {
     typedef std::map<wxString, Ptr<EntryT> > ArchiveCatalog;
     typedef wxArchiveIterator<InputStreamT,
@@ -1292,8 +1122,8 @@ void ArchiveTestCase<Classes>::ReadSimultaneous(TestInputStream& in)
 #endif
 
     // the names of two entries to read
 #endif
 
     // the names of two entries to read
-    const wxChar *name = _T("text/small");
-    const wxChar *name2 = _T("bin/bin1000");
+    const wxChar *name = wxT("text/small");
+    const wxChar *name2 = wxT("bin/bin1000");
 
     // open them
     typename ArchiveCatalog::iterator j;
 
     // open them
     typename ArchiveCatalog::iterator j;
@@ -1343,8 +1173,8 @@ public:
     void OnEntryUpdated(EntryT& WXUNUSED(entry)) { }
 };
 
     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);
 {
     static ArchiveNotifier<NotifierT, EntryT> notifier;
     entry.SetNotifier(notifier);
@@ -1352,211 +1182,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:
 {
 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:
     { }
 
 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);
+    // the entry point for the test
+    void runTest();
 
 
-    void OnSetNotifier(EntryT& entry);
+    void CreateArchive(wxOutputStream& out);
+    void ExtractArchive(wxInputStream& in);
 
 
-    int m_count;
-    wxString m_comment;
+    auto_ptr<wxArchiveClassFactory> m_factory;  // factory to make classes
+    int m_options;                              // test options
 };
 
 };
 
-void ZipTestCase::OnCreateArchive(wxZipOutputStream& zip)
+void CorruptionTestCase::runTest()
 {
 {
-    m_comment << _T("Comment for test ") << m_id;
-    zip.SetComment(m_comment);
-}
-
-void ZipTestCase::OnArchiveExtracted(wxZipInputStream& zip, int expectedTotal)
-{
-    CPPUNIT_ASSERT(zip.GetComment() == m_comment);
-    CPPUNIT_ASSERT(zip.GetTotalEntries() == expectedTotal);
-}
-
-void ZipTestCase::OnCreateEntry(wxZipOutputStream& zip,
-                                TestEntry& testEntry,
-                                wxZipEntry *entry)
-{
-    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(wxT("dir"));
+    arc->PutNextEntry(wxT("file"));
+    arc->Write(wxT("foo"), 3);
 }
 
 }
 
-void ZipTestCase::OnSetNotifier(EntryT& entry)
-{
-    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
+void CorruptionTestCase::ExtractArchive(wxInputStream& in)
 {
 {
-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(wxT("%-6d"), m_seed).mb_str());
 }
 
 
 ///////////////////////////////////////////////////////////////////////////////
 }
 
 
 ///////////////////////////////////////////////////////////////////////////////
-// The suite
+// Suite base
 
 
-class ArchiveTestSuite : public CppUnit::TestSuite
+ArchiveTestSuite::ArchiveTestSuite(string name)
+  : CppUnit::TestSuite("archive/" + name),
+    m_name(name.c_str(), *wxConvCurrent)
 {
 {
-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)
-{
-    m_path.AddEnvList(_T("PATH"));
+    m_name = wxT("wx") + m_name.Left(1).Upper() + m_name.Mid(1).Lower();
+    m_path.AddEnvList(wxT("PATH"));
+    m_archivers.push_back(wxT(""));
+    m_unarchivers.push_back(wxT(""));
 }
 
 // add the command for an external archiver to the list, testing for it in
 }
 
 // add the command for an external archiver to the list, testing for it in
@@ -1564,17 +1297,15 @@ ArchiveTestSuite::ArchiveTestSuite()
 //
 void ArchiveTestSuite::AddCmd(wxArrayString& cmdlist, const wxString& cmd)
 {
 //
 void ArchiveTestSuite::AddCmd(wxArrayString& cmdlist, const wxString& cmd)
 {
-    if (cmdlist.empty())
-        cmdlist.push_back(_T(""));
     if (IsInPath(cmd))
         cmdlist.push_back(cmd);
 }
 
 bool ArchiveTestSuite::IsInPath(const wxString& cmd)
 {
     if (IsInPath(cmd))
         cmdlist.push_back(cmd);
 }
 
 bool ArchiveTestSuite::IsInPath(const wxString& cmd)
 {
-    wxString c = cmd.BeforeFirst(_T(' '));
-#ifdef __WXMSW__
-    c += _T(".exe");
+    wxString c = cmd.BeforeFirst(wxT(' '));
+#ifdef __WINDOWS__
+    c += wxT(".exe");
 #endif
     return !m_path.FindValidPath(c).empty();
 }
 #endif
     return !m_path.FindValidPath(c).empty();
 }
@@ -1584,52 +1315,59 @@ bool ArchiveTestSuite::IsInPath(const wxString& cmd)
 ArchiveTestSuite *ArchiveTestSuite::makeSuite()
 {
     typedef wxArrayString::iterator Iter;
 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++)
                 {
                 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;
                     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
                     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 + wxT("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;
 }
 
 
     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,
 // make a display string for the option bits
 //
 string ArchiveTestSuite::Description(const wxString& type,
@@ -1639,38 +1377,57 @@ string ArchiveTestSuite::Description(const wxString& type,
                                      const wxString& unarchiver)
 {
     wxString descr;
                                      const wxString& unarchiver)
 {
     wxString descr;
-    descr << m_id << _T(" ");
 
     if (genericInterface)
 
     if (genericInterface)
-        descr << _T("wxArchive (") << type << _T(")");
+        descr << wxT("wxArchive (") << type << wxT(")");
     else
         descr << type;
 
     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 ? wxT("-") : wxT("file");
+        const wxString cmd = archiver.Contains("%s")
+                             ? wxString::Format(archiver, fn)
+                             : archiver;
+        descr << wxT(" (") << cmd << wxT(")");
+    }
+    if (!unarchiver.empty()) {
+        const wxChar *fn = (options & PipeIn) != 0 ? wxT("-") : wxT("file");
+        const wxString cmd = unarchiver.Contains("%s")
+                             ? wxString::Format(unarchiver, fn)
+                             : unarchiver;
+        descr << wxT(" (") << cmd << wxT(")");
+    }
 
     wxString optstr;
 
     if ((options & PipeIn) != 0)
 
     wxString optstr;
 
     if ((options & PipeIn) != 0)
-        optstr += _T("|PipeIn");
+        optstr += wxT("|PipeIn");
     if ((options & PipeOut) != 0)
     if ((options & PipeOut) != 0)
-        optstr += _T("|PipeOut");
+        optstr += wxT("|PipeOut");
     if ((options & Stub) != 0)
     if ((options & Stub) != 0)
-        optstr += _T("|Stub");
+        optstr += wxT("|Stub");
     if (!optstr.empty())
     if (!optstr.empty())
-        optstr = _T(" (") + optstr.substr(1) + _T(")");
+        optstr = wxT(" (") + optstr.substr(1) + wxT(")");
 
     descr << optstr;
 
 
     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