| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432 |
- ///////////////////////////////////////////////////////////////////////////////
- // Name: tests/archive/archive.cpp
- // Purpose: Test the archive classes
- // Author: Mike Wetherell
- // Copyright: (c) 2004 Mike Wetherell
- // Licence: wxWindows licence
- ///////////////////////////////////////////////////////////////////////////////
- #include "testprec.h"
- #ifdef __BORLANDC__
- # pragma hdrstop
- #endif
- #ifndef WX_PRECOMP
- # include "wx/wx.h"
- #endif
- #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.
- #if defined _MSC_VER && _MSC_VER < 1300
- #pragma warning (disable:4284)
- #endif
- #include "archivetest.h"
- #include "wx/dir.h"
- #include <string>
- #include <list>
- #include <map>
- #include <sys/stat.h>
- using std::string;
- using std::auto_ptr;
- // Check whether member templates can be used
- //
- #if defined __GNUC__ && \
- (__GNUC__ >= 3 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95))
- # define WXARC_MEMBER_TEMPLATES
- #endif
- #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 __DMC__ && __DMC__ >= 0x832
- # define WXARC_MEMBER_TEMPLATES
- #endif
- #if defined __HP_aCC && __HP_aCC > 33300
- # define WXARC_MEMBER_TEMPLATES
- #endif
- #if defined __SUNPRO_CC && __SUNPRO_CC > 0x500
- # define WXARC_MEMBER_TEMPLATES
- #endif
- ///////////////////////////////////////////////////////////////////////////////
- // A class to hold a test entry
- TestEntry::TestEntry(const wxDateTime& dt, int len, const char *data)
- : m_dt(dt),
- m_len(len),
- m_isText(len > 0)
- {
- m_data = new char[len];
- memcpy(m_data, data, len);
- for (int i = 0; i < len && m_isText; i++)
- m_isText = (signed char)m_data[i] > 0;
- }
- ///////////////////////////////////////////////////////////////////////////////
- // TestOutputStream and TestInputStream are memory streams which can be
- // seekable or non-seekable.
- const size_t STUB_SIZE = 2048;
- const size_t INITIAL_SIZE = 0x18000;
- const wxFileOffset SEEK_LIMIT = 0x100000;
- TestOutputStream::TestOutputStream(int options)
- : m_options(options)
- {
- Init();
- }
- void TestOutputStream::Init()
- {
- m_data = NULL;
- m_size = 0;
- m_capacity = 0;
- m_pos = 0;
- if (m_options & Stub) {
- wxCharBuffer buf(STUB_SIZE);
- memset(buf.data(), 0, STUB_SIZE);
- Write(buf, STUB_SIZE);
- }
- }
- wxFileOffset TestOutputStream::OnSysSeek(wxFileOffset pos, wxSeekMode mode)
- {
- if ((m_options & PipeOut) == 0) {
- switch (mode) {
- case wxFromStart: break;
- case wxFromCurrent: pos += m_pos; break;
- case wxFromEnd: pos += m_size; break;
- }
- if (pos < 0 || pos > SEEK_LIMIT)
- return wxInvalidOffset;
- m_pos = (size_t)pos;
- return m_pos;
- }
- return wxInvalidOffset;
- }
- wxFileOffset TestOutputStream::OnSysTell() const
- {
- return (m_options & PipeOut) == 0 ? (wxFileOffset)m_pos : wxInvalidOffset;
- }
- size_t TestOutputStream::OnSysWrite(const void *buffer, size_t size)
- {
- if (!IsOk() || !size)
- return 0;
- m_lasterror = wxSTREAM_WRITE_ERROR;
- size_t newsize = m_pos + size;
- wxCHECK(newsize > m_pos, 0);
- if (m_capacity < newsize) {
- size_t capacity = m_capacity ? m_capacity : INITIAL_SIZE;
- while (capacity < newsize) {
- capacity <<= 1;
- wxCHECK(capacity > m_capacity, 0);
- }
- char *buf = new char[capacity];
- if (m_data)
- memcpy(buf, m_data, m_capacity);
- delete [] m_data;
- m_data = buf;
- m_capacity = capacity;
- }
- memcpy(m_data + m_pos, buffer, size);
- m_pos += size;
- if (m_pos > m_size)
- m_size = m_pos;
- m_lasterror = wxSTREAM_NO_ERROR;
- return size;
- }
- void TestOutputStream::GetData(char*& data, size_t& size)
- {
- data = m_data;
- size = m_size;
- if (m_options & Stub) {
- char *d = m_data;
- size += STUB_SIZE;
- if (size > m_capacity) {
- d = new char[size];
- memcpy(d + STUB_SIZE, m_data, m_size);
- delete [] m_data;
- }
- else {
- memmove(d + STUB_SIZE, d, m_size);
- }
- memset(d, 0, STUB_SIZE);
- data = d;
- }
- Init();
- Reset();
- }
- ///////////////////////////////////////////////////////////////////////////////
- // 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),
- m_size(in.m_size),
- m_eoftype(in.m_eoftype)
- {
- m_data = new char[m_size];
- memcpy(m_data, in.m_data, m_size);
- }
- void TestInputStream::Rewind()
- {
- if ((m_options & Stub) && (m_options & PipeIn))
- m_pos = STUB_SIZE * 2;
- else
- m_pos = 0;
- if (m_wbacksize) {
- free(m_wback);
- m_wback = NULL;
- m_wbacksize = 0;
- m_wbackcur = 0;
- }
- Reset();
- }
- void TestInputStream::SetData(TestOutputStream& out)
- {
- delete [] m_data;
- m_options = out.GetOptions();
- out.GetData(m_data, m_size);
- Rewind();
- }
- wxFileOffset TestInputStream::OnSysSeek(wxFileOffset pos, wxSeekMode mode)
- {
- if ((m_options & PipeIn) == 0) {
- switch (mode) {
- case wxFromStart: break;
- case wxFromCurrent: pos += m_pos; break;
- case wxFromEnd: pos += m_size; break;
- }
- if (pos < 0 || pos > SEEK_LIMIT)
- return wxInvalidOffset;
- m_pos = (size_t)pos;
- return m_pos;
- }
- return wxInvalidOffset;
- }
- wxFileOffset TestInputStream::OnSysTell() const
- {
- return (m_options & PipeIn) == 0 ? (wxFileOffset)m_pos : wxInvalidOffset;
- }
- size_t TestInputStream::OnSysRead(void *buffer, size_t size)
- {
- if (!IsOk() || !size)
- 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_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;
- }
- ///////////////////////////////////////////////////////////////////////////////
- // minimal non-intrusive reference counting pointer for testing the iterators
- template <class T> class Ptr
- {
- public:
- explicit Ptr(T* p = NULL) : m_p(p), m_count(new int) { *m_count = 1; }
- Ptr(const Ptr& sp) : m_p(sp.m_p), m_count(sp.m_count) { ++*m_count; }
- ~Ptr() { Free(); }
- Ptr& operator =(const Ptr& sp) {
- if (&sp != this) {
- Free();
- m_p = sp.m_p;
- m_count = sp.m_count;
- ++*m_count;
- }
- return *this;
- }
- T* get() const { return m_p; }
- T* operator->() const { return m_p; }
- T& operator*() const { return *m_p; }
- private:
- void Free() {
- if (--*m_count == 0) {
- delete m_p;
- delete m_count;
- }
- }
- T *m_p;
- int *m_count;
- };
- ///////////////////////////////////////////////////////////////////////////////
- // Clean-up for temp directory
- class TempDir
- {
- public:
- TempDir();
- ~TempDir();
- wxString GetName() const { return m_tmp; }
- private:
- void RemoveDir(wxString& path);
- wxString m_tmp;
- wxString m_original;
- };
- TempDir::TempDir()
- {
- wxString tmp = wxFileName::CreateTempFileName(wxT("arctest-"));
- if (!tmp.empty()) {
- wxRemoveFile(tmp);
- m_original = wxGetCwd();
- CPPUNIT_ASSERT(wxMkdir(tmp, 0700));
- m_tmp = tmp;
- CPPUNIT_ASSERT(wxSetWorkingDirectory(tmp));
- }
- }
- TempDir::~TempDir()
- {
- if (!m_tmp.empty()) {
- wxSetWorkingDirectory(m_original);
- RemoveDir(m_tmp);
- }
- }
- void TempDir::RemoveDir(wxString& path)
- {
- wxCHECK_RET(!m_tmp.empty() && path.substr(0, m_tmp.length()) == m_tmp,
- wxT("remove '") + path + wxT("' fails safety check"));
- const wxChar *files[] = {
- 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[] = {
- wxT("text/"), wxT("bin/"), wxT("zero/"), wxT("empty/")
- };
- wxString tmp = m_tmp + wxFileName::GetPathSeparator();
- size_t i;
- for (i = 0; i < WXSIZEOF(files); i++)
- wxRemoveFile(tmp + wxFileName(files[i], wxPATH_UNIX).GetFullPath());
- for (i = 0; i < WXSIZEOF(dirs); i++)
- wxRmdir(tmp + wxFileName(dirs[i], wxPATH_UNIX).GetFullPath());
- if (!wxRmdir(m_tmp))
- {
- wxLogSysError(wxT("can't remove temporary dir '%s'"), m_tmp.c_str());
- }
- }
- ///////////////////////////////////////////////////////////////////////////////
- // wxFFile streams for piping to/from an external program
- #if defined __UNIX__ || defined __MINGW32__
- # define WXARC_popen popen
- # define WXARC_pclose pclose
- #elif defined _MSC_VER || defined __BORLANDC__
- # define WXARC_popen _popen
- # define WXARC_pclose _pclose
- #else
- # define WXARC_NO_POPEN
- # define WXARC_popen(cmd, type) NULL
- # define WXARC_pclose(fp)
- #endif
- #ifdef __WINDOWS__
- # define WXARC_b "b"
- #else
- # define WXARC_b
- #endif
- PFileInputStream::PFileInputStream(const wxString& cmd)
- : wxFFileInputStream(WXARC_popen(cmd.mb_str(), "r" WXARC_b))
- {
- }
- PFileInputStream::~PFileInputStream()
- {
- WXARC_pclose(m_file->fp()); m_file->Detach();
- }
- PFileOutputStream::PFileOutputStream(const wxString& cmd)
- : wxFFileOutputStream(WXARC_popen(cmd.mb_str(), "w" WXARC_b))
- {
- }
- PFileOutputStream::~PFileOutputStream()
- {
- WXARC_pclose(m_file->fp()); m_file->Detach();
- }
- ///////////////////////////////////////////////////////////////////////////////
- // The test case
- template <class ClassFactoryT>
- ArchiveTestCase<ClassFactoryT>::ArchiveTestCase(
- string name,
- ClassFactoryT *factory,
- int options,
- const wxString& archiver,
- const wxString& unarchiver)
- :
- CppUnit::TestCase(TestId::MakeId() + name),
- m_factory(factory),
- m_options(options),
- 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 ClassFactoryT>
- ArchiveTestCase<ClassFactoryT>::~ArchiveTestCase()
- {
- TestEntries::iterator it;
- for (it = m_testEntries.begin(); it != m_testEntries.end(); ++it)
- delete it->second;
- }
- template <class ClassFactoryT>
- void ArchiveTestCase<ClassFactoryT>::runTest()
- {
- TestOutputStream out(m_options);
- CreateTestData();
- if (m_archiver.empty())
- CreateArchive(out);
- else
- CreateArchive(out, m_archiver);
- // check archive could be created
- CPPUNIT_ASSERT(out.GetLength() > 0);
- TestInputStream in(out, m_id % ((m_options & PipeIn) ? 4 : 3));
- TestIterator(in);
- in.Rewind();
- TestPairIterator(in);
- in.Rewind();
- TestSmartIterator(in);
- in.Rewind();
- TestSmartPairIterator(in);
- in.Rewind();
- if ((m_options & PipeIn) == 0) {
- ReadSimultaneous(in);
- in.Rewind();
- }
- ModifyArchive(in, out);
- in.SetData(out);
- if (m_unarchiver.empty())
- 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 ClassFactoryT>
- void ArchiveTestCase<ClassFactoryT>::CreateTestData()
- {
- Add("text/");
- Add("text/empty", "");
- Add("text/small", "Small text file for testing\n"
- "archive streams in wxWidgets\n");
- Add("bin/");
- Add("bin/bin1000", 1000);
- Add("bin/bin4095", 4095);
- Add("bin/bin4096", 4096);
- Add("bin/bin4097", 4097);
- Add("bin/bin16384", 16384);
- Add("zero/");
- Add("zero/zero5", 5, 0);
- Add("zero/zero1024", 1024, 109);
- Add("zero/zero32768", 32768, 106);
- Add("zero/zero16385", 16385, 119);
- Add("empty/");
- }
- template <class ClassFactoryT>
- TestEntry& ArchiveTestCase<ClassFactoryT>::Add(const char *name,
- const char *data,
- int len /*=-1*/)
- {
- if (len == -1)
- len = strlen(data);
- TestEntry*& entry = m_testEntries[wxString(name, *wxConvCurrent)];
- wxASSERT(entry == NULL);
- entry = new TestEntry(m_timeStamp, len, data);
- m_timeStamp += wxTimeSpan(0, 1, 30);
- return *entry;
- }
- 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++)
- buf.data()[i] = (char)(value == EOF ? rand() : value);
- return Add(name, buf, len);
- }
- // Create an archive using the wx archive classes, write it to 'out'
- //
- template <class ClassFactoryT>
- void ArchiveTestCase<ClassFactoryT>::CreateArchive(wxOutputStream& out)
- {
- auto_ptr<OutputStreamT> arc(m_factory->NewStream(out));
- TestEntries::iterator it;
- OnCreateArchive(*arc);
- // We want to try creating entries in various different ways, 'choices'
- // is just a number used to select between all the various possibilities.
- int choices = m_id;
- for (it = m_testEntries.begin(); it != m_testEntries.end(); ++it) {
- choices += 5;
- TestEntry& testEntry = *it->second;
- wxString name = it->first;
- // 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() == 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
- string error_entry((wxT(" '") + name + wxT("'")).mb_str());
- string error_context(" failed for entry" + error_entry);
- if ((choices & 2) || testEntry.IsText()) {
- // try PutNextEntry(EntryT *pEntry)
- auto_ptr<EntryT> entry(m_factory->NewEntry());
- entry->SetName(name, wxPATH_UNIX);
- if (setIsDir)
- entry->SetIsDir();
- entry->SetDateTime(testEntry.GetDateTime());
- entry->SetSize(testEntry.GetLength());
- OnCreateEntry(*arc, testEntry, entry.get());
- CPPUNIT_ASSERT_MESSAGE("PutNextEntry" + error_context,
- arc->PutNextEntry(entry.release()));
- }
- else {
- // try the convenience methods
- OnCreateEntry(*arc, testEntry);
- if (setIsDir)
- CPPUNIT_ASSERT_MESSAGE("PutNextDirEntry" + error_context,
- arc->PutNextDirEntry(name, testEntry.GetDateTime()));
- else
- CPPUNIT_ASSERT_MESSAGE("PutNextEntry" + error_context,
- arc->PutNextEntry(name, testEntry.GetDateTime(),
- testEntry.GetLength()));
- }
- if (it->first.Last() != wxT('/')) {
- // for non-dirs write the data
- arc->Write(testEntry.GetData(), testEntry.GetSize());
- CPPUNIT_ASSERT_MESSAGE("LastWrite check" + error_context,
- arc->LastWrite() == testEntry.GetSize());
- // should work with or without explicit CloseEntry
- if (choices & 3)
- CPPUNIT_ASSERT_MESSAGE("CloseEntry" + error_context,
- arc->CloseEntry());
- }
- CPPUNIT_ASSERT_MESSAGE("IsOk" + error_context, arc->IsOk());
- }
- // should work with or without explicit Close
- if (m_id % 2)
- CPPUNIT_ASSERT(arc->Close());
- }
- // Create an archive using an external archive program
- //
- 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
- TempDir tmpdir;
- // write the files
- TestEntries::iterator i;
- for (i = m_testEntries.begin(); i != m_testEntries.end(); ++i) {
- wxFileName fn(i->first, wxPATH_UNIX);
- TestEntry& entry = *i->second;
- if (fn.IsDir()) {
- wxFileName::Mkdir(fn.GetPath(), 0777, wxPATH_MKDIR_FULL);
- } else {
- wxFileName::Mkdir(fn.GetPath(), 0777, wxPATH_MKDIR_FULL);
- wxFFileOutputStream fileout(fn.GetFullPath());
- fileout.Write(entry.GetData(), entry.GetSize());
- }
- }
- for (i = m_testEntries.begin(); i != m_testEntries.end(); ++i) {
- wxFileName fn(i->first, wxPATH_UNIX);
- TestEntry& entry = *i->second;
- wxDateTime dt = entry.GetDateTime();
- #ifdef __WINDOWS__
- if (fn.IsDir())
- entry.SetDateTime(wxDateTime());
- else
- #endif
- fn.SetTimes(NULL, &dt, NULL);
- }
- if ((m_options & PipeOut) == 0) {
- wxFileName fn(tmpdir.GetName());
- fn.SetExt(wxT("arc"));
- wxString tmparc = fn.GetPath(wxPATH_GET_SEPARATOR) + fn.GetFullName();
- // call the archiver to create an archive file
- 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);
- if (in.IsOk())
- out.Write(in);
- }
- wxRemoveFile(tmparc);
- }
- else {
- // for the non-seekable test, have the archiver output to "-"
- // and read the archive via a pipe
- PFileInputStream in(wxString::Format(archiver, wxT("-")));
- if (in.IsOk())
- out.Write(in);
- }
- }
- // Do a standard set of modification on an archive, delete an entry,
- // rename an entry and add an entry
- //
- 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;
- 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);
- while ((pEntry = arcIn->GetNextEntry()) != NULL) {
- auto_ptr<EntryT> entry(pEntry);
- OnSetNotifier(*entry);
- wxString name = entry->GetName(wxPATH_UNIX);
- // provide some context for the error message so that we know which
- // iteration of the loop we were on
- string error_entry((wxT(" '") + name + wxT("'")).mb_str());
- string error_context(" failed for entry" + error_entry);
- if (name == deleteName) {
- TestEntries::iterator it = m_testEntries.find(name);
- CPPUNIT_ASSERT_MESSAGE(
- "deletion failed (already deleted?) for" + error_entry,
- it != m_testEntries.end());
- TestEntry *p = it->second;
- m_testEntries.erase(it);
- delete p;
- }
- else {
- if (name == renameFrom) {
- entry->SetName(renameTo);
- TestEntries::iterator it = m_testEntries.find(renameFrom);
- CPPUNIT_ASSERT_MESSAGE(
- "rename failed (already renamed?) for" + error_entry,
- it != m_testEntries.end());
- TestEntry *p = it->second;
- m_testEntries.erase(it);
- m_testEntries[renameTo] = p;
- }
- CPPUNIT_ASSERT_MESSAGE("CopyEntry" + error_context,
- arcOut->CopyEntry(entry.release(), *arcIn));
- }
- }
- // check that the deletion and rename were done
- CPPUNIT_ASSERT(m_testEntries.count(deleteName) == 0);
- CPPUNIT_ASSERT(m_testEntries.count(renameFrom) == 0);
- CPPUNIT_ASSERT(m_testEntries.count(renameTo) == 1);
- // check that the end of the input archive was reached without error
- CPPUNIT_ASSERT(arcIn->Eof());
- // try adding a new entry
- TestEntry& testEntry = Add(newName.mb_str(), newData);
- auto_ptr<EntryT> newentry(m_factory->NewEntry());
- newentry->SetName(newName);
- newentry->SetDateTime(testEntry.GetDateTime());
- newentry->SetSize(testEntry.GetLength());
- OnCreateEntry(*arcOut, testEntry, newentry.get());
- OnSetNotifier(*newentry);
- CPPUNIT_ASSERT(arcOut->PutNextEntry(newentry.release()));
- CPPUNIT_ASSERT(arcOut->Write(newData, strlen(newData)).IsOk());
- // should work with or without explicit Close
- if (m_id % 2)
- CPPUNIT_ASSERT(arcOut->Close());
- }
- // Extract an archive using the wx archive classes
- //
- template <class ClassFactoryT>
- void ArchiveTestCase<ClassFactoryT>::ExtractArchive(wxInputStream& in)
- {
- typedef Ptr<EntryT> EntryPtr;
- typedef std::list<EntryPtr> Entries;
- typedef typename Entries::iterator EntryIter;
- auto_ptr<InputStreamT> arc(m_factory->NewStream(in));
- int expectedTotal = m_testEntries.size();
- EntryPtr entry;
- Entries entries;
- if ((m_options & PipeIn) == 0)
- OnArchiveExtracted(*arc, expectedTotal);
- while (entry = EntryPtr(arc->GetNextEntry()), entry.get() != NULL) {
- wxString name = entry->GetName(wxPATH_UNIX);
- // provide some context for the error message so that we know which
- // iteration of the loop we were on
- string error_entry((wxT(" '") + name + wxT("'")).mb_str());
- string error_context(" failed for entry" + error_entry);
- TestEntries::iterator it = m_testEntries.find(name);
- CPPUNIT_ASSERT_MESSAGE(
- "archive contains an entry that shouldn't be there" + error_entry,
- it != m_testEntries.end());
- 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());
- #endif
- // non-seekable entries are allowed to have GetSize == wxInvalidOffset
- // until the end of the entry's data has been read past
- CPPUNIT_ASSERT_MESSAGE("entry size check" + error_context,
- testEntry.GetLength() == entry->GetSize() ||
- ((m_options & PipeIn) != 0 && entry->GetSize() == wxInvalidOffset));
- CPPUNIT_ASSERT_MESSAGE(
- "arc->GetLength() == entry->GetSize()" + error_context,
- arc->GetLength() == entry->GetSize());
- if (name.Last() != wxT('/'))
- {
- CPPUNIT_ASSERT_MESSAGE("!IsDir" + error_context,
- !entry->IsDir());
- wxCharBuffer buf(testEntry.GetSize() + 1);
- CPPUNIT_ASSERT_MESSAGE("Read until Eof" + error_context,
- arc->Read(buf.data(), testEntry.GetSize() + 1).Eof());
- CPPUNIT_ASSERT_MESSAGE("LastRead check" + error_context,
- arc->LastRead() == testEntry.GetSize());
- CPPUNIT_ASSERT_MESSAGE("data compare" + error_context,
- !memcmp(buf.data(), testEntry.GetData(), testEntry.GetSize()));
- } else {
- CPPUNIT_ASSERT_MESSAGE("IsDir" + error_context, entry->IsDir());
- }
- // GetSize() must return the right result in all cases after all the
- // data has been read
- CPPUNIT_ASSERT_MESSAGE("entry size check" + error_context,
- testEntry.GetLength() == entry->GetSize());
- CPPUNIT_ASSERT_MESSAGE(
- "arc->GetLength() == entry->GetSize()" + error_context,
- arc->GetLength() == entry->GetSize());
- if ((m_options & PipeIn) == 0) {
- OnEntryExtracted(*entry, testEntry, arc.get());
- delete it->second;
- m_testEntries.erase(it);
- } else {
- entries.push_back(entry);
- }
- }
- // check that the end of the input archive was reached without error
- CPPUNIT_ASSERT(arc->Eof());
- // for non-seekable streams these data are only guaranteed to be
- // available once the end of the archive has been reached
- if (m_options & PipeIn) {
- for (EntryIter i = entries.begin(); i != entries.end(); ++i) {
- wxString name = (*i)->GetName(wxPATH_UNIX);
- TestEntries::iterator j = m_testEntries.find(name);
- OnEntryExtracted(**i, *j->second);
- delete j->second;
- m_testEntries.erase(j);
- }
- OnArchiveExtracted(*arc, expectedTotal);
- }
- }
- // Extract an archive using an external unarchive program
- //
- 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());
- fn.SetExt(wxT("arc"));
- wxString tmparc = fn.GetPath(wxPATH_GET_SEPARATOR) + fn.GetFullName();
- if (m_options & Stub)
- in.SeekI(STUB_SIZE * 2);
- // write the archive to a temporary file
- {
- wxFFileOutputStream out(tmparc);
- if (out.IsOk())
- out.Write(in);
- }
- // call unarchiver
- 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
- PFileOutputStream out(wxString::Format(unarchiver, wxT("-")));
- if (out.IsOk())
- out.Write(in);
- }
- wxString dir = tmpdir.GetName();
- VerifyDir(dir);
- }
- // Verifies the files produced by an external unarchiver are as expected
- //
- template <class ClassFactoryT>
- void ArchiveTestCase<ClassFactoryT>::VerifyDir(wxString& path,
- size_t rootlen /*=0*/)
- {
- wxDir dir;
- path += wxFileName::GetPathSeparator();
- int pos = path.length();
- wxString name;
- if (!rootlen)
- rootlen = pos;
- if (dir.Open(path) && dir.GetFirst(&name)) {
- do {
- path.replace(pos, wxString::npos, name);
- name = m_factory->GetInternalName(
- path.substr(rootlen, wxString::npos));
- bool isDir = wxDirExists(path);
- if (isDir)
- name += wxT("/");
- // provide some context for the error message so that we know which
- // iteration of the loop we were on
- string error_entry((wxT(" '") + name + wxT("'")).mb_str());
- string error_context(" failed for entry" + error_entry);
- TestEntries::iterator it = m_testEntries.find(name);
- CPPUNIT_ASSERT_MESSAGE(
- "archive contains an entry that shouldn't be there"
- + error_entry,
- it != m_testEntries.end());
- const TestEntry& testEntry = *it->second;
- #if 0 //ndef __WINDOWS__
- CPPUNIT_ASSERT_MESSAGE("timestamp check" + error_context,
- testEntry.GetDateTime() ==
- wxFileName(path).GetModificationTime());
- #endif
- if (!isDir) {
- wxFFileInputStream in(path);
- CPPUNIT_ASSERT_MESSAGE(
- "entry not found in archive" + error_entry, in.IsOk());
- size_t size = (size_t)in.GetLength();
- wxCharBuffer buf(size);
- CPPUNIT_ASSERT_MESSAGE("Read" + error_context,
- in.Read(buf.data(), size).LastRead() == size);
- CPPUNIT_ASSERT_MESSAGE("size check" + error_context,
- testEntry.GetSize() == size);
- CPPUNIT_ASSERT_MESSAGE("data compare" + error_context,
- memcmp(buf.data(), testEntry.GetData(), size) == 0);
- }
- else {
- VerifyDir(path, rootlen);
- }
- delete it->second;
- m_testEntries.erase(it);
- }
- while (dir.GetNext(&name));
- }
- }
- // test the simple iterators that give away ownership of an entry
- //
- template <class ClassFactoryT>
- void ArchiveTestCase<ClassFactoryT>::TestIterator(wxInputStream& in)
- {
- typedef std::list<EntryT*> ArchiveCatalog;
- typedef typename ArchiveCatalog::iterator CatalogIter;
- auto_ptr<InputStreamT> arc(m_factory->NewStream(in));
- size_t count = 0;
- #ifdef WXARC_MEMBER_TEMPLATES
- ArchiveCatalog cat((IterT)*arc, IterT());
- #else
- ArchiveCatalog cat;
- for (IterT i(*arc); i != IterT(); ++i)
- cat.push_back(*i);
- #endif
- for (CatalogIter it = cat.begin(); it != cat.end(); ++it) {
- auto_ptr<EntryT> entry(*it);
- count += m_testEntries.count(entry->GetName(wxPATH_UNIX));
- }
- CPPUNIT_ASSERT(m_testEntries.size() == cat.size());
- CPPUNIT_ASSERT(count == cat.size());
- }
- // test the pair iterators that can be used to load a std::map or wxHashMap
- // these also give away ownership of entries
- //
- template <class ClassFactoryT>
- void ArchiveTestCase<ClassFactoryT>::TestPairIterator(wxInputStream& in)
- {
- typedef std::map<wxString, EntryT*> ArchiveCatalog;
- typedef typename ArchiveCatalog::iterator CatalogIter;
- auto_ptr<InputStreamT> arc(m_factory->NewStream(in));
- size_t count = 0;
- #ifdef WXARC_MEMBER_TEMPLATES
- ArchiveCatalog cat((PairIterT)*arc, PairIterT());
- #else
- ArchiveCatalog cat;
- for (PairIterT i(*arc); i != PairIterT(); ++i)
- cat.insert(*i);
- #endif
- for (CatalogIter it = cat.begin(); it != cat.end(); ++it) {
- auto_ptr<EntryT> entry(it->second);
- count += m_testEntries.count(entry->GetName(wxPATH_UNIX));
- }
- CPPUNIT_ASSERT(m_testEntries.size() == cat.size());
- CPPUNIT_ASSERT(count == cat.size());
- }
- // simple iterators using smart pointers, no need to worry about ownership
- //
- template <class ClassFactoryT>
- void ArchiveTestCase<ClassFactoryT>::TestSmartIterator(wxInputStream& in)
- {
- typedef std::list<Ptr<EntryT> > ArchiveCatalog;
- typedef typename ArchiveCatalog::iterator CatalogIter;
- typedef wxArchiveIterator<InputStreamT, Ptr<EntryT> > Iter;
- auto_ptr<InputStreamT> arc(m_factory->NewStream(in));
- #ifdef WXARC_MEMBER_TEMPLATES
- ArchiveCatalog cat((Iter)*arc, Iter());
- #else
- ArchiveCatalog cat;
- for (Iter i(*arc); i != Iter(); ++i)
- cat.push_back(*i);
- #endif
- CPPUNIT_ASSERT(m_testEntries.size() == cat.size());
- for (CatalogIter it = cat.begin(); it != cat.end(); ++it)
- CPPUNIT_ASSERT(m_testEntries.count((*it)->GetName(wxPATH_UNIX)));
- }
- // pair iterator using smart pointers
- //
- 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
- // type is Ptr<EntryT>, so this iterator can't be made to work.
- (void)in;
- #else
- typedef std::map<wxString, Ptr<EntryT> > ArchiveCatalog;
- typedef typename ArchiveCatalog::iterator CatalogIter;
- typedef wxArchiveIterator<InputStreamT,
- std::pair<wxString, Ptr<EntryT> > > PairIter;
- auto_ptr<InputStreamT> arc(m_factory->NewStream(in));
- #ifdef WXARC_MEMBER_TEMPLATES
- ArchiveCatalog cat((PairIter)*arc, PairIter());
- #else
- ArchiveCatalog cat;
- for (PairIter i(*arc); i != PairIter(); ++i)
- cat.insert(*i);
- #endif
- CPPUNIT_ASSERT(m_testEntries.size() == cat.size());
- for (CatalogIter it = cat.begin(); it != cat.end(); ++it)
- CPPUNIT_ASSERT(m_testEntries.count(it->second->GetName(wxPATH_UNIX)));
- #endif
- }
- // try reading two entries at the same time
- //
- template <class ClassFactoryT>
- void ArchiveTestCase<ClassFactoryT>::ReadSimultaneous(TestInputStream& in)
- {
- typedef std::map<wxString, Ptr<EntryT> > ArchiveCatalog;
- typedef wxArchiveIterator<InputStreamT,
- std::pair<wxString, Ptr<EntryT> > > PairIter;
- // create two archive input streams
- TestInputStream in2(in);
- auto_ptr<InputStreamT> arc(m_factory->NewStream(in));
- auto_ptr<InputStreamT> arc2(m_factory->NewStream(in2));
- // load the catalog
- #ifdef WXARC_MEMBER_TEMPLATES
- ArchiveCatalog cat((PairIter)*arc, PairIter());
- #else
- ArchiveCatalog cat;
- for (PairIter i(*arc); i != PairIter(); ++i)
- cat.insert(*i);
- #endif
- // the names of two entries to read
- const wxChar *name = wxT("text/small");
- const wxChar *name2 = wxT("bin/bin1000");
- // open them
- typename ArchiveCatalog::iterator j;
- CPPUNIT_ASSERT((j = cat.find(name)) != cat.end());
- CPPUNIT_ASSERT(arc->OpenEntry(*j->second));
- CPPUNIT_ASSERT((j = cat.find(name2)) != cat.end());
- CPPUNIT_ASSERT(arc2->OpenEntry(*j->second));
- // get pointers to the expected data
- TestEntries::iterator k;
- CPPUNIT_ASSERT((k = m_testEntries.find(name)) != m_testEntries.end());
- TestEntry *entry = k->second;
- CPPUNIT_ASSERT((k = m_testEntries.find(name2)) != m_testEntries.end());
- TestEntry *entry2 = k->second;
- size_t count = 0, count2 = 0;
- size_t size = entry->GetSize(), size2 = entry2->GetSize();
- const char *data = entry->GetData(), *data2 = entry2->GetData();
- // read and check the two entries in parallel, character by character
- while (arc->IsOk() || arc2->IsOk()) {
- char ch = arc->GetC();
- if (arc->LastRead() == 1) {
- CPPUNIT_ASSERT(count < size);
- CPPUNIT_ASSERT(ch == data[count++]);
- }
- char ch2 = arc2->GetC();
- if (arc2->LastRead() == 1) {
- CPPUNIT_ASSERT(count2 < size2);
- CPPUNIT_ASSERT(ch2 == data2[count2++]);
- }
- }
- CPPUNIT_ASSERT(arc->Eof());
- CPPUNIT_ASSERT(arc2->Eof());
- CPPUNIT_ASSERT(count == size);
- CPPUNIT_ASSERT(count2 == size2);
- }
- // Nothing useful can be done with a generic notifier yet, so just test one
- // can be set
- //
- template <class NotifierT, class EntryT>
- class ArchiveNotifier : public NotifierT
- {
- public:
- void OnEntryUpdated(EntryT& WXUNUSED(entry)) { }
- };
- template <class ClassFactoryT>
- void ArchiveTestCase<ClassFactoryT>::OnSetNotifier(EntryT& entry)
- {
- static ArchiveNotifier<NotifierT, EntryT> notifier;
- entry.SetNotifier(notifier);
- }
- ///////////////////////////////////////////////////////////////////////////////
- // An additional case to check that reading corrupt archives doesn't crash
- class CorruptionTestCase : public CppUnit::TestCase
- {
- public:
- CorruptionTestCase(std::string name,
- wxArchiveClassFactory *factory,
- int options)
- : CppUnit::TestCase(TestId::MakeId() + name),
- m_factory(factory),
- m_options(options)
- { }
- protected:
- // the entry point for the test
- void runTest();
- void CreateArchive(wxOutputStream& out);
- void ExtractArchive(wxInputStream& in);
- auto_ptr<wxArchiveClassFactory> m_factory; // factory to make classes
- int m_options; // test options
- };
- void CorruptionTestCase::runTest()
- {
- 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;
- }
- // 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;
- }
- // try chopping the archive off
- for (int size = 1; size <= len; size++) {
- in.Chop(size);
- ExtractArchive(in);
- in.Rewind();
- }
- }
- void CorruptionTestCase::CreateArchive(wxOutputStream& out)
- {
- auto_ptr<wxArchiveOutputStream> arc(m_factory->NewStream(out));
- arc->PutNextDirEntry(wxT("dir"));
- arc->PutNextEntry(wxT("file"));
- arc->Write(wxT("foo"), 3);
- }
- void CorruptionTestCase::ExtractArchive(wxInputStream& in)
- {
- auto_ptr<wxArchiveInputStream> arc(m_factory->NewStream(in));
- auto_ptr<wxArchiveEntry> entry(arc->GetNextEntry());
- while (entry.get() != NULL) {
- char buf[1024];
- while (arc->IsOk())
- arc->Read(buf, sizeof(buf));
- auto_ptr<wxArchiveEntry> next(arc->GetNextEntry());
- entry = next;
- }
- }
- ///////////////////////////////////////////////////////////////////////////////
- // Make the ids
- int TestId::m_seed = 6219;
- // static
- string TestId::MakeId()
- {
- m_seed = (m_seed * 171) % 30269;
- return string(wxString::Format(wxT("%-6d"), m_seed).mb_str());
- }
- ///////////////////////////////////////////////////////////////////////////////
- // Suite base
- ArchiveTestSuite::ArchiveTestSuite(string name)
- : CppUnit::TestSuite("archive/" + name),
- m_name(name.c_str(), *wxConvCurrent)
- {
- 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
- // the path first
- //
- void ArchiveTestSuite::AddCmd(wxArrayString& cmdlist, const wxString& cmd)
- {
- if (IsInPath(cmd))
- cmdlist.push_back(cmd);
- }
- bool ArchiveTestSuite::IsInPath(const wxString& cmd)
- {
- wxString c = cmd.BeforeFirst(wxT(' '));
- #ifdef __WINDOWS__
- c += wxT(".exe");
- #endif
- return !m_path.FindValidPath(c).empty();
- }
- // make the test suite
- //
- ArchiveTestSuite *ArchiveTestSuite::makeSuite()
- {
- typedef wxArrayString::iterator Iter;
- 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++)
- {
- #ifdef WXARC_NO_POPEN
- // if no popen then can't pipe in/out of archiver
- if ((options & PipeIn) && !i->empty())
- continue;
- if ((options & PipeOut) && !j->empty())
- continue;
- #endif
- string descr = Description(m_name, options,
- generic != 0, *j, *i);
- CppUnit::Test *test = makeTest(descr, options,
- generic != 0, *j, *i);
- if (test)
- addTest(test);
- }
- 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));
- }
- }
- 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,
- int options,
- bool genericInterface,
- const wxString& archiver,
- const wxString& unarchiver)
- {
- wxString descr;
- if (genericInterface)
- descr << wxT("wxArchive (") << type << wxT(")");
- else
- descr << type;
- 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)
- optstr += wxT("|PipeIn");
- if ((options & PipeOut) != 0)
- optstr += wxT("|PipeOut");
- if ((options & Stub) != 0)
- optstr += wxT("|Stub");
- if (!optstr.empty())
- optstr = wxT(" (") + optstr.substr(1) + wxT(")");
- descr << optstr;
- return string(descr.mb_str());
- }
- ///////////////////////////////////////////////////////////////////////////////
- // 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 && wxUSE_ARCHIVE_STREAMS
|