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