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