]> git.saurik.com Git - wxWidgets.git/blame - tests/archive/archivetest.cpp
only run tests using multiline strings with multiline text control; document that...
[wxWidgets.git] / tests / archive / archivetest.cpp
CommitLineData
00375592
VZ
1///////////////////////////////////////////////////////////////////////////////
2// Name: tests/archive/archive.cpp
3// Purpose: Test the archive classes
4// Author: Mike Wetherell
5// RCS-ID: $Id$
6// Copyright: (c) 2004 Mike Wetherell
7// Licence: wxWindows licence
8///////////////////////////////////////////////////////////////////////////////
9
8899b155 10#include "testprec.h"
00375592
VZ
11
12#ifdef __BORLANDC__
13# pragma hdrstop
14#endif
15
16#ifndef WX_PRECOMP
17# include "wx/wx.h"
18#endif
19
9e8e867f 20#if wxUSE_STREAMS && wxUSE_ARCHIVE_STREAMS
00375592 21
f44eaed6
RN
22// VC++ 6 warns that the list iterator's '->' operator will not work whenever
23// std::list is used with a non-pointer, so switch it off.
24#if defined _MSC_VER && _MSC_VER < 1300
95662a83
RN
25#pragma warning (disable:4284)
26#endif
27
e6477b92 28#include "archivetest.h"
00375592 29#include "wx/dir.h"
00375592
VZ
30#include <string>
31#include <list>
95662a83 32#include <map>
00375592
VZ
33#include <sys/stat.h>
34
95662a83
RN
35using std::string;
36using std::auto_ptr;
37
38
00375592
VZ
39// Check whether member templates can be used
40//
41#if defined __GNUC__ && \
42 (__GNUC__ >= 3 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95))
43# define WXARC_MEMBER_TEMPLATES
44#endif
a901c3fd 45#if defined _MSC_VER && _MSC_VER >= 1310 && !defined __WIN64__
00375592
VZ
46# define WXARC_MEMBER_TEMPLATES
47#endif
48#if defined __BORLANDC__ && __BORLANDC__ >= 0x530
49# define WXARC_MEMBER_TEMPLATES
50#endif
51#if defined __DMC__ && __DMC__ >= 0x832
52# define WXARC_MEMBER_TEMPLATES
53#endif
54#if defined __MWERKS__ && __MWERKS__ >= 0x2200
55# define WXARC_MEMBER_TEMPLATES
56#endif
57#if defined __HP_aCC && __HP_aCC > 33300
58# define WXARC_MEMBER_TEMPLATES
59#endif
60#if defined __SUNPRO_CC && __SUNPRO_CC > 0x500
61# define WXARC_MEMBER_TEMPLATES
62#endif
63
64
00375592
VZ
65///////////////////////////////////////////////////////////////////////////////
66// A class to hold a test entry
67
00375592
VZ
68TestEntry::TestEntry(const wxDateTime& dt, int len, const char *data)
69 : m_dt(dt),
70 m_len(len),
71 m_isText(len > 0)
72{
f44eaed6
RN
73 m_data = new char[len];
74 memcpy(m_data, data, len);
00375592
VZ
75
76 for (int i = 0; i < len && m_isText; i++)
77 m_isText = (signed char)m_data[i] > 0;
78}
79
80
81///////////////////////////////////////////////////////////////////////////////
82// TestOutputStream and TestInputStream are memory streams which can be
83// seekable or non-seekable.
84
e6477b92
MW
85const size_t STUB_SIZE = 2048;
86const size_t INITIAL_SIZE = 0x18000;
87const wxFileOffset SEEK_LIMIT = 0x100000;
00375592
VZ
88
89TestOutputStream::TestOutputStream(int options)
90 : m_options(options)
91{
92 Init();
93}
94
95void TestOutputStream::Init()
96{
97 m_data = NULL;
98 m_size = 0;
99 m_capacity = 0;
100 m_pos = 0;
101
102 if (m_options & Stub) {
103 wxCharBuffer buf(STUB_SIZE);
104 memset(buf.data(), 0, STUB_SIZE);
105 Write(buf, STUB_SIZE);
106 }
107}
108
109wxFileOffset TestOutputStream::OnSysSeek(wxFileOffset pos, wxSeekMode mode)
110{
111 if ((m_options & PipeOut) == 0) {
112 switch (mode) {
113 case wxFromStart: break;
114 case wxFromCurrent: pos += m_pos; break;
115 case wxFromEnd: pos += m_size; break;
116 }
117 if (pos < 0 || pos > SEEK_LIMIT)
118 return wxInvalidOffset;
95662a83 119 m_pos = (size_t)pos;
00375592
VZ
120 return m_pos;
121 }
122 return wxInvalidOffset;
123}
124
125wxFileOffset TestOutputStream::OnSysTell() const
126{
46263455 127 return (m_options & PipeOut) == 0 ? (wxFileOffset)m_pos : wxInvalidOffset;
00375592
VZ
128}
129
130size_t TestOutputStream::OnSysWrite(const void *buffer, size_t size)
131{
132 if (!IsOk() || !size)
133 return 0;
134 m_lasterror = wxSTREAM_WRITE_ERROR;
135
136 size_t newsize = m_pos + size;
137 wxCHECK(newsize > m_pos, 0);
138
139 if (m_capacity < newsize) {
e6477b92 140 size_t capacity = m_capacity ? m_capacity : INITIAL_SIZE;
00375592
VZ
141
142 while (capacity < newsize) {
143 capacity <<= 1;
144 wxCHECK(capacity > m_capacity, 0);
145 }
146
147 char *buf = new char[capacity];
148 if (m_data)
149 memcpy(buf, m_data, m_capacity);
150 delete [] m_data;
151 m_data = buf;
152 m_capacity = capacity;
153 }
154
155 memcpy(m_data + m_pos, buffer, size);
156 m_pos += size;
157 if (m_pos > m_size)
158 m_size = m_pos;
159 m_lasterror = wxSTREAM_NO_ERROR;
160
161 return size;
162}
163
f44eaed6 164void TestOutputStream::GetData(char*& data, size_t& size)
00375592
VZ
165{
166 data = m_data;
167 size = m_size;
168
169 if (m_options & Stub) {
170 char *d = m_data;
171 size += STUB_SIZE;
172
173 if (size > m_capacity) {
174 d = new char[size];
175 memcpy(d + STUB_SIZE, m_data, m_size);
176 delete [] m_data;
177 }
178 else {
179 memmove(d + STUB_SIZE, d, m_size);
180 }
181
182 memset(d, 0, STUB_SIZE);
183 data = d;
184 }
185
186 Init();
187 Reset();
188}
189
00375592 190
e6477b92
MW
191///////////////////////////////////////////////////////////////////////////////
192// TestOutputStream and TestInputStream are memory streams which can be
193// seekable or non-seekable.
00375592
VZ
194
195TestInputStream::TestInputStream(const TestInputStream& in)
a09cd189
WS
196 : wxInputStream(),
197 m_options(in.m_options),
00375592 198 m_pos(in.m_pos),
716e748b
MW
199 m_size(in.m_size),
200 m_eoftype(in.m_eoftype)
00375592 201{
f44eaed6
RN
202 m_data = new char[m_size];
203 memcpy(m_data, in.m_data, m_size);
00375592
VZ
204}
205
206void TestInputStream::Rewind()
207{
208 if ((m_options & Stub) && (m_options & PipeIn))
e6477b92 209 m_pos = STUB_SIZE * 2;
00375592
VZ
210 else
211 m_pos = 0;
212
213 if (m_wbacksize) {
214 free(m_wback);
215 m_wback = NULL;
216 m_wbacksize = 0;
217 m_wbackcur = 0;
218 }
98de4ac1
MW
219
220 Reset();
00375592
VZ
221}
222
223void TestInputStream::SetData(TestOutputStream& out)
224{
f44eaed6 225 delete [] m_data;
00375592
VZ
226 m_options = out.GetOptions();
227 out.GetData(m_data, m_size);
228 Rewind();
00375592
VZ
229}
230
231wxFileOffset TestInputStream::OnSysSeek(wxFileOffset pos, wxSeekMode mode)
232{
233 if ((m_options & PipeIn) == 0) {
234 switch (mode) {
235 case wxFromStart: break;
236 case wxFromCurrent: pos += m_pos; break;
237 case wxFromEnd: pos += m_size; break;
238 }
e6477b92 239 if (pos < 0 || pos > SEEK_LIMIT)
00375592 240 return wxInvalidOffset;
95662a83 241 m_pos = (size_t)pos;
00375592
VZ
242 return m_pos;
243 }
244 return wxInvalidOffset;
245}
246
247wxFileOffset TestInputStream::OnSysTell() const
248{
46263455 249 return (m_options & PipeIn) == 0 ? (wxFileOffset)m_pos : wxInvalidOffset;
00375592
VZ
250}
251
252size_t TestInputStream::OnSysRead(void *buffer, size_t size)
253{
254 if (!IsOk() || !size)
255 return 0;
716e748b
MW
256
257 size_t count;
258
259 if (m_pos >= m_size)
260 count = 0;
261 else if (m_size - m_pos < size)
262 count = m_size - m_pos;
263 else
264 count = size;
265
266 if (count) {
267 memcpy(buffer, m_data + m_pos, count);
268 m_pos += count;
00375592
VZ
269 }
270
716e748b 271 if (((m_eoftype & AtLast) != 0 && m_pos >= m_size) || count < size)
fe26031e 272 {
716e748b
MW
273 if ((m_eoftype & WithError) != 0)
274 m_lasterror = wxSTREAM_READ_ERROR;
275 else
276 m_lasterror = wxSTREAM_EOF;
fe26031e 277 }
716e748b
MW
278
279 return count;
00375592
VZ
280}
281
282
283///////////////////////////////////////////////////////////////////////////////
284// minimal non-intrusive reference counting pointer for testing the iterators
285
286template <class T> class Ptr
287{
288public:
289 explicit Ptr(T* p = NULL) : m_p(p), m_count(new int) { *m_count = 1; }
290 Ptr(const Ptr& sp) : m_p(sp.m_p), m_count(sp.m_count) { ++*m_count; }
291 ~Ptr() { Free(); }
292
293 Ptr& operator =(const Ptr& sp) {
294 if (&sp != this) {
295 Free();
296 m_p = sp.m_p;
297 m_count = sp.m_count;
298 ++*m_count;
299 }
300 return *this;
301 }
302
303 T* get() const { return m_p; }
304 T* operator->() const { return m_p; }
305 T& operator*() const { return *m_p; }
306
307private:
308 void Free() {
309 if (--*m_count == 0) {
310 delete m_p;
311 delete m_count;
312 }
313 }
314
315 T *m_p;
316 int *m_count;
317};
318
319
320///////////////////////////////////////////////////////////////////////////////
321// Clean-up for temp directory
322
323class TempDir
324{
325public:
326 TempDir();
327 ~TempDir();
328 wxString GetName() const { return m_tmp; }
329
330private:
331 void RemoveDir(wxString& path);
332 wxString m_tmp;
333 wxString m_original;
334};
335
336TempDir::TempDir()
337{
338 wxString tmp = wxFileName::CreateTempFileName(_T("arctest-"));
489f6cf7 339 if (!tmp.empty()) {
00375592
VZ
340 wxRemoveFile(tmp);
341 m_original = wxGetCwd();
342 CPPUNIT_ASSERT(wxMkdir(tmp, 0700));
343 m_tmp = tmp;
344 CPPUNIT_ASSERT(wxSetWorkingDirectory(tmp));
345 }
346}
347
348TempDir::~TempDir()
349{
489f6cf7 350 if (!m_tmp.empty()) {
00375592
VZ
351 wxSetWorkingDirectory(m_original);
352 RemoveDir(m_tmp);
353 }
354}
355
356void TempDir::RemoveDir(wxString& path)
357{
358 wxCHECK_RET(!m_tmp.empty() && path.substr(0, m_tmp.length()) == m_tmp,
359 _T("remove '") + path + _T("' fails safety check"));
360
361 const wxChar *files[] = {
362 _T("text/empty"),
363 _T("text/small"),
364 _T("bin/bin1000"),
365 _T("bin/bin4095"),
366 _T("bin/bin4096"),
367 _T("bin/bin4097"),
368 _T("bin/bin16384"),
369 _T("zero/zero5"),
370 _T("zero/zero1024"),
371 _T("zero/zero32768"),
372 _T("zero/zero16385"),
373 _T("zero/newname"),
374 _T("newfile"),
375 };
376
377 const wxChar *dirs[] = {
378 _T("text/"), _T("bin/"), _T("zero/"), _T("empty/")
379 };
380
381 wxString tmp = m_tmp + wxFileName::GetPathSeparator();
382 size_t i;
383
384 for (i = 0; i < WXSIZEOF(files); i++)
385 wxRemoveFile(tmp + wxFileName(files[i], wxPATH_UNIX).GetFullPath());
386
387 for (i = 0; i < WXSIZEOF(dirs); i++)
388 wxRmdir(tmp + wxFileName(dirs[i], wxPATH_UNIX).GetFullPath());
389
390 if (!wxRmdir(m_tmp))
391 wxLogSysError(_T("can't remove temporary dir '%s'"), m_tmp.c_str());
392}
393
394
395///////////////////////////////////////////////////////////////////////////////
396// wxFFile streams for piping to/from an external program
397
398#if defined __UNIX__ || defined __MINGW32__
399# define WXARC_popen popen
400# define WXARC_pclose pclose
401#elif defined _MSC_VER || defined __BORLANDC__
402# define WXARC_popen _popen
403# define WXARC_pclose _pclose
404#else
405# define WXARC_NO_POPEN
406# define WXARC_popen(cmd, type) NULL
407# define WXARC_pclose(fp)
408#endif
409
410#ifdef __WXMSW__
411# define WXARC_b "b"
412#else
413# define WXARC_b
414#endif
415
e6477b92
MW
416PFileInputStream::PFileInputStream(const wxString& cmd)
417 : wxFFileInputStream(WXARC_popen(cmd.mb_str(), "r" WXARC_b))
00375592 418{
e6477b92 419}
00375592 420
e6477b92 421PFileInputStream::~PFileInputStream()
00375592 422{
e6477b92
MW
423 WXARC_pclose(m_file->fp()); m_file->Detach();
424}
425
426PFileOutputStream::PFileOutputStream(const wxString& cmd)
427: wxFFileOutputStream(WXARC_popen(cmd.mb_str(), "w" WXARC_b))
428{
429}
430
431PFileOutputStream::~PFileOutputStream()
432{
433 WXARC_pclose(m_file->fp()); m_file->Detach();
434}
00375592
VZ
435
436
437///////////////////////////////////////////////////////////////////////////////
438// The test case
439
e6477b92
MW
440template <class ClassFactoryT>
441ArchiveTestCase<ClassFactoryT>::ArchiveTestCase(
95662a83 442 string name,
e6477b92 443 ClassFactoryT *factory,
95662a83
RN
444 int options,
445 const wxString& archiver,
446 const wxString& unarchiver)
447 :
716e748b 448 CppUnit::TestCase(TestId::MakeId() + name),
e6477b92 449 m_factory(factory),
95662a83 450 m_options(options),
e6477b92 451 m_timeStamp(1, wxDateTime::Mar, 2004, 12, 0),
716e748b 452 m_id(TestId::GetId()),
95662a83
RN
453 m_archiver(archiver),
454 m_unarchiver(unarchiver)
00375592 455{
95662a83 456 wxASSERT(m_factory.get() != NULL);
00375592 457}
a09cd189 458
e6477b92
MW
459template <class ClassFactoryT>
460ArchiveTestCase<ClassFactoryT>::~ArchiveTestCase()
00375592
VZ
461{
462 TestEntries::iterator it;
463 for (it = m_testEntries.begin(); it != m_testEntries.end(); ++it)
464 delete it->second;
465}
466
e6477b92
MW
467template <class ClassFactoryT>
468void ArchiveTestCase<ClassFactoryT>::runTest()
00375592
VZ
469{
470 TestOutputStream out(m_options);
471
472 CreateTestData();
473
474 if (m_archiver.empty())
475 CreateArchive(out);
476 else
477 CreateArchive(out, m_archiver);
478
479 // check archive could be created
46263455 480 CPPUNIT_ASSERT(out.GetLength() > 0);
00375592 481
716e748b 482 TestInputStream in(out, m_id % ((m_options & PipeIn) ? 4 : 3));
00375592
VZ
483
484 TestIterator(in);
485 in.Rewind();
486 TestPairIterator(in);
487 in.Rewind();
488 TestSmartIterator(in);
489 in.Rewind();
490 TestSmartPairIterator(in);
491 in.Rewind();
492
493 if ((m_options & PipeIn) == 0) {
494 ReadSimultaneous(in);
495 in.Rewind();
496 }
497
498 ModifyArchive(in, out);
499 in.SetData(out);
500
501 if (m_unarchiver.empty())
502 ExtractArchive(in);
503 else
504 ExtractArchive(in, m_unarchiver);
a09cd189 505
00375592
VZ
506 // check that all the test entries were found in the archive
507 CPPUNIT_ASSERT(m_testEntries.empty());
508}
509
e6477b92
MW
510template <class ClassFactoryT>
511void ArchiveTestCase<ClassFactoryT>::CreateTestData()
00375592
VZ
512{
513 Add("text/");
514 Add("text/empty", "");
515 Add("text/small", "Small text file for testing\n"
516 "archive streams in wxWidgets\n");
517
518 Add("bin/");
519 Add("bin/bin1000", 1000);
520 Add("bin/bin4095", 4095);
521 Add("bin/bin4096", 4096);
522 Add("bin/bin4097", 4097);
523 Add("bin/bin16384", 16384);
524
525 Add("zero/");
526 Add("zero/zero5", 5, 0);
527 Add("zero/zero1024", 1024, 109);
528 Add("zero/zero32768", 32768, 106);
529 Add("zero/zero16385", 16385, 119);
530
531 Add("empty/");
532}
533
e6477b92
MW
534template <class ClassFactoryT>
535TestEntry& ArchiveTestCase<ClassFactoryT>::Add(const char *name,
536 const char *data,
537 int len /*=-1*/)
00375592
VZ
538{
539 if (len == -1)
540 len = strlen(data);
541 TestEntry*& entry = m_testEntries[wxString(name, *wxConvCurrent)];
542 wxASSERT(entry == NULL);
543 entry = new TestEntry(m_timeStamp, len, data);
544 m_timeStamp += wxTimeSpan(0, 1, 30);
545 return *entry;
546}
547
e6477b92
MW
548template <class ClassFactoryT>
549TestEntry& ArchiveTestCase<ClassFactoryT>::Add(const char *name,
550 int len /*=0*/,
551 int value /*=EOF*/)
00375592
VZ
552{
553 wxCharBuffer buf(len);
554 for (int i = 0; i < len; i++)
f44eaed6 555 buf.data()[i] = (char)(value == EOF ? rand() : value);
00375592
VZ
556 return Add(name, buf, len);
557}
558
559// Create an archive using the wx archive classes, write it to 'out'
560//
e6477b92
MW
561template <class ClassFactoryT>
562void ArchiveTestCase<ClassFactoryT>::CreateArchive(wxOutputStream& out)
00375592 563{
95662a83 564 auto_ptr<OutputStreamT> arc(m_factory->NewStream(out));
00375592
VZ
565 TestEntries::iterator it;
566
567 OnCreateArchive(*arc);
568
569 // We want to try creating entries in various different ways, 'choices'
570 // is just a number used to select between all the various possibilities.
571 int choices = m_id;
572
573 for (it = m_testEntries.begin(); it != m_testEntries.end(); ++it) {
574 choices += 5;
575 TestEntry& testEntry = *it->second;
576 wxString name = it->first;
577
578 // It should be possible to create a directory entry just by supplying
579 // a name that looks like a directory, or alternatively any old name
580 // can be identified as a directory using SetIsDir or PutNextDirEntry
581 bool setIsDir = name.Last() == _T('/') && (choices & 1);
582 if (setIsDir)
583 name.erase(name.length() - 1);
584
585 // provide some context for the error message so that we know which
586 // iteration of the loop we were on
95662a83
RN
587 string error_entry((_T(" '") + name + _T("'")).mb_str());
588 string error_context(" failed for entry" + error_entry);
00375592
VZ
589
590 if ((choices & 2) || testEntry.IsText()) {
591 // try PutNextEntry(EntryT *pEntry)
95662a83 592 auto_ptr<EntryT> entry(m_factory->NewEntry());
00375592
VZ
593 entry->SetName(name, wxPATH_UNIX);
594 if (setIsDir)
595 entry->SetIsDir();
596 entry->SetDateTime(testEntry.GetDateTime());
597 entry->SetSize(testEntry.GetLength());
598 OnCreateEntry(*arc, testEntry, entry.get());
599 CPPUNIT_ASSERT_MESSAGE("PutNextEntry" + error_context,
600 arc->PutNextEntry(entry.release()));
601 }
602 else {
603 // try the convenience methods
604 OnCreateEntry(*arc, testEntry);
605 if (setIsDir)
606 CPPUNIT_ASSERT_MESSAGE("PutNextDirEntry" + error_context,
607 arc->PutNextDirEntry(name, testEntry.GetDateTime()));
608 else
609 CPPUNIT_ASSERT_MESSAGE("PutNextEntry" + error_context,
610 arc->PutNextEntry(name, testEntry.GetDateTime(),
611 testEntry.GetLength()));
612 }
613
e6477b92 614 if (it->first.Last() != _T('/')) {
00375592
VZ
615 // for non-dirs write the data
616 arc->Write(testEntry.GetData(), testEntry.GetSize());
617 CPPUNIT_ASSERT_MESSAGE("LastWrite check" + error_context,
618 arc->LastWrite() == testEntry.GetSize());
619 // should work with or without explicit CloseEntry
620 if (choices & 3)
621 CPPUNIT_ASSERT_MESSAGE("CloseEntry" + error_context,
622 arc->CloseEntry());
623 }
624
625 CPPUNIT_ASSERT_MESSAGE("IsOk" + error_context, arc->IsOk());
626 }
627
628 // should work with or without explicit Close
629 if (m_id % 2)
630 CPPUNIT_ASSERT(arc->Close());
631}
632
633// Create an archive using an external archive program
634//
e6477b92
MW
635template <class ClassFactoryT>
636void ArchiveTestCase<ClassFactoryT>::CreateArchive(wxOutputStream& out,
637 const wxString& archiver)
00375592
VZ
638{
639 // for an external archiver the test data need to be written to
640 // temp files
641 TempDir tmpdir;
642
643 // write the files
644 TestEntries::iterator i;
645 for (i = m_testEntries.begin(); i != m_testEntries.end(); ++i) {
646 wxFileName fn(i->first, wxPATH_UNIX);
647 TestEntry& entry = *i->second;
648
649 if (fn.IsDir()) {
650 fn.Mkdir(0777, wxPATH_MKDIR_FULL);
651 } else {
652 wxFileName::Mkdir(fn.GetPath(), 0777, wxPATH_MKDIR_FULL);
653 wxFFileOutputStream fileout(fn.GetFullPath());
654 fileout.Write(entry.GetData(), entry.GetSize());
655 }
656 }
657
658 for (i = m_testEntries.begin(); i != m_testEntries.end(); ++i) {
659 wxFileName fn(i->first, wxPATH_UNIX);
660 TestEntry& entry = *i->second;
661 wxDateTime dt = entry.GetDateTime();
662#ifdef __WXMSW__
663 if (fn.IsDir())
664 entry.SetDateTime(wxDateTime());
665 else
666#endif
667 fn.SetTimes(NULL, &dt, NULL);
668 }
669
670 if ((m_options & PipeOut) == 0) {
671 wxFileName fn(tmpdir.GetName());
672 fn.SetExt(_T("arc"));
e6477b92 673 wxString tmparc = fn.GetPath(wxPATH_GET_SEPARATOR) + fn.GetFullName();
00375592
VZ
674
675 // call the archiver to create an archive file
676 system(wxString::Format(archiver, tmparc.c_str()).mb_str());
677
678 // then load the archive file
679 {
680 wxFFileInputStream in(tmparc);
681 if (in.Ok())
682 out.Write(in);
683 }
684
685 wxRemoveFile(tmparc);
686 }
687 else {
688 // for the non-seekable test, have the archiver output to "-"
689 // and read the archive via a pipe
690 PFileInputStream in(wxString::Format(archiver, _T("-")));
691 if (in.Ok())
692 out.Write(in);
693 }
694}
695
696// Do a standard set of modification on an archive, delete an entry,
697// rename an entry and add an entry
698//
e6477b92
MW
699template <class ClassFactoryT>
700void ArchiveTestCase<ClassFactoryT>::ModifyArchive(wxInputStream& in,
701 wxOutputStream& out)
00375592 702{
95662a83
RN
703 auto_ptr<InputStreamT> arcIn(m_factory->NewStream(in));
704 auto_ptr<OutputStreamT> arcOut(m_factory->NewStream(out));
f44eaed6 705 EntryT *pEntry;
00375592
VZ
706
707 const wxString deleteName = _T("bin/bin1000");
708 const wxString renameFrom = _T("zero/zero1024");
709 const wxString renameTo = _T("zero/newname");
710 const wxString newName = _T("newfile");
711 const char *newData = "New file added as a test\n";
712
713 arcOut->CopyArchiveMetaData(*arcIn);
714
f44eaed6
RN
715 while ((pEntry = arcIn->GetNextEntry()) != NULL) {
716 auto_ptr<EntryT> entry(pEntry);
717 OnSetNotifier(*entry);
718 wxString name = entry->GetName(wxPATH_UNIX);
00375592
VZ
719
720 // provide some context for the error message so that we know which
721 // iteration of the loop we were on
95662a83
RN
722 string error_entry((_T(" '") + name + _T("'")).mb_str());
723 string error_context(" failed for entry" + error_entry);
00375592
VZ
724
725 if (name == deleteName) {
726 TestEntries::iterator it = m_testEntries.find(name);
727 CPPUNIT_ASSERT_MESSAGE(
728 "deletion failed (already deleted?) for" + error_entry,
729 it != m_testEntries.end());
730 TestEntry *p = it->second;
731 m_testEntries.erase(it);
732 delete p;
733 }
734 else {
735 if (name == renameFrom) {
f44eaed6 736 entry->SetName(renameTo);
00375592
VZ
737 TestEntries::iterator it = m_testEntries.find(renameFrom);
738 CPPUNIT_ASSERT_MESSAGE(
739 "rename failed (already renamed?) for" + error_entry,
740 it != m_testEntries.end());
741 TestEntry *p = it->second;
742 m_testEntries.erase(it);
743 m_testEntries[renameTo] = p;
744 }
745
746 CPPUNIT_ASSERT_MESSAGE("CopyEntry" + error_context,
f44eaed6 747 arcOut->CopyEntry(entry.release(), *arcIn));
00375592
VZ
748 }
749 }
750
751 // check that the deletion and rename were done
752 CPPUNIT_ASSERT(m_testEntries.count(deleteName) == 0);
753 CPPUNIT_ASSERT(m_testEntries.count(renameFrom) == 0);
754 CPPUNIT_ASSERT(m_testEntries.count(renameTo) == 1);
755
756 // check that the end of the input archive was reached without error
757 CPPUNIT_ASSERT(arcIn->Eof());
758
759 // try adding a new entry
760 TestEntry& testEntry = Add(newName.mb_str(), newData);
95662a83
RN
761 auto_ptr<EntryT> newentry(m_factory->NewEntry());
762 newentry->SetName(newName);
763 newentry->SetDateTime(testEntry.GetDateTime());
764 newentry->SetSize(testEntry.GetLength());
765 OnCreateEntry(*arcOut, testEntry, newentry.get());
766 OnSetNotifier(*newentry);
767 CPPUNIT_ASSERT(arcOut->PutNextEntry(newentry.release()));
00375592
VZ
768 CPPUNIT_ASSERT(arcOut->Write(newData, strlen(newData)).IsOk());
769
770 // should work with or without explicit Close
771 if (m_id % 2)
772 CPPUNIT_ASSERT(arcOut->Close());
773}
774
775// Extract an archive using the wx archive classes
776//
e6477b92
MW
777template <class ClassFactoryT>
778void ArchiveTestCase<ClassFactoryT>::ExtractArchive(wxInputStream& in)
00375592
VZ
779{
780 typedef Ptr<EntryT> EntryPtr;
781 typedef std::list<EntryPtr> Entries;
782 typedef typename Entries::iterator EntryIter;
783
95662a83 784 auto_ptr<InputStreamT> arc(m_factory->NewStream(in));
00375592
VZ
785 int expectedTotal = m_testEntries.size();
786 EntryPtr entry;
787 Entries entries;
788
789 if ((m_options & PipeIn) == 0)
790 OnArchiveExtracted(*arc, expectedTotal);
791
792 while (entry = EntryPtr(arc->GetNextEntry()), entry.get() != NULL) {
793 wxString name = entry->GetName(wxPATH_UNIX);
794
795 // provide some context for the error message so that we know which
796 // iteration of the loop we were on
95662a83
RN
797 string error_entry((_T(" '") + name + _T("'")).mb_str());
798 string error_context(" failed for entry" + error_entry);
00375592
VZ
799
800 TestEntries::iterator it = m_testEntries.find(name);
801 CPPUNIT_ASSERT_MESSAGE(
802 "archive contains an entry that shouldn't be there" + error_entry,
803 it != m_testEntries.end());
804
805 const TestEntry& testEntry = *it->second;
806
cc90c1cc
MW
807#ifndef __WXMSW__
808 // On Windows some archivers compensate for Windows DST handling, but
809 // other don't, so disable the test for now.
00375592
VZ
810 wxDateTime dt = testEntry.GetDateTime();
811 if (dt.IsValid())
812 CPPUNIT_ASSERT_MESSAGE("timestamp check" + error_context,
813 dt == entry->GetDateTime());
cc90c1cc 814#endif
00375592
VZ
815
816 // non-seekable entries are allowed to have GetSize == wxInvalidOffset
817 // until the end of the entry's data has been read past
818 CPPUNIT_ASSERT_MESSAGE("entry size check" + error_context,
819 testEntry.GetLength() == entry->GetSize() ||
820 ((m_options & PipeIn) != 0 && entry->GetSize() == wxInvalidOffset));
821 CPPUNIT_ASSERT_MESSAGE(
46263455
VZ
822 "arc->GetLength() == entry->GetSize()" + error_context,
823 arc->GetLength() == entry->GetSize());
00375592
VZ
824
825 if (name.Last() != _T('/'))
826 {
827 CPPUNIT_ASSERT_MESSAGE("!IsDir" + error_context,
828 !entry->IsDir());
829 wxCharBuffer buf(testEntry.GetSize() + 1);
830 CPPUNIT_ASSERT_MESSAGE("Read until Eof" + error_context,
831 arc->Read(buf.data(), testEntry.GetSize() + 1).Eof());
832 CPPUNIT_ASSERT_MESSAGE("LastRead check" + error_context,
833 arc->LastRead() == testEntry.GetSize());
834 CPPUNIT_ASSERT_MESSAGE("data compare" + error_context,
835 !memcmp(buf.data(), testEntry.GetData(), testEntry.GetSize()));
836 } else {
837 CPPUNIT_ASSERT_MESSAGE("IsDir" + error_context, entry->IsDir());
838 }
839
840 // GetSize() must return the right result in all cases after all the
841 // data has been read
842 CPPUNIT_ASSERT_MESSAGE("entry size check" + error_context,
843 testEntry.GetLength() == entry->GetSize());
844 CPPUNIT_ASSERT_MESSAGE(
46263455
VZ
845 "arc->GetLength() == entry->GetSize()" + error_context,
846 arc->GetLength() == entry->GetSize());
00375592
VZ
847
848 if ((m_options & PipeIn) == 0) {
849 OnEntryExtracted(*entry, testEntry, arc.get());
850 delete it->second;
851 m_testEntries.erase(it);
852 } else {
853 entries.push_back(entry);
854 }
855 }
856
857 // check that the end of the input archive was reached without error
858 CPPUNIT_ASSERT(arc->Eof());
859
860 // for non-seekable streams these data are only guaranteed to be
861 // available once the end of the archive has been reached
862 if (m_options & PipeIn) {
863 for (EntryIter i = entries.begin(); i != entries.end(); ++i) {
864 wxString name = (*i)->GetName(wxPATH_UNIX);
865 TestEntries::iterator j = m_testEntries.find(name);
866 OnEntryExtracted(**i, *j->second);
867 delete j->second;
868 m_testEntries.erase(j);
869 }
870 OnArchiveExtracted(*arc, expectedTotal);
871 }
872}
873
874// Extract an archive using an external unarchive program
875//
e6477b92
MW
876template <class ClassFactoryT>
877void ArchiveTestCase<ClassFactoryT>::ExtractArchive(wxInputStream& in,
878 const wxString& unarchiver)
00375592
VZ
879{
880 // for an external unarchiver, unarchive to a tempdir
881 TempDir tmpdir;
882
883 if ((m_options & PipeIn) == 0) {
884 wxFileName fn(tmpdir.GetName());
885 fn.SetExt(_T("arc"));
e6477b92 886 wxString tmparc = fn.GetPath(wxPATH_GET_SEPARATOR) + fn.GetFullName();
a09cd189 887
00375592 888 if (m_options & Stub)
e6477b92 889 in.SeekI(STUB_SIZE * 2);
00375592
VZ
890
891 // write the archive to a temporary file
892 {
893 wxFFileOutputStream out(tmparc);
894 if (out.Ok())
895 out.Write(in);
896 }
897
898 // call unarchiver
899 system(wxString::Format(unarchiver, tmparc.c_str()).mb_str());
900 wxRemoveFile(tmparc);
901 }
902 else {
903 // for the non-seekable test, have the archiver extract "-" and
904 // feed it the archive via a pipe
905 PFileOutputStream out(wxString::Format(unarchiver, _T("-")));
906 if (out.Ok())
907 out.Write(in);
908 }
909
910 wxString dir = tmpdir.GetName();
911 VerifyDir(dir);
912}
913
914// Verifies the files produced by an external unarchiver are as expected
915//
e6477b92
MW
916template <class ClassFactoryT>
917void ArchiveTestCase<ClassFactoryT>::VerifyDir(wxString& path,
918 size_t rootlen /*=0*/)
00375592
VZ
919{
920 wxDir dir;
921 path += wxFileName::GetPathSeparator();
922 int pos = path.length();
923 wxString name;
924
925 if (!rootlen)
926 rootlen = pos;
927
928 if (dir.Open(path) && dir.GetFirst(&name)) {
929 do {
930 path.replace(pos, wxString::npos, name);
931 name = m_factory->GetInternalName(
932 path.substr(rootlen, wxString::npos));
933
da865fdd 934 bool isDir = wxDirExists(path);
00375592
VZ
935 if (isDir)
936 name += _T("/");
937
938 // provide some context for the error message so that we know which
939 // iteration of the loop we were on
95662a83
RN
940 string error_entry((_T(" '") + name + _T("'")).mb_str());
941 string error_context(" failed for entry" + error_entry);
00375592
VZ
942
943 TestEntries::iterator it = m_testEntries.find(name);
944 CPPUNIT_ASSERT_MESSAGE(
945 "archive contains an entry that shouldn't be there"
946 + error_entry,
947 it != m_testEntries.end());
948
949 const TestEntry& testEntry = *it->second;
00375592 950
716e748b 951#if 0 //ndef __WXMSW__
00375592
VZ
952 CPPUNIT_ASSERT_MESSAGE("timestamp check" + error_context,
953 testEntry.GetDateTime() ==
954 wxFileName(path).GetModificationTime());
955#endif
956 if (!isDir) {
957 wxFFileInputStream in(path);
958 CPPUNIT_ASSERT_MESSAGE(
959 "entry not found in archive" + error_entry, in.Ok());
960
f44eaed6 961 size_t size = (size_t)in.GetLength();
00375592
VZ
962 wxCharBuffer buf(size);
963 CPPUNIT_ASSERT_MESSAGE("Read" + error_context,
964 in.Read(buf.data(), size).LastRead() == size);
965 CPPUNIT_ASSERT_MESSAGE("size check" + error_context,
966 testEntry.GetSize() == size);
967 CPPUNIT_ASSERT_MESSAGE("data compare" + error_context,
968 memcmp(buf.data(), testEntry.GetData(), size) == 0);
969 }
970 else {
971 VerifyDir(path, rootlen);
972 }
973
974 delete it->second;
975 m_testEntries.erase(it);
976 }
977 while (dir.GetNext(&name));
978 }
979}
980
981// test the simple iterators that give away ownership of an entry
982//
e6477b92
MW
983template <class ClassFactoryT>
984void ArchiveTestCase<ClassFactoryT>::TestIterator(wxInputStream& in)
00375592
VZ
985{
986 typedef std::list<EntryT*> ArchiveCatalog;
987 typedef typename ArchiveCatalog::iterator CatalogIter;
988
95662a83 989 auto_ptr<InputStreamT> arc(m_factory->NewStream(in));
00375592
VZ
990 size_t count = 0;
991
992#ifdef WXARC_MEMBER_TEMPLATES
993 ArchiveCatalog cat((IterT)*arc, IterT());
994#else
995 ArchiveCatalog cat;
996 for (IterT i(*arc); i != IterT(); ++i)
997 cat.push_back(*i);
998#endif
999
1000 for (CatalogIter it = cat.begin(); it != cat.end(); ++it) {
95662a83 1001 auto_ptr<EntryT> entry(*it);
00375592
VZ
1002 count += m_testEntries.count(entry->GetName(wxPATH_UNIX));
1003 }
1004
1005 CPPUNIT_ASSERT(m_testEntries.size() == cat.size());
1006 CPPUNIT_ASSERT(count == cat.size());
1007}
1008
1009// test the pair iterators that can be used to load a std::map or wxHashMap
1010// these also give away ownership of entries
1011//
e6477b92
MW
1012template <class ClassFactoryT>
1013void ArchiveTestCase<ClassFactoryT>::TestPairIterator(wxInputStream& in)
00375592
VZ
1014{
1015 typedef std::map<wxString, EntryT*> ArchiveCatalog;
1016 typedef typename ArchiveCatalog::iterator CatalogIter;
1017
95662a83 1018 auto_ptr<InputStreamT> arc(m_factory->NewStream(in));
00375592
VZ
1019 size_t count = 0;
1020
1021#ifdef WXARC_MEMBER_TEMPLATES
1022 ArchiveCatalog cat((PairIterT)*arc, PairIterT());
1023#else
1024 ArchiveCatalog cat;
1025 for (PairIterT i(*arc); i != PairIterT(); ++i)
95662a83 1026 cat.insert(*i);
00375592
VZ
1027#endif
1028
1029 for (CatalogIter it = cat.begin(); it != cat.end(); ++it) {
95662a83 1030 auto_ptr<EntryT> entry(it->second);
00375592
VZ
1031 count += m_testEntries.count(entry->GetName(wxPATH_UNIX));
1032 }
1033
1034 CPPUNIT_ASSERT(m_testEntries.size() == cat.size());
1035 CPPUNIT_ASSERT(count == cat.size());
1036}
1037
1038// simple iterators using smart pointers, no need to worry about ownership
1039//
e6477b92
MW
1040template <class ClassFactoryT>
1041void ArchiveTestCase<ClassFactoryT>::TestSmartIterator(wxInputStream& in)
00375592
VZ
1042{
1043 typedef std::list<Ptr<EntryT> > ArchiveCatalog;
1044 typedef typename ArchiveCatalog::iterator CatalogIter;
1045 typedef wxArchiveIterator<InputStreamT, Ptr<EntryT> > Iter;
1046
95662a83 1047 auto_ptr<InputStreamT> arc(m_factory->NewStream(in));
00375592
VZ
1048
1049#ifdef WXARC_MEMBER_TEMPLATES
1050 ArchiveCatalog cat((Iter)*arc, Iter());
1051#else
1052 ArchiveCatalog cat;
1053 for (Iter i(*arc); i != Iter(); ++i)
1054 cat.push_back(*i);
1055#endif
1056
1057 CPPUNIT_ASSERT(m_testEntries.size() == cat.size());
1058
1059 for (CatalogIter it = cat.begin(); it != cat.end(); ++it)
1060 CPPUNIT_ASSERT(m_testEntries.count((*it)->GetName(wxPATH_UNIX)));
1061}
1062
1063// pair iterator using smart pointers
1064//
e6477b92
MW
1065template <class ClassFactoryT>
1066void ArchiveTestCase<ClassFactoryT>::TestSmartPairIterator(wxInputStream& in)
00375592 1067{
f44eaed6
RN
1068#if defined _MSC_VER && defined _MSC_VER < 1200
1069 // With VC++ 5.0 the '=' operator of std::pair breaks when the second
1070 // type is Ptr<EntryT>, so this iterator can't be made to work.
1071 (void)in;
1072#else
00375592
VZ
1073 typedef std::map<wxString, Ptr<EntryT> > ArchiveCatalog;
1074 typedef typename ArchiveCatalog::iterator CatalogIter;
1075 typedef wxArchiveIterator<InputStreamT,
1076 std::pair<wxString, Ptr<EntryT> > > PairIter;
1077
95662a83 1078 auto_ptr<InputStreamT> arc(m_factory->NewStream(in));
00375592
VZ
1079
1080#ifdef WXARC_MEMBER_TEMPLATES
1081 ArchiveCatalog cat((PairIter)*arc, PairIter());
1082#else
1083 ArchiveCatalog cat;
1084 for (PairIter i(*arc); i != PairIter(); ++i)
95662a83 1085 cat.insert(*i);
00375592
VZ
1086#endif
1087
1088 CPPUNIT_ASSERT(m_testEntries.size() == cat.size());
1089
1090 for (CatalogIter it = cat.begin(); it != cat.end(); ++it)
1091 CPPUNIT_ASSERT(m_testEntries.count(it->second->GetName(wxPATH_UNIX)));
f44eaed6 1092#endif
00375592
VZ
1093}
1094
1095// try reading two entries at the same time
1096//
e6477b92
MW
1097template <class ClassFactoryT>
1098void ArchiveTestCase<ClassFactoryT>::ReadSimultaneous(TestInputStream& in)
00375592
VZ
1099{
1100 typedef std::map<wxString, Ptr<EntryT> > ArchiveCatalog;
1101 typedef wxArchiveIterator<InputStreamT,
1102 std::pair<wxString, Ptr<EntryT> > > PairIter;
1103
1104 // create two archive input streams
1105 TestInputStream in2(in);
95662a83
RN
1106 auto_ptr<InputStreamT> arc(m_factory->NewStream(in));
1107 auto_ptr<InputStreamT> arc2(m_factory->NewStream(in2));
00375592
VZ
1108
1109 // load the catalog
1110#ifdef WXARC_MEMBER_TEMPLATES
1111 ArchiveCatalog cat((PairIter)*arc, PairIter());
1112#else
1113 ArchiveCatalog cat;
1114 for (PairIter i(*arc); i != PairIter(); ++i)
95662a83 1115 cat.insert(*i);
00375592
VZ
1116#endif
1117
1118 // the names of two entries to read
1119 const wxChar *name = _T("text/small");
1120 const wxChar *name2 = _T("bin/bin1000");
1121
1122 // open them
1123 typename ArchiveCatalog::iterator j;
1124 CPPUNIT_ASSERT((j = cat.find(name)) != cat.end());
1125 CPPUNIT_ASSERT(arc->OpenEntry(*j->second));
1126 CPPUNIT_ASSERT((j = cat.find(name2)) != cat.end());
1127 CPPUNIT_ASSERT(arc2->OpenEntry(*j->second));
1128
1129 // get pointers to the expected data
1130 TestEntries::iterator k;
1131 CPPUNIT_ASSERT((k = m_testEntries.find(name)) != m_testEntries.end());
1132 TestEntry *entry = k->second;
1133 CPPUNIT_ASSERT((k = m_testEntries.find(name2)) != m_testEntries.end());
1134 TestEntry *entry2 = k->second;
1135
1136 size_t count = 0, count2 = 0;
1137 size_t size = entry->GetSize(), size2 = entry2->GetSize();
1138 const char *data = entry->GetData(), *data2 = entry2->GetData();
1139
1140 // read and check the two entries in parallel, character by character
1141 while (arc->IsOk() || arc2->IsOk()) {
1142 char ch = arc->GetC();
1143 if (arc->LastRead() == 1) {
1144 CPPUNIT_ASSERT(count < size);
1145 CPPUNIT_ASSERT(ch == data[count++]);
1146 }
1147 char ch2 = arc2->GetC();
1148 if (arc2->LastRead() == 1) {
1149 CPPUNIT_ASSERT(count2 < size2);
1150 CPPUNIT_ASSERT(ch2 == data2[count2++]);
1151 }
1152 }
1153
1154 CPPUNIT_ASSERT(arc->Eof());
1155 CPPUNIT_ASSERT(arc2->Eof());
1156 CPPUNIT_ASSERT(count == size);
1157 CPPUNIT_ASSERT(count2 == size2);
1158}
1159
1160// Nothing useful can be done with a generic notifier yet, so just test one
1161// can be set
1162//
1163template <class NotifierT, class EntryT>
1164class ArchiveNotifier : public NotifierT
1165{
1166public:
1167 void OnEntryUpdated(EntryT& WXUNUSED(entry)) { }
1168};
1169
e6477b92
MW
1170template <class ClassFactoryT>
1171void ArchiveTestCase<ClassFactoryT>::OnSetNotifier(EntryT& entry)
00375592
VZ
1172{
1173 static ArchiveNotifier<NotifierT, EntryT> notifier;
1174 entry.SetNotifier(notifier);
1175}
1176
1177
98de4ac1
MW
1178///////////////////////////////////////////////////////////////////////////////
1179// An additional case to check that reading corrupt archives doesn't crash
1180
1181class CorruptionTestCase : public CppUnit::TestCase
1182{
1183public:
1184 CorruptionTestCase(std::string name,
1185 wxArchiveClassFactory *factory,
1186 int options)
1187 : CppUnit::TestCase(TestId::MakeId() + name),
1188 m_factory(factory),
1189 m_options(options)
1190 { }
1191
1192protected:
1193 // the entry point for the test
1194 void runTest();
1195
1196 void CreateArchive(wxOutputStream& out);
1197 void ExtractArchive(wxInputStream& in);
1198
1199 auto_ptr<wxArchiveClassFactory> m_factory; // factory to make classes
1200 int m_options; // test options
1201};
1202
1203void CorruptionTestCase::runTest()
1204{
1205 TestOutputStream out(m_options);
1206 CreateArchive(out);
1207 TestInputStream in(out, 0);
1208 wxFileOffset len = in.GetLength();
1209
1210 // try flipping one byte in the archive
5098c258
VZ
1211 int pos;
1212 for (pos = 0; pos < len; pos++) {
98de4ac1
MW
1213 char n = in[pos];
1214 in[pos] = ~n;
1215 ExtractArchive(in);
1216 in.Rewind();
1217 in[pos] = n;
1218 }
1219
1220 // try zeroing one byte in the archive
5098c258 1221 for (pos = 0; pos < len; pos++) {
98de4ac1
MW
1222 char n = in[pos];
1223 in[pos] = 0;
1224 ExtractArchive(in);
1225 in.Rewind();
1226 in[pos] = n;
1227 }
1228
1229 // try chopping the archive off
1230 for (int size = 1; size <= len; size++) {
1231 in.Chop(size);
1232 ExtractArchive(in);
1233 in.Rewind();
1234 }
1235}
1236
1237void CorruptionTestCase::CreateArchive(wxOutputStream& out)
1238{
1239 auto_ptr<wxArchiveOutputStream> arc(m_factory->NewStream(out));
1240
1241 arc->PutNextDirEntry(_T("dir"));
1242 arc->PutNextEntry(_T("file"));
1243 arc->Write(_T("foo"), 3);
1244}
1245
1246void CorruptionTestCase::ExtractArchive(wxInputStream& in)
1247{
1248 auto_ptr<wxArchiveInputStream> arc(m_factory->NewStream(in));
1249 auto_ptr<wxArchiveEntry> entry(arc->GetNextEntry());
93a800a9 1250
98de4ac1 1251 while (entry.get() != NULL) {
98de4ac1 1252 char buf[1024];
93a800a9 1253
98de4ac1
MW
1254 while (arc->IsOk())
1255 arc->Read(buf, sizeof(buf));
1256
ed626c8b
VZ
1257 auto_ptr<wxArchiveEntry> next(arc->GetNextEntry());
1258 entry = next;
98de4ac1
MW
1259 }
1260}
1261
1262
716e748b
MW
1263///////////////////////////////////////////////////////////////////////////////
1264// Make the ids
1265
1266int TestId::m_seed = 6219;
1267
1268// static
1269string TestId::MakeId()
1270{
1271 m_seed = (m_seed * 171) % 30269;
98de4ac1 1272 return string(wxString::Format(_T("%-6d"), m_seed).mb_str());
716e748b
MW
1273}
1274
1275
00375592 1276///////////////////////////////////////////////////////////////////////////////
e6477b92 1277// Suite base
00375592 1278
e6477b92
MW
1279ArchiveTestSuite::ArchiveTestSuite(string name)
1280 : CppUnit::TestSuite("archive/" + name),
e6477b92 1281 m_name(name.c_str(), *wxConvCurrent)
00375592 1282{
e6477b92 1283 m_name = _T("wx") + m_name.Left(1).Upper() + m_name.Mid(1).Lower();
00375592 1284 m_path.AddEnvList(_T("PATH"));
e6477b92
MW
1285 m_archivers.push_back(_T(""));
1286 m_unarchivers.push_back(_T(""));
00375592
VZ
1287}
1288
1289// add the command for an external archiver to the list, testing for it in
1290// the path first
1291//
1292void ArchiveTestSuite::AddCmd(wxArrayString& cmdlist, const wxString& cmd)
1293{
00375592
VZ
1294 if (IsInPath(cmd))
1295 cmdlist.push_back(cmd);
1296}
1297
1298bool ArchiveTestSuite::IsInPath(const wxString& cmd)
1299{
1300 wxString c = cmd.BeforeFirst(_T(' '));
1301#ifdef __WXMSW__
1302 c += _T(".exe");
1303#endif
1304 return !m_path.FindValidPath(c).empty();
1305}
1306
1307// make the test suite
1308//
1309ArchiveTestSuite *ArchiveTestSuite::makeSuite()
1310{
1311 typedef wxArrayString::iterator Iter;
00375592 1312
e6477b92
MW
1313 for (int generic = 0; generic < 2; generic++)
1314 for (Iter i = m_unarchivers.begin(); i != m_unarchivers.end(); ++i)
1315 for (Iter j = m_archivers.begin(); j != m_archivers.end(); ++j)
00375592
VZ
1316 for (int options = 0; options <= AllOptions; options++)
1317 {
e6477b92
MW
1318#ifdef WXARC_NO_POPEN
1319 // if no popen then can't pipe in/out of archiver
00375592
VZ
1320 if ((options & PipeIn) && !i->empty())
1321 continue;
00375592
VZ
1322 if ((options & PipeOut) && !j->empty())
1323 continue;
1324#endif
e6477b92
MW
1325 string descr = Description(m_name, options,
1326 generic != 0, *j, *i);
00375592 1327
716e748b 1328 CppUnit::Test *test = makeTest(descr, options,
e6477b92
MW
1329 generic != 0, *j, *i);
1330
716e748b 1331 if (test)
e6477b92 1332 addTest(test);
e6477b92 1333 }
00375592 1334
93a800a9 1335 for (int options = 0; options <= PipeIn; options += PipeIn)
98de4ac1
MW
1336 {
1337 wxObject *pObj = wxCreateDynamicObject(m_name + _T("ClassFactory"));
1338 wxArchiveClassFactory *factory;
1339 factory = wxDynamicCast(pObj, wxArchiveClassFactory);
1340
1341 if (factory) {
1342 string descr(m_name.mb_str());
1343 descr = "CorruptionTestCase (" + descr + ")";
1344
1345 if (options)
1346 descr += " (PipeIn)";
1347
1348 addTest(new CorruptionTestCase(descr, factory, options));
1349 }
1350 }
1351
00375592
VZ
1352 return this;
1353}
1354
e6477b92
MW
1355CppUnit::Test *ArchiveTestSuite::makeTest(
1356 string WXUNUSED(descr),
e6477b92
MW
1357 int WXUNUSED(options),
1358 bool WXUNUSED(genericInterface),
1359 const wxString& WXUNUSED(archiver),
1360 const wxString& WXUNUSED(unarchiver))
1361{
1362 return NULL;
1363}
1364
00375592
VZ
1365// make a display string for the option bits
1366//
95662a83
RN
1367string ArchiveTestSuite::Description(const wxString& type,
1368 int options,
1369 bool genericInterface,
1370 const wxString& archiver,
1371 const wxString& unarchiver)
00375592
VZ
1372{
1373 wxString descr;
a09cd189 1374
00375592
VZ
1375 if (genericInterface)
1376 descr << _T("wxArchive (") << type << _T(")");
1377 else
1378 descr << type;
1379
e6477b92
MW
1380 if (!archiver.empty()) {
1381 const wxChar *fn = (options & PipeOut) != 0 ? _T("-") : _T("file");
1382 descr << _T(" (") << wxString::Format(archiver, fn) << _T(")");
1383 }
1384 if (!unarchiver.empty()) {
1385 const wxChar *fn = (options & PipeIn) != 0 ? _T("-") : _T("file");
1386 descr << _T(" (") << wxString::Format(unarchiver, fn) << _T(")");
1387 }
a09cd189 1388
00375592
VZ
1389 wxString optstr;
1390
1391 if ((options & PipeIn) != 0)
1392 optstr += _T("|PipeIn");
1393 if ((options & PipeOut) != 0)
1394 optstr += _T("|PipeOut");
1395 if ((options & Stub) != 0)
1396 optstr += _T("|Stub");
1397 if (!optstr.empty())
1398 optstr = _T(" (") + optstr.substr(1) + _T(")");
1399
1400 descr << optstr;
1401
98de4ac1 1402 return string(descr.mb_str());
00375592
VZ
1403}
1404
00375592 1405
e6477b92
MW
1406///////////////////////////////////////////////////////////////////////////////
1407// Instantiations
1408
1409template class ArchiveTestCase<wxArchiveClassFactory>;
1410
1411#if wxUSE_ZIPSTREAM
1412#include "wx/zipstrm.h"
1413template class ArchiveTestCase<wxZipClassFactory>;
1414#endif
00375592 1415
cdd7933f
MW
1416#if wxUSE_TARSTREAM
1417#include "wx/tarstrm.h"
1418template class ArchiveTestCase<wxTarClassFactory>;
1419#endif
1420
9e8e867f 1421#endif // wxUSE_STREAMS && wxUSE_ARCHIVE_STREAMS