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