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