]>
git.saurik.com Git - wxWidgets.git/blob - tests/archive/archivetest.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: tests/archive/archive.cpp
3 // Purpose: Test the archive classes
4 // Author: Mike Wetherell
6 // Copyright: (c) 2004 Mike Wetherell
7 // Licence: wxWindows licence
8 ///////////////////////////////////////////////////////////////////////////////
20 #if wxUSE_STREAMS && wxUSE_ARCHIVE_STREAMS
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
25 #pragma warning (disable:4284)
28 #include "archivetest.h"
39 // Check whether member templates can be used
41 #if defined __GNUC__ && \
42 (__GNUC__ >= 3 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95))
43 # define WXARC_MEMBER_TEMPLATES
45 #if defined _MSC_VER && _MSC_VER >= 1310
46 # define WXARC_MEMBER_TEMPLATES
48 #if defined __BORLANDC__ && __BORLANDC__ >= 0x530
49 # define WXARC_MEMBER_TEMPLATES
51 #if defined __DMC__ && __DMC__ >= 0x832
52 # define WXARC_MEMBER_TEMPLATES
54 #if defined __MWERKS__ && __MWERKS__ >= 0x2200
55 # define WXARC_MEMBER_TEMPLATES
57 #if defined __HP_aCC && __HP_aCC > 33300
58 # define WXARC_MEMBER_TEMPLATES
60 #if defined __SUNPRO_CC && __SUNPRO_CC > 0x500
61 # define WXARC_MEMBER_TEMPLATES
65 ///////////////////////////////////////////////////////////////////////////////
66 // A class to hold a test entry
68 TestEntry::TestEntry(const wxDateTime
& dt
, int len
, const char *data
)
73 m_data
= new char[len
];
74 memcpy(m_data
, data
, len
);
76 for (int i
= 0; i
< len
&& m_isText
; i
++)
77 m_isText
= (signed char)m_data
[i
] > 0;
81 ///////////////////////////////////////////////////////////////////////////////
82 // TestOutputStream and TestInputStream are memory streams which can be
83 // seekable or non-seekable.
85 const size_t STUB_SIZE
= 2048;
86 const size_t INITIAL_SIZE
= 0x18000;
87 const wxFileOffset SEEK_LIMIT
= 0x100000;
89 TestOutputStream::TestOutputStream(int options
)
95 void TestOutputStream::Init()
102 if (m_options
& Stub
) {
103 wxCharBuffer
buf(STUB_SIZE
);
104 memset(buf
.data(), 0, STUB_SIZE
);
105 Write(buf
, STUB_SIZE
);
109 wxFileOffset
TestOutputStream::OnSysSeek(wxFileOffset pos
, wxSeekMode mode
)
111 if ((m_options
& PipeOut
) == 0) {
113 case wxFromStart
: break;
114 case wxFromCurrent
: pos
+= m_pos
; break;
115 case wxFromEnd
: pos
+= m_size
; break;
117 if (pos
< 0 || pos
> SEEK_LIMIT
)
118 return wxInvalidOffset
;
122 return wxInvalidOffset
;
125 wxFileOffset
TestOutputStream::OnSysTell() const
127 return (m_options
& PipeOut
) == 0 ? (wxFileOffset
)m_pos
: wxInvalidOffset
;
130 size_t TestOutputStream::OnSysWrite(const void *buffer
, size_t size
)
132 if (!IsOk() || !size
)
134 m_lasterror
= wxSTREAM_WRITE_ERROR
;
136 size_t newsize
= m_pos
+ size
;
137 wxCHECK(newsize
> m_pos
, 0);
139 if (m_capacity
< newsize
) {
140 size_t capacity
= m_capacity
? m_capacity
: INITIAL_SIZE
;
142 while (capacity
< newsize
) {
144 wxCHECK(capacity
> m_capacity
, 0);
147 char *buf
= new char[capacity
];
149 memcpy(buf
, m_data
, m_capacity
);
152 m_capacity
= capacity
;
155 memcpy(m_data
+ m_pos
, buffer
, size
);
159 m_lasterror
= wxSTREAM_NO_ERROR
;
164 void TestOutputStream::GetData(char*& data
, size_t& size
)
169 if (m_options
& Stub
) {
173 if (size
> m_capacity
) {
175 memcpy(d
+ STUB_SIZE
, m_data
, m_size
);
179 memmove(d
+ STUB_SIZE
, d
, m_size
);
182 memset(d
, 0, STUB_SIZE
);
191 ///////////////////////////////////////////////////////////////////////////////
192 // TestOutputStream and TestInputStream are memory streams which can be
193 // seekable or non-seekable.
195 TestInputStream::TestInputStream(const TestInputStream
& in
)
197 m_options(in
.m_options
),
201 m_data
= new char[m_size
];
202 memcpy(m_data
, in
.m_data
, m_size
);
205 void TestInputStream::Rewind()
207 if ((m_options
& Stub
) && (m_options
& PipeIn
))
208 m_pos
= STUB_SIZE
* 2;
220 void TestInputStream::SetData(TestOutputStream
& out
)
223 m_options
= out
.GetOptions();
224 out
.GetData(m_data
, m_size
);
229 wxFileOffset
TestInputStream::OnSysSeek(wxFileOffset pos
, wxSeekMode mode
)
231 if ((m_options
& PipeIn
) == 0) {
233 case wxFromStart
: break;
234 case wxFromCurrent
: pos
+= m_pos
; break;
235 case wxFromEnd
: pos
+= m_size
; break;
237 if (pos
< 0 || pos
> SEEK_LIMIT
)
238 return wxInvalidOffset
;
242 return wxInvalidOffset
;
245 wxFileOffset
TestInputStream::OnSysTell() const
247 return (m_options
& PipeIn
) == 0 ? (wxFileOffset
)m_pos
: wxInvalidOffset
;
250 size_t TestInputStream::OnSysRead(void *buffer
, size_t size
)
252 if (!IsOk() || !size
)
254 if (m_size
<= m_pos
) {
255 m_lasterror
= wxSTREAM_EOF
;
259 if (m_size
- m_pos
< size
)
260 size
= m_size
- m_pos
;
261 memcpy(buffer
, m_data
+ m_pos
, size
);
267 ///////////////////////////////////////////////////////////////////////////////
268 // minimal non-intrusive reference counting pointer for testing the iterators
270 template <class T
> class Ptr
273 explicit Ptr(T
* p
= NULL
) : m_p(p
), m_count(new int) { *m_count
= 1; }
274 Ptr(const Ptr
& sp
) : m_p(sp
.m_p
), m_count(sp
.m_count
) { ++*m_count
; }
277 Ptr
& operator =(const Ptr
& sp
) {
281 m_count
= sp
.m_count
;
287 T
* get() const { return m_p
; }
288 T
* operator->() const { return m_p
; }
289 T
& operator*() const { return *m_p
; }
293 if (--*m_count
== 0) {
304 ///////////////////////////////////////////////////////////////////////////////
305 // Clean-up for temp directory
312 wxString
GetName() const { return m_tmp
; }
315 void RemoveDir(wxString
& path
);
322 wxString tmp
= wxFileName::CreateTempFileName(_T("arctest-"));
323 if (tmp
!= wxEmptyString
) {
325 m_original
= wxGetCwd();
326 CPPUNIT_ASSERT(wxMkdir(tmp
, 0700));
328 CPPUNIT_ASSERT(wxSetWorkingDirectory(tmp
));
334 if (m_tmp
!= wxEmptyString
) {
335 wxSetWorkingDirectory(m_original
);
340 void TempDir::RemoveDir(wxString
& path
)
342 wxCHECK_RET(!m_tmp
.empty() && path
.substr(0, m_tmp
.length()) == m_tmp
,
343 _T("remove '") + path
+ _T("' fails safety check"));
345 const wxChar
*files
[] = {
355 _T("zero/zero32768"),
356 _T("zero/zero16385"),
361 const wxChar
*dirs
[] = {
362 _T("text/"), _T("bin/"), _T("zero/"), _T("empty/")
365 wxString tmp
= m_tmp
+ wxFileName::GetPathSeparator();
368 for (i
= 0; i
< WXSIZEOF(files
); i
++)
369 wxRemoveFile(tmp
+ wxFileName(files
[i
], wxPATH_UNIX
).GetFullPath());
371 for (i
= 0; i
< WXSIZEOF(dirs
); i
++)
372 wxRmdir(tmp
+ wxFileName(dirs
[i
], wxPATH_UNIX
).GetFullPath());
375 wxLogSysError(_T("can't remove temporary dir '%s'"), m_tmp
.c_str());
379 ///////////////////////////////////////////////////////////////////////////////
380 // wxFFile streams for piping to/from an external program
382 #if defined __UNIX__ || defined __MINGW32__
383 # define WXARC_popen popen
384 # define WXARC_pclose pclose
385 #elif defined _MSC_VER || defined __BORLANDC__
386 # define WXARC_popen _popen
387 # define WXARC_pclose _pclose
389 # define WXARC_NO_POPEN
390 # define WXARC_popen(cmd, type) NULL
391 # define WXARC_pclose(fp)
400 PFileInputStream::PFileInputStream(const wxString
& cmd
)
401 : wxFFileInputStream(WXARC_popen(cmd
.mb_str(), "r" WXARC_b
))
405 PFileInputStream::~PFileInputStream()
407 WXARC_pclose(m_file
->fp()); m_file
->Detach();
410 PFileOutputStream::PFileOutputStream(const wxString
& cmd
)
411 : wxFFileOutputStream(WXARC_popen(cmd
.mb_str(), "w" WXARC_b
))
415 PFileOutputStream::~PFileOutputStream()
417 WXARC_pclose(m_file
->fp()); m_file
->Detach();
421 ///////////////////////////////////////////////////////////////////////////////
424 template <class ClassFactoryT
>
425 ArchiveTestCase
<ClassFactoryT
>::ArchiveTestCase(
428 ClassFactoryT
*factory
,
430 const wxString
& archiver
,
431 const wxString
& unarchiver
)
433 CppUnit::TestCase(name
),
436 m_timeStamp(1, wxDateTime::Mar
, 2004, 12, 0),
438 m_archiver(archiver
),
439 m_unarchiver(unarchiver
)
441 wxASSERT(m_factory
.get() != NULL
);
444 template <class ClassFactoryT
>
445 ArchiveTestCase
<ClassFactoryT
>::~ArchiveTestCase()
447 TestEntries::iterator it
;
448 for (it
= m_testEntries
.begin(); it
!= m_testEntries
.end(); ++it
)
452 template <class ClassFactoryT
>
453 void ArchiveTestCase
<ClassFactoryT
>::runTest()
455 TestOutputStream
out(m_options
);
459 if (m_archiver
.empty())
462 CreateArchive(out
, m_archiver
);
464 // check archive could be created
465 CPPUNIT_ASSERT(out
.GetLength() > 0);
467 TestInputStream
in(out
);
471 TestPairIterator(in
);
473 TestSmartIterator(in
);
475 TestSmartPairIterator(in
);
478 if ((m_options
& PipeIn
) == 0) {
479 ReadSimultaneous(in
);
483 ModifyArchive(in
, out
);
486 if (m_unarchiver
.empty())
489 ExtractArchive(in
, m_unarchiver
);
491 // check that all the test entries were found in the archive
492 CPPUNIT_ASSERT(m_testEntries
.empty());
495 template <class ClassFactoryT
>
496 void ArchiveTestCase
<ClassFactoryT
>::CreateTestData()
499 Add("text/empty", "");
500 Add("text/small", "Small text file for testing\n"
501 "archive streams in wxWidgets\n");
504 Add("bin/bin1000", 1000);
505 Add("bin/bin4095", 4095);
506 Add("bin/bin4096", 4096);
507 Add("bin/bin4097", 4097);
508 Add("bin/bin16384", 16384);
511 Add("zero/zero5", 5, 0);
512 Add("zero/zero1024", 1024, 109);
513 Add("zero/zero32768", 32768, 106);
514 Add("zero/zero16385", 16385, 119);
519 template <class ClassFactoryT
>
520 TestEntry
& ArchiveTestCase
<ClassFactoryT
>::Add(const char *name
,
526 TestEntry
*& entry
= m_testEntries
[wxString(name
, *wxConvCurrent
)];
527 wxASSERT(entry
== NULL
);
528 entry
= new TestEntry(m_timeStamp
, len
, data
);
529 m_timeStamp
+= wxTimeSpan(0, 1, 30);
533 template <class ClassFactoryT
>
534 TestEntry
& ArchiveTestCase
<ClassFactoryT
>::Add(const char *name
,
538 wxCharBuffer
buf(len
);
539 for (int i
= 0; i
< len
; i
++)
540 buf
.data()[i
] = (char)(value
== EOF
? rand() : value
);
541 return Add(name
, buf
, len
);
544 // Create an archive using the wx archive classes, write it to 'out'
546 template <class ClassFactoryT
>
547 void ArchiveTestCase
<ClassFactoryT
>::CreateArchive(wxOutputStream
& out
)
549 auto_ptr
<OutputStreamT
> arc(m_factory
->NewStream(out
));
550 TestEntries::iterator it
;
552 OnCreateArchive(*arc
);
554 // We want to try creating entries in various different ways, 'choices'
555 // is just a number used to select between all the various possibilities.
558 for (it
= m_testEntries
.begin(); it
!= m_testEntries
.end(); ++it
) {
560 TestEntry
& testEntry
= *it
->second
;
561 wxString name
= it
->first
;
563 // It should be possible to create a directory entry just by supplying
564 // a name that looks like a directory, or alternatively any old name
565 // can be identified as a directory using SetIsDir or PutNextDirEntry
566 bool setIsDir
= name
.Last() == _T('/') && (choices
& 1);
568 name
.erase(name
.length() - 1);
570 // provide some context for the error message so that we know which
571 // iteration of the loop we were on
572 string
error_entry((_T(" '") + name
+ _T("'")).mb_str());
573 string
error_context(" failed for entry" + error_entry
);
575 if ((choices
& 2) || testEntry
.IsText()) {
576 // try PutNextEntry(EntryT *pEntry)
577 auto_ptr
<EntryT
> entry(m_factory
->NewEntry());
578 entry
->SetName(name
, wxPATH_UNIX
);
581 entry
->SetDateTime(testEntry
.GetDateTime());
582 entry
->SetSize(testEntry
.GetLength());
583 OnCreateEntry(*arc
, testEntry
, entry
.get());
584 CPPUNIT_ASSERT_MESSAGE("PutNextEntry" + error_context
,
585 arc
->PutNextEntry(entry
.release()));
588 // try the convenience methods
589 OnCreateEntry(*arc
, testEntry
);
591 CPPUNIT_ASSERT_MESSAGE("PutNextDirEntry" + error_context
,
592 arc
->PutNextDirEntry(name
, testEntry
.GetDateTime()));
594 CPPUNIT_ASSERT_MESSAGE("PutNextEntry" + error_context
,
595 arc
->PutNextEntry(name
, testEntry
.GetDateTime(),
596 testEntry
.GetLength()));
599 if (it
->first
.Last() != _T('/')) {
600 // for non-dirs write the data
601 arc
->Write(testEntry
.GetData(), testEntry
.GetSize());
602 CPPUNIT_ASSERT_MESSAGE("LastWrite check" + error_context
,
603 arc
->LastWrite() == testEntry
.GetSize());
604 // should work with or without explicit CloseEntry
606 CPPUNIT_ASSERT_MESSAGE("CloseEntry" + error_context
,
610 CPPUNIT_ASSERT_MESSAGE("IsOk" + error_context
, arc
->IsOk());
613 // should work with or without explicit Close
615 CPPUNIT_ASSERT(arc
->Close());
618 // Create an archive using an external archive program
620 template <class ClassFactoryT
>
621 void ArchiveTestCase
<ClassFactoryT
>::CreateArchive(wxOutputStream
& out
,
622 const wxString
& archiver
)
624 // for an external archiver the test data need to be written to
629 TestEntries::iterator i
;
630 for (i
= m_testEntries
.begin(); i
!= m_testEntries
.end(); ++i
) {
631 wxFileName
fn(i
->first
, wxPATH_UNIX
);
632 TestEntry
& entry
= *i
->second
;
635 fn
.Mkdir(0777, wxPATH_MKDIR_FULL
);
637 wxFileName::Mkdir(fn
.GetPath(), 0777, wxPATH_MKDIR_FULL
);
638 wxFFileOutputStream
fileout(fn
.GetFullPath());
639 fileout
.Write(entry
.GetData(), entry
.GetSize());
643 for (i
= m_testEntries
.begin(); i
!= m_testEntries
.end(); ++i
) {
644 wxFileName
fn(i
->first
, wxPATH_UNIX
);
645 TestEntry
& entry
= *i
->second
;
646 wxDateTime dt
= entry
.GetDateTime();
649 entry
.SetDateTime(wxDateTime());
652 fn
.SetTimes(NULL
, &dt
, NULL
);
655 if ((m_options
& PipeOut
) == 0) {
656 wxFileName
fn(tmpdir
.GetName());
657 fn
.SetExt(_T("arc"));
658 wxString tmparc
= fn
.GetPath(wxPATH_GET_SEPARATOR
) + fn
.GetFullName();
660 // call the archiver to create an archive file
661 system(wxString::Format(archiver
, tmparc
.c_str()).mb_str());
663 // then load the archive file
665 wxFFileInputStream
in(tmparc
);
670 wxRemoveFile(tmparc
);
673 // for the non-seekable test, have the archiver output to "-"
674 // and read the archive via a pipe
675 PFileInputStream
in(wxString::Format(archiver
, _T("-")));
681 // Do a standard set of modification on an archive, delete an entry,
682 // rename an entry and add an entry
684 template <class ClassFactoryT
>
685 void ArchiveTestCase
<ClassFactoryT
>::ModifyArchive(wxInputStream
& in
,
688 auto_ptr
<InputStreamT
> arcIn(m_factory
->NewStream(in
));
689 auto_ptr
<OutputStreamT
> arcOut(m_factory
->NewStream(out
));
692 const wxString deleteName
= _T("bin/bin1000");
693 const wxString renameFrom
= _T("zero/zero1024");
694 const wxString renameTo
= _T("zero/newname");
695 const wxString newName
= _T("newfile");
696 const char *newData
= "New file added as a test\n";
698 arcOut
->CopyArchiveMetaData(*arcIn
);
700 while ((pEntry
= arcIn
->GetNextEntry()) != NULL
) {
701 auto_ptr
<EntryT
> entry(pEntry
);
702 OnSetNotifier(*entry
);
703 wxString name
= entry
->GetName(wxPATH_UNIX
);
705 // provide some context for the error message so that we know which
706 // iteration of the loop we were on
707 string
error_entry((_T(" '") + name
+ _T("'")).mb_str());
708 string
error_context(" failed for entry" + error_entry
);
710 if (name
== deleteName
) {
711 TestEntries::iterator it
= m_testEntries
.find(name
);
712 CPPUNIT_ASSERT_MESSAGE(
713 "deletion failed (already deleted?) for" + error_entry
,
714 it
!= m_testEntries
.end());
715 TestEntry
*p
= it
->second
;
716 m_testEntries
.erase(it
);
720 if (name
== renameFrom
) {
721 entry
->SetName(renameTo
);
722 TestEntries::iterator it
= m_testEntries
.find(renameFrom
);
723 CPPUNIT_ASSERT_MESSAGE(
724 "rename failed (already renamed?) for" + error_entry
,
725 it
!= m_testEntries
.end());
726 TestEntry
*p
= it
->second
;
727 m_testEntries
.erase(it
);
728 m_testEntries
[renameTo
] = p
;
731 CPPUNIT_ASSERT_MESSAGE("CopyEntry" + error_context
,
732 arcOut
->CopyEntry(entry
.release(), *arcIn
));
736 // check that the deletion and rename were done
737 CPPUNIT_ASSERT(m_testEntries
.count(deleteName
) == 0);
738 CPPUNIT_ASSERT(m_testEntries
.count(renameFrom
) == 0);
739 CPPUNIT_ASSERT(m_testEntries
.count(renameTo
) == 1);
741 // check that the end of the input archive was reached without error
742 CPPUNIT_ASSERT(arcIn
->Eof());
744 // try adding a new entry
745 TestEntry
& testEntry
= Add(newName
.mb_str(), newData
);
746 auto_ptr
<EntryT
> newentry(m_factory
->NewEntry());
747 newentry
->SetName(newName
);
748 newentry
->SetDateTime(testEntry
.GetDateTime());
749 newentry
->SetSize(testEntry
.GetLength());
750 OnCreateEntry(*arcOut
, testEntry
, newentry
.get());
751 OnSetNotifier(*newentry
);
752 CPPUNIT_ASSERT(arcOut
->PutNextEntry(newentry
.release()));
753 CPPUNIT_ASSERT(arcOut
->Write(newData
, strlen(newData
)).IsOk());
755 // should work with or without explicit Close
757 CPPUNIT_ASSERT(arcOut
->Close());
760 // Extract an archive using the wx archive classes
762 template <class ClassFactoryT
>
763 void ArchiveTestCase
<ClassFactoryT
>::ExtractArchive(wxInputStream
& in
)
765 typedef Ptr
<EntryT
> EntryPtr
;
766 typedef std::list
<EntryPtr
> Entries
;
767 typedef typename
Entries::iterator EntryIter
;
769 auto_ptr
<InputStreamT
> arc(m_factory
->NewStream(in
));
770 int expectedTotal
= m_testEntries
.size();
774 if ((m_options
& PipeIn
) == 0)
775 OnArchiveExtracted(*arc
, expectedTotal
);
777 while (entry
= EntryPtr(arc
->GetNextEntry()), entry
.get() != NULL
) {
778 wxString name
= entry
->GetName(wxPATH_UNIX
);
780 // provide some context for the error message so that we know which
781 // iteration of the loop we were on
782 string
error_entry((_T(" '") + name
+ _T("'")).mb_str());
783 string
error_context(" failed for entry" + error_entry
);
785 TestEntries::iterator it
= m_testEntries
.find(name
);
786 CPPUNIT_ASSERT_MESSAGE(
787 "archive contains an entry that shouldn't be there" + error_entry
,
788 it
!= m_testEntries
.end());
790 const TestEntry
& testEntry
= *it
->second
;
792 wxDateTime dt
= testEntry
.GetDateTime();
794 CPPUNIT_ASSERT_MESSAGE("timestamp check" + error_context
,
795 dt
== entry
->GetDateTime());
797 // non-seekable entries are allowed to have GetSize == wxInvalidOffset
798 // until the end of the entry's data has been read past
799 CPPUNIT_ASSERT_MESSAGE("entry size check" + error_context
,
800 testEntry
.GetLength() == entry
->GetSize() ||
801 ((m_options
& PipeIn
) != 0 && entry
->GetSize() == wxInvalidOffset
));
802 CPPUNIT_ASSERT_MESSAGE(
803 "arc->GetLength() == entry->GetSize()" + error_context
,
804 arc
->GetLength() == entry
->GetSize());
806 if (name
.Last() != _T('/'))
808 CPPUNIT_ASSERT_MESSAGE("!IsDir" + error_context
,
810 wxCharBuffer
buf(testEntry
.GetSize() + 1);
811 CPPUNIT_ASSERT_MESSAGE("Read until Eof" + error_context
,
812 arc
->Read(buf
.data(), testEntry
.GetSize() + 1).Eof());
813 CPPUNIT_ASSERT_MESSAGE("LastRead check" + error_context
,
814 arc
->LastRead() == testEntry
.GetSize());
815 CPPUNIT_ASSERT_MESSAGE("data compare" + error_context
,
816 !memcmp(buf
.data(), testEntry
.GetData(), testEntry
.GetSize()));
818 CPPUNIT_ASSERT_MESSAGE("IsDir" + error_context
, entry
->IsDir());
821 // GetSize() must return the right result in all cases after all the
822 // data has been read
823 CPPUNIT_ASSERT_MESSAGE("entry size check" + error_context
,
824 testEntry
.GetLength() == entry
->GetSize());
825 CPPUNIT_ASSERT_MESSAGE(
826 "arc->GetLength() == entry->GetSize()" + error_context
,
827 arc
->GetLength() == entry
->GetSize());
829 if ((m_options
& PipeIn
) == 0) {
830 OnEntryExtracted(*entry
, testEntry
, arc
.get());
832 m_testEntries
.erase(it
);
834 entries
.push_back(entry
);
838 // check that the end of the input archive was reached without error
839 CPPUNIT_ASSERT(arc
->Eof());
841 // for non-seekable streams these data are only guaranteed to be
842 // available once the end of the archive has been reached
843 if (m_options
& PipeIn
) {
844 for (EntryIter i
= entries
.begin(); i
!= entries
.end(); ++i
) {
845 wxString name
= (*i
)->GetName(wxPATH_UNIX
);
846 TestEntries::iterator j
= m_testEntries
.find(name
);
847 OnEntryExtracted(**i
, *j
->second
);
849 m_testEntries
.erase(j
);
851 OnArchiveExtracted(*arc
, expectedTotal
);
855 // Extract an archive using an external unarchive program
857 template <class ClassFactoryT
>
858 void ArchiveTestCase
<ClassFactoryT
>::ExtractArchive(wxInputStream
& in
,
859 const wxString
& unarchiver
)
861 // for an external unarchiver, unarchive to a tempdir
864 if ((m_options
& PipeIn
) == 0) {
865 wxFileName
fn(tmpdir
.GetName());
866 fn
.SetExt(_T("arc"));
867 wxString tmparc
= fn
.GetPath(wxPATH_GET_SEPARATOR
) + fn
.GetFullName();
869 if (m_options
& Stub
)
870 in
.SeekI(STUB_SIZE
* 2);
872 // write the archive to a temporary file
874 wxFFileOutputStream
out(tmparc
);
880 system(wxString::Format(unarchiver
, tmparc
.c_str()).mb_str());
881 wxRemoveFile(tmparc
);
884 // for the non-seekable test, have the archiver extract "-" and
885 // feed it the archive via a pipe
886 PFileOutputStream
out(wxString::Format(unarchiver
, _T("-")));
891 wxString dir
= tmpdir
.GetName();
895 // Verifies the files produced by an external unarchiver are as expected
897 template <class ClassFactoryT
>
898 void ArchiveTestCase
<ClassFactoryT
>::VerifyDir(wxString
& path
,
899 size_t rootlen
/*=0*/)
902 path
+= wxFileName::GetPathSeparator();
903 int pos
= path
.length();
909 if (dir
.Open(path
) && dir
.GetFirst(&name
)) {
911 path
.replace(pos
, wxString::npos
, name
);
912 name
= m_factory
->GetInternalName(
913 path
.substr(rootlen
, wxString::npos
));
915 bool isDir
= wxDirExists(path
);
919 // provide some context for the error message so that we know which
920 // iteration of the loop we were on
921 string
error_entry((_T(" '") + name
+ _T("'")).mb_str());
922 string
error_context(" failed for entry" + error_entry
);
924 TestEntries::iterator it
= m_testEntries
.find(name
);
925 CPPUNIT_ASSERT_MESSAGE(
926 "archive contains an entry that shouldn't be there"
928 it
!= m_testEntries
.end());
930 const TestEntry
& testEntry
= *it
->second
;
933 CPPUNIT_ASSERT_MESSAGE("timestamp check" + error_context
,
934 testEntry
.GetDateTime() ==
935 wxFileName(path
).GetModificationTime());
938 wxFFileInputStream
in(path
);
939 CPPUNIT_ASSERT_MESSAGE(
940 "entry not found in archive" + error_entry
, in
.Ok());
942 size_t size
= (size_t)in
.GetLength();
943 wxCharBuffer
buf(size
);
944 CPPUNIT_ASSERT_MESSAGE("Read" + error_context
,
945 in
.Read(buf
.data(), size
).LastRead() == size
);
946 CPPUNIT_ASSERT_MESSAGE("size check" + error_context
,
947 testEntry
.GetSize() == size
);
948 CPPUNIT_ASSERT_MESSAGE("data compare" + error_context
,
949 memcmp(buf
.data(), testEntry
.GetData(), size
) == 0);
952 VerifyDir(path
, rootlen
);
956 m_testEntries
.erase(it
);
958 while (dir
.GetNext(&name
));
962 // test the simple iterators that give away ownership of an entry
964 template <class ClassFactoryT
>
965 void ArchiveTestCase
<ClassFactoryT
>::TestIterator(wxInputStream
& in
)
967 typedef std::list
<EntryT
*> ArchiveCatalog
;
968 typedef typename
ArchiveCatalog::iterator CatalogIter
;
970 auto_ptr
<InputStreamT
> arc(m_factory
->NewStream(in
));
973 #ifdef WXARC_MEMBER_TEMPLATES
974 ArchiveCatalog
cat((IterT
)*arc
, IterT());
977 for (IterT
i(*arc
); i
!= IterT(); ++i
)
981 for (CatalogIter it
= cat
.begin(); it
!= cat
.end(); ++it
) {
982 auto_ptr
<EntryT
> entry(*it
);
983 count
+= m_testEntries
.count(entry
->GetName(wxPATH_UNIX
));
986 CPPUNIT_ASSERT(m_testEntries
.size() == cat
.size());
987 CPPUNIT_ASSERT(count
== cat
.size());
990 // test the pair iterators that can be used to load a std::map or wxHashMap
991 // these also give away ownership of entries
993 template <class ClassFactoryT
>
994 void ArchiveTestCase
<ClassFactoryT
>::TestPairIterator(wxInputStream
& in
)
996 typedef std::map
<wxString
, EntryT
*> ArchiveCatalog
;
997 typedef typename
ArchiveCatalog::iterator CatalogIter
;
999 auto_ptr
<InputStreamT
> arc(m_factory
->NewStream(in
));
1002 #ifdef WXARC_MEMBER_TEMPLATES
1003 ArchiveCatalog
cat((PairIterT
)*arc
, PairIterT());
1006 for (PairIterT
i(*arc
); i
!= PairIterT(); ++i
)
1010 for (CatalogIter it
= cat
.begin(); it
!= cat
.end(); ++it
) {
1011 auto_ptr
<EntryT
> entry(it
->second
);
1012 count
+= m_testEntries
.count(entry
->GetName(wxPATH_UNIX
));
1015 CPPUNIT_ASSERT(m_testEntries
.size() == cat
.size());
1016 CPPUNIT_ASSERT(count
== cat
.size());
1019 // simple iterators using smart pointers, no need to worry about ownership
1021 template <class ClassFactoryT
>
1022 void ArchiveTestCase
<ClassFactoryT
>::TestSmartIterator(wxInputStream
& in
)
1024 typedef std::list
<Ptr
<EntryT
> > ArchiveCatalog
;
1025 typedef typename
ArchiveCatalog::iterator CatalogIter
;
1026 typedef wxArchiveIterator
<InputStreamT
, Ptr
<EntryT
> > Iter
;
1028 auto_ptr
<InputStreamT
> arc(m_factory
->NewStream(in
));
1030 #ifdef WXARC_MEMBER_TEMPLATES
1031 ArchiveCatalog
cat((Iter
)*arc
, Iter());
1034 for (Iter
i(*arc
); i
!= Iter(); ++i
)
1038 CPPUNIT_ASSERT(m_testEntries
.size() == cat
.size());
1040 for (CatalogIter it
= cat
.begin(); it
!= cat
.end(); ++it
)
1041 CPPUNIT_ASSERT(m_testEntries
.count((*it
)->GetName(wxPATH_UNIX
)));
1044 // pair iterator using smart pointers
1046 template <class ClassFactoryT
>
1047 void ArchiveTestCase
<ClassFactoryT
>::TestSmartPairIterator(wxInputStream
& in
)
1049 #if defined _MSC_VER && defined _MSC_VER < 1200
1050 // With VC++ 5.0 the '=' operator of std::pair breaks when the second
1051 // type is Ptr<EntryT>, so this iterator can't be made to work.
1054 typedef std::map
<wxString
, Ptr
<EntryT
> > ArchiveCatalog
;
1055 typedef typename
ArchiveCatalog::iterator CatalogIter
;
1056 typedef wxArchiveIterator
<InputStreamT
,
1057 std::pair
<wxString
, Ptr
<EntryT
> > > PairIter
;
1059 auto_ptr
<InputStreamT
> arc(m_factory
->NewStream(in
));
1061 #ifdef WXARC_MEMBER_TEMPLATES
1062 ArchiveCatalog
cat((PairIter
)*arc
, PairIter());
1065 for (PairIter
i(*arc
); i
!= PairIter(); ++i
)
1069 CPPUNIT_ASSERT(m_testEntries
.size() == cat
.size());
1071 for (CatalogIter it
= cat
.begin(); it
!= cat
.end(); ++it
)
1072 CPPUNIT_ASSERT(m_testEntries
.count(it
->second
->GetName(wxPATH_UNIX
)));
1076 // try reading two entries at the same time
1078 template <class ClassFactoryT
>
1079 void ArchiveTestCase
<ClassFactoryT
>::ReadSimultaneous(TestInputStream
& in
)
1081 typedef std::map
<wxString
, Ptr
<EntryT
> > ArchiveCatalog
;
1082 typedef wxArchiveIterator
<InputStreamT
,
1083 std::pair
<wxString
, Ptr
<EntryT
> > > PairIter
;
1085 // create two archive input streams
1086 TestInputStream
in2(in
);
1087 auto_ptr
<InputStreamT
> arc(m_factory
->NewStream(in
));
1088 auto_ptr
<InputStreamT
> arc2(m_factory
->NewStream(in2
));
1091 #ifdef WXARC_MEMBER_TEMPLATES
1092 ArchiveCatalog
cat((PairIter
)*arc
, PairIter());
1095 for (PairIter
i(*arc
); i
!= PairIter(); ++i
)
1099 // the names of two entries to read
1100 const wxChar
*name
= _T("text/small");
1101 const wxChar
*name2
= _T("bin/bin1000");
1104 typename
ArchiveCatalog::iterator j
;
1105 CPPUNIT_ASSERT((j
= cat
.find(name
)) != cat
.end());
1106 CPPUNIT_ASSERT(arc
->OpenEntry(*j
->second
));
1107 CPPUNIT_ASSERT((j
= cat
.find(name2
)) != cat
.end());
1108 CPPUNIT_ASSERT(arc2
->OpenEntry(*j
->second
));
1110 // get pointers to the expected data
1111 TestEntries::iterator k
;
1112 CPPUNIT_ASSERT((k
= m_testEntries
.find(name
)) != m_testEntries
.end());
1113 TestEntry
*entry
= k
->second
;
1114 CPPUNIT_ASSERT((k
= m_testEntries
.find(name2
)) != m_testEntries
.end());
1115 TestEntry
*entry2
= k
->second
;
1117 size_t count
= 0, count2
= 0;
1118 size_t size
= entry
->GetSize(), size2
= entry2
->GetSize();
1119 const char *data
= entry
->GetData(), *data2
= entry2
->GetData();
1121 // read and check the two entries in parallel, character by character
1122 while (arc
->IsOk() || arc2
->IsOk()) {
1123 char ch
= arc
->GetC();
1124 if (arc
->LastRead() == 1) {
1125 CPPUNIT_ASSERT(count
< size
);
1126 CPPUNIT_ASSERT(ch
== data
[count
++]);
1128 char ch2
= arc2
->GetC();
1129 if (arc2
->LastRead() == 1) {
1130 CPPUNIT_ASSERT(count2
< size2
);
1131 CPPUNIT_ASSERT(ch2
== data2
[count2
++]);
1135 CPPUNIT_ASSERT(arc
->Eof());
1136 CPPUNIT_ASSERT(arc2
->Eof());
1137 CPPUNIT_ASSERT(count
== size
);
1138 CPPUNIT_ASSERT(count2
== size2
);
1141 // Nothing useful can be done with a generic notifier yet, so just test one
1144 template <class NotifierT
, class EntryT
>
1145 class ArchiveNotifier
: public NotifierT
1148 void OnEntryUpdated(EntryT
& WXUNUSED(entry
)) { }
1151 template <class ClassFactoryT
>
1152 void ArchiveTestCase
<ClassFactoryT
>::OnSetNotifier(EntryT
& entry
)
1154 static ArchiveNotifier
<NotifierT
, EntryT
> notifier
;
1155 entry
.SetNotifier(notifier
);
1159 ///////////////////////////////////////////////////////////////////////////////
1162 ArchiveTestSuite::ArchiveTestSuite(string name
)
1163 : CppUnit::TestSuite("archive/" + name
),
1165 m_name(name
.c_str(), *wxConvCurrent
)
1167 m_name
= _T("wx") + m_name
.Left(1).Upper() + m_name
.Mid(1).Lower();
1168 m_path
.AddEnvList(_T("PATH"));
1169 m_archivers
.push_back(_T(""));
1170 m_unarchivers
.push_back(_T(""));
1173 // add the command for an external archiver to the list, testing for it in
1176 void ArchiveTestSuite::AddCmd(wxArrayString
& cmdlist
, const wxString
& cmd
)
1179 cmdlist
.push_back(cmd
);
1182 bool ArchiveTestSuite::IsInPath(const wxString
& cmd
)
1184 wxString c
= cmd
.BeforeFirst(_T(' '));
1188 return !m_path
.FindValidPath(c
).empty();
1191 // make the test suite
1193 ArchiveTestSuite
*ArchiveTestSuite::makeSuite()
1195 typedef wxArrayString::iterator Iter
;
1197 for (int generic
= 0; generic
< 2; generic
++)
1198 for (Iter i
= m_unarchivers
.begin(); i
!= m_unarchivers
.end(); ++i
)
1199 for (Iter j
= m_archivers
.begin(); j
!= m_archivers
.end(); ++j
)
1200 for (int options
= 0; options
<= AllOptions
; options
++)
1202 #ifdef WXARC_NO_POPEN
1203 // if no popen then can't pipe in/out of archiver
1204 if ((options
& PipeIn
) && !i
->empty())
1206 if ((options
& PipeOut
) && !j
->empty())
1209 string descr
= Description(m_name
, options
,
1210 generic
!= 0, *j
, *i
);
1212 CppUnit::Test
*test
= makeTest(descr
, m_id
, options
,
1213 generic
!= 0, *j
, *i
);
1224 CppUnit::Test
*ArchiveTestSuite::makeTest(
1225 string
WXUNUSED(descr
),
1227 int WXUNUSED(options
),
1228 bool WXUNUSED(genericInterface
),
1229 const wxString
& WXUNUSED(archiver
),
1230 const wxString
& WXUNUSED(unarchiver
))
1235 // make a display string for the option bits
1237 string
ArchiveTestSuite::Description(const wxString
& type
,
1239 bool genericInterface
,
1240 const wxString
& archiver
,
1241 const wxString
& unarchiver
)
1244 descr
<< m_id
<< _T(" ");
1246 if (genericInterface
)
1247 descr
<< _T("wxArchive (") << type
<< _T(")");
1251 if (!archiver
.empty()) {
1252 const wxChar
*fn
= (options
& PipeOut
) != 0 ? _T("-") : _T("file");
1253 descr
<< _T(" (") << wxString::Format(archiver
, fn
) << _T(")");
1255 if (!unarchiver
.empty()) {
1256 const wxChar
*fn
= (options
& PipeIn
) != 0 ? _T("-") : _T("file");
1257 descr
<< _T(" (") << wxString::Format(unarchiver
, fn
) << _T(")");
1262 if ((options
& PipeIn
) != 0)
1263 optstr
+= _T("|PipeIn");
1264 if ((options
& PipeOut
) != 0)
1265 optstr
+= _T("|PipeOut");
1266 if ((options
& Stub
) != 0)
1267 optstr
+= _T("|Stub");
1268 if (!optstr
.empty())
1269 optstr
= _T(" (") + optstr
.substr(1) + _T(")");
1273 return (const char*)descr
.mb_str();
1277 ///////////////////////////////////////////////////////////////////////////////
1280 template class ArchiveTestCase
<wxArchiveClassFactory
>;
1283 #include "wx/zipstrm.h"
1284 template class ArchiveTestCase
<wxZipClassFactory
>;
1287 #endif // wxUSE_STREAMS && wxUSE_ARCHIVE_STREAMS