]>
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
5 // Copyright: (c) 2004 Mike Wetherell
6 // Licence: wxWindows licence
7 ///////////////////////////////////////////////////////////////////////////////
19 #if wxUSE_STREAMS && wxUSE_ARCHIVE_STREAMS
21 // VC++ 6 warns that the list iterator's '->' operator will not work whenever
22 // std::list is used with a non-pointer, so switch it off.
23 #if defined _MSC_VER && _MSC_VER < 1300
24 #pragma warning (disable:4284)
27 #include "archivetest.h"
38 // Check whether member templates can be used
40 #if defined __GNUC__ && \
41 (__GNUC__ >= 3 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95))
42 # define WXARC_MEMBER_TEMPLATES
44 #if defined _MSC_VER && _MSC_VER >= 1310 && !defined __WIN64__
45 # define WXARC_MEMBER_TEMPLATES
47 #if defined __BORLANDC__ && __BORLANDC__ >= 0x530
48 # define WXARC_MEMBER_TEMPLATES
50 #if defined __DMC__ && __DMC__ >= 0x832
51 # define WXARC_MEMBER_TEMPLATES
53 #if defined __HP_aCC && __HP_aCC > 33300
54 # define WXARC_MEMBER_TEMPLATES
56 #if defined __SUNPRO_CC && __SUNPRO_CC > 0x500
57 # define WXARC_MEMBER_TEMPLATES
61 ///////////////////////////////////////////////////////////////////////////////
62 // A class to hold a test entry
64 TestEntry::TestEntry(const wxDateTime
& dt
, int len
, const char *data
)
69 m_data
= new char[len
];
70 memcpy(m_data
, data
, len
);
72 for (int i
= 0; i
< len
&& m_isText
; i
++)
73 m_isText
= (signed char)m_data
[i
] > 0;
77 ///////////////////////////////////////////////////////////////////////////////
78 // TestOutputStream and TestInputStream are memory streams which can be
79 // seekable or non-seekable.
81 const size_t STUB_SIZE
= 2048;
82 const size_t INITIAL_SIZE
= 0x18000;
83 const wxFileOffset SEEK_LIMIT
= 0x100000;
85 TestOutputStream::TestOutputStream(int options
)
91 void TestOutputStream::Init()
98 if (m_options
& Stub
) {
99 wxCharBuffer
buf(STUB_SIZE
);
100 memset(buf
.data(), 0, STUB_SIZE
);
101 Write(buf
, STUB_SIZE
);
105 wxFileOffset
TestOutputStream::OnSysSeek(wxFileOffset pos
, wxSeekMode mode
)
107 if ((m_options
& PipeOut
) == 0) {
109 case wxFromStart
: break;
110 case wxFromCurrent
: pos
+= m_pos
; break;
111 case wxFromEnd
: pos
+= m_size
; break;
113 if (pos
< 0 || pos
> SEEK_LIMIT
)
114 return wxInvalidOffset
;
118 return wxInvalidOffset
;
121 wxFileOffset
TestOutputStream::OnSysTell() const
123 return (m_options
& PipeOut
) == 0 ? (wxFileOffset
)m_pos
: wxInvalidOffset
;
126 size_t TestOutputStream::OnSysWrite(const void *buffer
, size_t size
)
128 if (!IsOk() || !size
)
130 m_lasterror
= wxSTREAM_WRITE_ERROR
;
132 size_t newsize
= m_pos
+ size
;
133 wxCHECK(newsize
> m_pos
, 0);
135 if (m_capacity
< newsize
) {
136 size_t capacity
= m_capacity
? m_capacity
: INITIAL_SIZE
;
138 while (capacity
< newsize
) {
140 wxCHECK(capacity
> m_capacity
, 0);
143 char *buf
= new char[capacity
];
145 memcpy(buf
, m_data
, m_capacity
);
148 m_capacity
= capacity
;
151 memcpy(m_data
+ m_pos
, buffer
, size
);
155 m_lasterror
= wxSTREAM_NO_ERROR
;
160 void TestOutputStream::GetData(char*& data
, size_t& size
)
165 if (m_options
& Stub
) {
169 if (size
> m_capacity
) {
171 memcpy(d
+ STUB_SIZE
, m_data
, m_size
);
175 memmove(d
+ STUB_SIZE
, d
, m_size
);
178 memset(d
, 0, STUB_SIZE
);
187 ///////////////////////////////////////////////////////////////////////////////
188 // TestOutputStream and TestInputStream are memory streams which can be
189 // seekable or non-seekable.
191 TestInputStream::TestInputStream(const TestInputStream
& in
)
193 m_options(in
.m_options
),
196 m_eoftype(in
.m_eoftype
)
198 m_data
= new char[m_size
];
199 memcpy(m_data
, in
.m_data
, m_size
);
202 void TestInputStream::Rewind()
204 if ((m_options
& Stub
) && (m_options
& PipeIn
))
205 m_pos
= STUB_SIZE
* 2;
219 void TestInputStream::SetData(TestOutputStream
& out
)
222 m_options
= out
.GetOptions();
223 out
.GetData(m_data
, m_size
);
227 wxFileOffset
TestInputStream::OnSysSeek(wxFileOffset pos
, wxSeekMode mode
)
229 if ((m_options
& PipeIn
) == 0) {
231 case wxFromStart
: break;
232 case wxFromCurrent
: pos
+= m_pos
; break;
233 case wxFromEnd
: pos
+= m_size
; break;
235 if (pos
< 0 || pos
> SEEK_LIMIT
)
236 return wxInvalidOffset
;
240 return wxInvalidOffset
;
243 wxFileOffset
TestInputStream::OnSysTell() const
245 return (m_options
& PipeIn
) == 0 ? (wxFileOffset
)m_pos
: wxInvalidOffset
;
248 size_t TestInputStream::OnSysRead(void *buffer
, size_t size
)
250 if (!IsOk() || !size
)
257 else if (m_size
- m_pos
< size
)
258 count
= m_size
- m_pos
;
263 memcpy(buffer
, m_data
+ m_pos
, count
);
267 if (((m_eoftype
& AtLast
) != 0 && m_pos
>= m_size
) || count
< size
)
269 if ((m_eoftype
& WithError
) != 0)
270 m_lasterror
= wxSTREAM_READ_ERROR
;
272 m_lasterror
= wxSTREAM_EOF
;
279 ///////////////////////////////////////////////////////////////////////////////
280 // minimal non-intrusive reference counting pointer for testing the iterators
282 template <class T
> class Ptr
285 explicit Ptr(T
* p
= NULL
) : m_p(p
), m_count(new int) { *m_count
= 1; }
286 Ptr(const Ptr
& sp
) : m_p(sp
.m_p
), m_count(sp
.m_count
) { ++*m_count
; }
289 Ptr
& operator =(const Ptr
& sp
) {
293 m_count
= sp
.m_count
;
299 T
* get() const { return m_p
; }
300 T
* operator->() const { return m_p
; }
301 T
& operator*() const { return *m_p
; }
305 if (--*m_count
== 0) {
316 ///////////////////////////////////////////////////////////////////////////////
317 // Clean-up for temp directory
324 wxString
GetName() const { return m_tmp
; }
327 void RemoveDir(wxString
& path
);
334 wxString tmp
= wxFileName::CreateTempFileName(wxT("arctest-"));
337 m_original
= wxGetCwd();
338 CPPUNIT_ASSERT(wxMkdir(tmp
, 0700));
340 CPPUNIT_ASSERT(wxSetWorkingDirectory(tmp
));
346 if (!m_tmp
.empty()) {
347 wxSetWorkingDirectory(m_original
);
352 void TempDir::RemoveDir(wxString
& path
)
354 wxCHECK_RET(!m_tmp
.empty() && path
.substr(0, m_tmp
.length()) == m_tmp
,
355 wxT("remove '") + path
+ wxT("' fails safety check"));
357 const wxChar
*files
[] = {
366 wxT("zero/zero1024"),
367 wxT("zero/zero32768"),
368 wxT("zero/zero16385"),
373 const wxChar
*dirs
[] = {
374 wxT("text/"), wxT("bin/"), wxT("zero/"), wxT("empty/")
377 wxString tmp
= m_tmp
+ wxFileName::GetPathSeparator();
380 for (i
= 0; i
< WXSIZEOF(files
); i
++)
381 wxRemoveFile(tmp
+ wxFileName(files
[i
], wxPATH_UNIX
).GetFullPath());
383 for (i
= 0; i
< WXSIZEOF(dirs
); i
++)
384 wxRmdir(tmp
+ wxFileName(dirs
[i
], wxPATH_UNIX
).GetFullPath());
388 wxLogSysError(wxT("can't remove temporary dir '%s'"), m_tmp
.c_str());
393 ///////////////////////////////////////////////////////////////////////////////
394 // wxFFile streams for piping to/from an external program
396 #if defined __UNIX__ || defined __MINGW32__
397 # define WXARC_popen popen
398 # define WXARC_pclose pclose
399 #elif defined _MSC_VER || defined __BORLANDC__
400 # define WXARC_popen _popen
401 # define WXARC_pclose _pclose
403 # define WXARC_NO_POPEN
404 # define WXARC_popen(cmd, type) NULL
405 # define WXARC_pclose(fp)
414 PFileInputStream::PFileInputStream(const wxString
& cmd
)
415 : wxFFileInputStream(WXARC_popen(cmd
.mb_str(), "r" WXARC_b
))
419 PFileInputStream::~PFileInputStream()
421 WXARC_pclose(m_file
->fp()); m_file
->Detach();
424 PFileOutputStream::PFileOutputStream(const wxString
& cmd
)
425 : wxFFileOutputStream(WXARC_popen(cmd
.mb_str(), "w" WXARC_b
))
429 PFileOutputStream::~PFileOutputStream()
431 WXARC_pclose(m_file
->fp()); m_file
->Detach();
435 ///////////////////////////////////////////////////////////////////////////////
438 template <class ClassFactoryT
>
439 ArchiveTestCase
<ClassFactoryT
>::ArchiveTestCase(
441 ClassFactoryT
*factory
,
443 const wxString
& archiver
,
444 const wxString
& unarchiver
)
446 CppUnit::TestCase(TestId::MakeId() + name
),
449 m_timeStamp(1, wxDateTime::Mar
, 2004, 12, 0),
450 m_id(TestId::GetId()),
451 m_archiver(archiver
),
452 m_unarchiver(unarchiver
)
454 wxASSERT(m_factory
.get() != NULL
);
457 template <class ClassFactoryT
>
458 ArchiveTestCase
<ClassFactoryT
>::~ArchiveTestCase()
460 TestEntries::iterator it
;
461 for (it
= m_testEntries
.begin(); it
!= m_testEntries
.end(); ++it
)
465 template <class ClassFactoryT
>
466 void ArchiveTestCase
<ClassFactoryT
>::runTest()
468 TestOutputStream
out(m_options
);
472 if (m_archiver
.empty())
475 CreateArchive(out
, m_archiver
);
477 // check archive could be created
478 CPPUNIT_ASSERT(out
.GetLength() > 0);
480 TestInputStream
in(out
, m_id
% ((m_options
& PipeIn
) ? 4 : 3));
484 TestPairIterator(in
);
486 TestSmartIterator(in
);
488 TestSmartPairIterator(in
);
491 if ((m_options
& PipeIn
) == 0) {
492 ReadSimultaneous(in
);
496 ModifyArchive(in
, out
);
499 if (m_unarchiver
.empty())
502 ExtractArchive(in
, m_unarchiver
);
504 // check that all the test entries were found in the archive
505 CPPUNIT_ASSERT(m_testEntries
.empty());
508 template <class ClassFactoryT
>
509 void ArchiveTestCase
<ClassFactoryT
>::CreateTestData()
512 Add("text/empty", "");
513 Add("text/small", "Small text file for testing\n"
514 "archive streams in wxWidgets\n");
517 Add("bin/bin1000", 1000);
518 Add("bin/bin4095", 4095);
519 Add("bin/bin4096", 4096);
520 Add("bin/bin4097", 4097);
521 Add("bin/bin16384", 16384);
524 Add("zero/zero5", 5, 0);
525 Add("zero/zero1024", 1024, 109);
526 Add("zero/zero32768", 32768, 106);
527 Add("zero/zero16385", 16385, 119);
532 template <class ClassFactoryT
>
533 TestEntry
& ArchiveTestCase
<ClassFactoryT
>::Add(const char *name
,
539 TestEntry
*& entry
= m_testEntries
[wxString(name
, *wxConvCurrent
)];
540 wxASSERT(entry
== NULL
);
541 entry
= new TestEntry(m_timeStamp
, len
, data
);
542 m_timeStamp
+= wxTimeSpan(0, 1, 30);
546 template <class ClassFactoryT
>
547 TestEntry
& ArchiveTestCase
<ClassFactoryT
>::Add(const char *name
,
551 wxCharBuffer
buf(len
);
552 for (int i
= 0; i
< len
; i
++)
553 buf
.data()[i
] = (char)(value
== EOF
? rand() : value
);
554 return Add(name
, buf
, len
);
557 // Create an archive using the wx archive classes, write it to 'out'
559 template <class ClassFactoryT
>
560 void ArchiveTestCase
<ClassFactoryT
>::CreateArchive(wxOutputStream
& out
)
562 auto_ptr
<OutputStreamT
> arc(m_factory
->NewStream(out
));
563 TestEntries::iterator it
;
565 OnCreateArchive(*arc
);
567 // We want to try creating entries in various different ways, 'choices'
568 // is just a number used to select between all the various possibilities.
571 for (it
= m_testEntries
.begin(); it
!= m_testEntries
.end(); ++it
) {
573 TestEntry
& testEntry
= *it
->second
;
574 wxString name
= it
->first
;
576 // It should be possible to create a directory entry just by supplying
577 // a name that looks like a directory, or alternatively any old name
578 // can be identified as a directory using SetIsDir or PutNextDirEntry
579 bool setIsDir
= name
.Last() == wxT('/') && (choices
& 1);
581 name
.erase(name
.length() - 1);
583 // provide some context for the error message so that we know which
584 // iteration of the loop we were on
585 string
error_entry((wxT(" '") + name
+ wxT("'")).mb_str());
586 string
error_context(" failed for entry" + error_entry
);
588 if ((choices
& 2) || testEntry
.IsText()) {
589 // try PutNextEntry(EntryT *pEntry)
590 auto_ptr
<EntryT
> entry(m_factory
->NewEntry());
591 entry
->SetName(name
, wxPATH_UNIX
);
594 entry
->SetDateTime(testEntry
.GetDateTime());
595 entry
->SetSize(testEntry
.GetLength());
596 OnCreateEntry(*arc
, testEntry
, entry
.get());
597 CPPUNIT_ASSERT_MESSAGE("PutNextEntry" + error_context
,
598 arc
->PutNextEntry(entry
.release()));
601 // try the convenience methods
602 OnCreateEntry(*arc
, testEntry
);
604 CPPUNIT_ASSERT_MESSAGE("PutNextDirEntry" + error_context
,
605 arc
->PutNextDirEntry(name
, testEntry
.GetDateTime()));
607 CPPUNIT_ASSERT_MESSAGE("PutNextEntry" + error_context
,
608 arc
->PutNextEntry(name
, testEntry
.GetDateTime(),
609 testEntry
.GetLength()));
612 if (it
->first
.Last() != wxT('/')) {
613 // for non-dirs write the data
614 arc
->Write(testEntry
.GetData(), testEntry
.GetSize());
615 CPPUNIT_ASSERT_MESSAGE("LastWrite check" + error_context
,
616 arc
->LastWrite() == testEntry
.GetSize());
617 // should work with or without explicit CloseEntry
619 CPPUNIT_ASSERT_MESSAGE("CloseEntry" + error_context
,
623 CPPUNIT_ASSERT_MESSAGE("IsOk" + error_context
, arc
->IsOk());
626 // should work with or without explicit Close
628 CPPUNIT_ASSERT(arc
->Close());
631 // Create an archive using an external archive program
633 template <class ClassFactoryT
>
634 void ArchiveTestCase
<ClassFactoryT
>::CreateArchive(wxOutputStream
& out
,
635 const wxString
& archiver
)
637 // for an external archiver the test data need to be written to
642 TestEntries::iterator i
;
643 for (i
= m_testEntries
.begin(); i
!= m_testEntries
.end(); ++i
) {
644 wxFileName
fn(i
->first
, wxPATH_UNIX
);
645 TestEntry
& entry
= *i
->second
;
648 wxFileName::Mkdir(fn
.GetPath(), 0777, wxPATH_MKDIR_FULL
);
650 wxFileName::Mkdir(fn
.GetPath(), 0777, wxPATH_MKDIR_FULL
);
651 wxFFileOutputStream
fileout(fn
.GetFullPath());
652 fileout
.Write(entry
.GetData(), entry
.GetSize());
656 for (i
= m_testEntries
.begin(); i
!= m_testEntries
.end(); ++i
) {
657 wxFileName
fn(i
->first
, wxPATH_UNIX
);
658 TestEntry
& entry
= *i
->second
;
659 wxDateTime dt
= entry
.GetDateTime();
662 entry
.SetDateTime(wxDateTime());
665 fn
.SetTimes(NULL
, &dt
, NULL
);
668 if ((m_options
& PipeOut
) == 0) {
669 wxFileName
fn(tmpdir
.GetName());
670 fn
.SetExt(wxT("arc"));
671 wxString tmparc
= fn
.GetPath(wxPATH_GET_SEPARATOR
) + fn
.GetFullName();
673 // call the archiver to create an archive file
674 if ( system(wxString::Format(archiver
, tmparc
.c_str()).mb_str()) == -1 )
676 wxLogError("Failed to run acrhiver command \"%s\"", archiver
);
679 // then load the archive file
681 wxFFileInputStream
in(tmparc
);
686 wxRemoveFile(tmparc
);
689 // for the non-seekable test, have the archiver output to "-"
690 // and read the archive via a pipe
691 PFileInputStream
in(wxString::Format(archiver
, wxT("-")));
697 // Do a standard set of modification on an archive, delete an entry,
698 // rename an entry and add an entry
700 template <class ClassFactoryT
>
701 void ArchiveTestCase
<ClassFactoryT
>::ModifyArchive(wxInputStream
& in
,
704 auto_ptr
<InputStreamT
> arcIn(m_factory
->NewStream(in
));
705 auto_ptr
<OutputStreamT
> arcOut(m_factory
->NewStream(out
));
708 const wxString deleteName
= wxT("bin/bin1000");
709 const wxString renameFrom
= wxT("zero/zero1024");
710 const wxString renameTo
= wxT("zero/newname");
711 const wxString newName
= wxT("newfile");
712 const char *newData
= "New file added as a test\n";
714 arcOut
->CopyArchiveMetaData(*arcIn
);
716 while ((pEntry
= arcIn
->GetNextEntry()) != NULL
) {
717 auto_ptr
<EntryT
> entry(pEntry
);
718 OnSetNotifier(*entry
);
719 wxString name
= entry
->GetName(wxPATH_UNIX
);
721 // provide some context for the error message so that we know which
722 // iteration of the loop we were on
723 string
error_entry((wxT(" '") + name
+ wxT("'")).mb_str());
724 string
error_context(" failed for entry" + error_entry
);
726 if (name
== deleteName
) {
727 TestEntries::iterator it
= m_testEntries
.find(name
);
728 CPPUNIT_ASSERT_MESSAGE(
729 "deletion failed (already deleted?) for" + error_entry
,
730 it
!= m_testEntries
.end());
731 TestEntry
*p
= it
->second
;
732 m_testEntries
.erase(it
);
736 if (name
== renameFrom
) {
737 entry
->SetName(renameTo
);
738 TestEntries::iterator it
= m_testEntries
.find(renameFrom
);
739 CPPUNIT_ASSERT_MESSAGE(
740 "rename failed (already renamed?) for" + error_entry
,
741 it
!= m_testEntries
.end());
742 TestEntry
*p
= it
->second
;
743 m_testEntries
.erase(it
);
744 m_testEntries
[renameTo
] = p
;
747 CPPUNIT_ASSERT_MESSAGE("CopyEntry" + error_context
,
748 arcOut
->CopyEntry(entry
.release(), *arcIn
));
752 // check that the deletion and rename were done
753 CPPUNIT_ASSERT(m_testEntries
.count(deleteName
) == 0);
754 CPPUNIT_ASSERT(m_testEntries
.count(renameFrom
) == 0);
755 CPPUNIT_ASSERT(m_testEntries
.count(renameTo
) == 1);
757 // check that the end of the input archive was reached without error
758 CPPUNIT_ASSERT(arcIn
->Eof());
760 // try adding a new entry
761 TestEntry
& testEntry
= Add(newName
.mb_str(), newData
);
762 auto_ptr
<EntryT
> newentry(m_factory
->NewEntry());
763 newentry
->SetName(newName
);
764 newentry
->SetDateTime(testEntry
.GetDateTime());
765 newentry
->SetSize(testEntry
.GetLength());
766 OnCreateEntry(*arcOut
, testEntry
, newentry
.get());
767 OnSetNotifier(*newentry
);
768 CPPUNIT_ASSERT(arcOut
->PutNextEntry(newentry
.release()));
769 CPPUNIT_ASSERT(arcOut
->Write(newData
, strlen(newData
)).IsOk());
771 // should work with or without explicit Close
773 CPPUNIT_ASSERT(arcOut
->Close());
776 // Extract an archive using the wx archive classes
778 template <class ClassFactoryT
>
779 void ArchiveTestCase
<ClassFactoryT
>::ExtractArchive(wxInputStream
& in
)
781 typedef Ptr
<EntryT
> EntryPtr
;
782 typedef std::list
<EntryPtr
> Entries
;
783 typedef typename
Entries::iterator EntryIter
;
785 auto_ptr
<InputStreamT
> arc(m_factory
->NewStream(in
));
786 int expectedTotal
= m_testEntries
.size();
790 if ((m_options
& PipeIn
) == 0)
791 OnArchiveExtracted(*arc
, expectedTotal
);
793 while (entry
= EntryPtr(arc
->GetNextEntry()), entry
.get() != NULL
) {
794 wxString name
= entry
->GetName(wxPATH_UNIX
);
796 // provide some context for the error message so that we know which
797 // iteration of the loop we were on
798 string
error_entry((wxT(" '") + name
+ wxT("'")).mb_str());
799 string
error_context(" failed for entry" + error_entry
);
801 TestEntries::iterator it
= m_testEntries
.find(name
);
802 CPPUNIT_ASSERT_MESSAGE(
803 "archive contains an entry that shouldn't be there" + error_entry
,
804 it
!= m_testEntries
.end());
806 const TestEntry
& testEntry
= *it
->second
;
809 // On Windows some archivers compensate for Windows DST handling, but
810 // other don't, so disable the test for now.
811 wxDateTime dt
= testEntry
.GetDateTime();
813 CPPUNIT_ASSERT_MESSAGE("timestamp check" + error_context
,
814 dt
== entry
->GetDateTime());
817 // non-seekable entries are allowed to have GetSize == wxInvalidOffset
818 // until the end of the entry's data has been read past
819 CPPUNIT_ASSERT_MESSAGE("entry size check" + error_context
,
820 testEntry
.GetLength() == entry
->GetSize() ||
821 ((m_options
& PipeIn
) != 0 && entry
->GetSize() == wxInvalidOffset
));
822 CPPUNIT_ASSERT_MESSAGE(
823 "arc->GetLength() == entry->GetSize()" + error_context
,
824 arc
->GetLength() == entry
->GetSize());
826 if (name
.Last() != wxT('/'))
828 CPPUNIT_ASSERT_MESSAGE("!IsDir" + error_context
,
830 wxCharBuffer
buf(testEntry
.GetSize() + 1);
831 CPPUNIT_ASSERT_MESSAGE("Read until Eof" + error_context
,
832 arc
->Read(buf
.data(), testEntry
.GetSize() + 1).Eof());
833 CPPUNIT_ASSERT_MESSAGE("LastRead check" + error_context
,
834 arc
->LastRead() == testEntry
.GetSize());
835 CPPUNIT_ASSERT_MESSAGE("data compare" + error_context
,
836 !memcmp(buf
.data(), testEntry
.GetData(), testEntry
.GetSize()));
838 CPPUNIT_ASSERT_MESSAGE("IsDir" + error_context
, entry
->IsDir());
841 // GetSize() must return the right result in all cases after all the
842 // data has been read
843 CPPUNIT_ASSERT_MESSAGE("entry size check" + error_context
,
844 testEntry
.GetLength() == entry
->GetSize());
845 CPPUNIT_ASSERT_MESSAGE(
846 "arc->GetLength() == entry->GetSize()" + error_context
,
847 arc
->GetLength() == entry
->GetSize());
849 if ((m_options
& PipeIn
) == 0) {
850 OnEntryExtracted(*entry
, testEntry
, arc
.get());
852 m_testEntries
.erase(it
);
854 entries
.push_back(entry
);
858 // check that the end of the input archive was reached without error
859 CPPUNIT_ASSERT(arc
->Eof());
861 // for non-seekable streams these data are only guaranteed to be
862 // available once the end of the archive has been reached
863 if (m_options
& PipeIn
) {
864 for (EntryIter i
= entries
.begin(); i
!= entries
.end(); ++i
) {
865 wxString name
= (*i
)->GetName(wxPATH_UNIX
);
866 TestEntries::iterator j
= m_testEntries
.find(name
);
867 OnEntryExtracted(**i
, *j
->second
);
869 m_testEntries
.erase(j
);
871 OnArchiveExtracted(*arc
, expectedTotal
);
875 // Extract an archive using an external unarchive program
877 template <class ClassFactoryT
>
878 void ArchiveTestCase
<ClassFactoryT
>::ExtractArchive(wxInputStream
& in
,
879 const wxString
& unarchiver
)
881 // for an external unarchiver, unarchive to a tempdir
884 if ((m_options
& PipeIn
) == 0) {
885 wxFileName
fn(tmpdir
.GetName());
886 fn
.SetExt(wxT("arc"));
887 wxString tmparc
= fn
.GetPath(wxPATH_GET_SEPARATOR
) + fn
.GetFullName();
889 if (m_options
& Stub
)
890 in
.SeekI(STUB_SIZE
* 2);
892 // write the archive to a temporary file
894 wxFFileOutputStream
out(tmparc
);
900 if ( system(wxString::Format(unarchiver
, tmparc
.c_str()).mb_str()) == -1 )
902 wxLogError("Failed to run unarchiver command \"%s\"", unarchiver
);
905 wxRemoveFile(tmparc
);
908 // for the non-seekable test, have the archiver extract "-" and
909 // feed it the archive via a pipe
910 PFileOutputStream
out(wxString::Format(unarchiver
, wxT("-")));
915 wxString dir
= tmpdir
.GetName();
919 // Verifies the files produced by an external unarchiver are as expected
921 template <class ClassFactoryT
>
922 void ArchiveTestCase
<ClassFactoryT
>::VerifyDir(wxString
& path
,
923 size_t rootlen
/*=0*/)
926 path
+= wxFileName::GetPathSeparator();
927 int pos
= path
.length();
933 if (dir
.Open(path
) && dir
.GetFirst(&name
)) {
935 path
.replace(pos
, wxString::npos
, name
);
936 name
= m_factory
->GetInternalName(
937 path
.substr(rootlen
, wxString::npos
));
939 bool isDir
= wxDirExists(path
);
943 // provide some context for the error message so that we know which
944 // iteration of the loop we were on
945 string
error_entry((wxT(" '") + name
+ wxT("'")).mb_str());
946 string
error_context(" failed for entry" + error_entry
);
948 TestEntries::iterator it
= m_testEntries
.find(name
);
949 CPPUNIT_ASSERT_MESSAGE(
950 "archive contains an entry that shouldn't be there"
952 it
!= m_testEntries
.end());
954 const TestEntry
& testEntry
= *it
->second
;
956 #if 0 //ndef __WINDOWS__
957 CPPUNIT_ASSERT_MESSAGE("timestamp check" + error_context
,
958 testEntry
.GetDateTime() ==
959 wxFileName(path
).GetModificationTime());
962 wxFFileInputStream
in(path
);
963 CPPUNIT_ASSERT_MESSAGE(
964 "entry not found in archive" + error_entry
, in
.IsOk());
966 size_t size
= (size_t)in
.GetLength();
967 wxCharBuffer
buf(size
);
968 CPPUNIT_ASSERT_MESSAGE("Read" + error_context
,
969 in
.Read(buf
.data(), size
).LastRead() == size
);
970 CPPUNIT_ASSERT_MESSAGE("size check" + error_context
,
971 testEntry
.GetSize() == size
);
972 CPPUNIT_ASSERT_MESSAGE("data compare" + error_context
,
973 memcmp(buf
.data(), testEntry
.GetData(), size
) == 0);
976 VerifyDir(path
, rootlen
);
980 m_testEntries
.erase(it
);
982 while (dir
.GetNext(&name
));
986 // test the simple iterators that give away ownership of an entry
988 template <class ClassFactoryT
>
989 void ArchiveTestCase
<ClassFactoryT
>::TestIterator(wxInputStream
& in
)
991 typedef std::list
<EntryT
*> ArchiveCatalog
;
992 typedef typename
ArchiveCatalog::iterator CatalogIter
;
994 auto_ptr
<InputStreamT
> arc(m_factory
->NewStream(in
));
997 #ifdef WXARC_MEMBER_TEMPLATES
998 ArchiveCatalog
cat((IterT
)*arc
, IterT());
1001 for (IterT
i(*arc
); i
!= IterT(); ++i
)
1005 for (CatalogIter it
= cat
.begin(); it
!= cat
.end(); ++it
) {
1006 auto_ptr
<EntryT
> entry(*it
);
1007 count
+= m_testEntries
.count(entry
->GetName(wxPATH_UNIX
));
1010 CPPUNIT_ASSERT(m_testEntries
.size() == cat
.size());
1011 CPPUNIT_ASSERT(count
== cat
.size());
1014 // test the pair iterators that can be used to load a std::map or wxHashMap
1015 // these also give away ownership of entries
1017 template <class ClassFactoryT
>
1018 void ArchiveTestCase
<ClassFactoryT
>::TestPairIterator(wxInputStream
& in
)
1020 typedef std::map
<wxString
, EntryT
*> ArchiveCatalog
;
1021 typedef typename
ArchiveCatalog::iterator CatalogIter
;
1023 auto_ptr
<InputStreamT
> arc(m_factory
->NewStream(in
));
1026 #ifdef WXARC_MEMBER_TEMPLATES
1027 ArchiveCatalog
cat((PairIterT
)*arc
, PairIterT());
1030 for (PairIterT
i(*arc
); i
!= PairIterT(); ++i
)
1034 for (CatalogIter it
= cat
.begin(); it
!= cat
.end(); ++it
) {
1035 auto_ptr
<EntryT
> entry(it
->second
);
1036 count
+= m_testEntries
.count(entry
->GetName(wxPATH_UNIX
));
1039 CPPUNIT_ASSERT(m_testEntries
.size() == cat
.size());
1040 CPPUNIT_ASSERT(count
== cat
.size());
1043 // simple iterators using smart pointers, no need to worry about ownership
1045 template <class ClassFactoryT
>
1046 void ArchiveTestCase
<ClassFactoryT
>::TestSmartIterator(wxInputStream
& in
)
1048 typedef std::list
<Ptr
<EntryT
> > ArchiveCatalog
;
1049 typedef typename
ArchiveCatalog::iterator CatalogIter
;
1050 typedef wxArchiveIterator
<InputStreamT
, Ptr
<EntryT
> > Iter
;
1052 auto_ptr
<InputStreamT
> arc(m_factory
->NewStream(in
));
1054 #ifdef WXARC_MEMBER_TEMPLATES
1055 ArchiveCatalog
cat((Iter
)*arc
, Iter());
1058 for (Iter
i(*arc
); i
!= Iter(); ++i
)
1062 CPPUNIT_ASSERT(m_testEntries
.size() == cat
.size());
1064 for (CatalogIter it
= cat
.begin(); it
!= cat
.end(); ++it
)
1065 CPPUNIT_ASSERT(m_testEntries
.count((*it
)->GetName(wxPATH_UNIX
)));
1068 // pair iterator using smart pointers
1070 template <class ClassFactoryT
>
1071 void ArchiveTestCase
<ClassFactoryT
>::TestSmartPairIterator(wxInputStream
& in
)
1073 #if defined _MSC_VER && defined _MSC_VER < 1200
1074 // With VC++ 5.0 the '=' operator of std::pair breaks when the second
1075 // type is Ptr<EntryT>, so this iterator can't be made to work.
1078 typedef std::map
<wxString
, Ptr
<EntryT
> > ArchiveCatalog
;
1079 typedef typename
ArchiveCatalog::iterator CatalogIter
;
1080 typedef wxArchiveIterator
<InputStreamT
,
1081 std::pair
<wxString
, Ptr
<EntryT
> > > PairIter
;
1083 auto_ptr
<InputStreamT
> arc(m_factory
->NewStream(in
));
1085 #ifdef WXARC_MEMBER_TEMPLATES
1086 ArchiveCatalog
cat((PairIter
)*arc
, PairIter());
1089 for (PairIter
i(*arc
); i
!= PairIter(); ++i
)
1093 CPPUNIT_ASSERT(m_testEntries
.size() == cat
.size());
1095 for (CatalogIter it
= cat
.begin(); it
!= cat
.end(); ++it
)
1096 CPPUNIT_ASSERT(m_testEntries
.count(it
->second
->GetName(wxPATH_UNIX
)));
1100 // try reading two entries at the same time
1102 template <class ClassFactoryT
>
1103 void ArchiveTestCase
<ClassFactoryT
>::ReadSimultaneous(TestInputStream
& in
)
1105 typedef std::map
<wxString
, Ptr
<EntryT
> > ArchiveCatalog
;
1106 typedef wxArchiveIterator
<InputStreamT
,
1107 std::pair
<wxString
, Ptr
<EntryT
> > > PairIter
;
1109 // create two archive input streams
1110 TestInputStream
in2(in
);
1111 auto_ptr
<InputStreamT
> arc(m_factory
->NewStream(in
));
1112 auto_ptr
<InputStreamT
> arc2(m_factory
->NewStream(in2
));
1115 #ifdef WXARC_MEMBER_TEMPLATES
1116 ArchiveCatalog
cat((PairIter
)*arc
, PairIter());
1119 for (PairIter
i(*arc
); i
!= PairIter(); ++i
)
1123 // the names of two entries to read
1124 const wxChar
*name
= wxT("text/small");
1125 const wxChar
*name2
= wxT("bin/bin1000");
1128 typename
ArchiveCatalog::iterator j
;
1129 CPPUNIT_ASSERT((j
= cat
.find(name
)) != cat
.end());
1130 CPPUNIT_ASSERT(arc
->OpenEntry(*j
->second
));
1131 CPPUNIT_ASSERT((j
= cat
.find(name2
)) != cat
.end());
1132 CPPUNIT_ASSERT(arc2
->OpenEntry(*j
->second
));
1134 // get pointers to the expected data
1135 TestEntries::iterator k
;
1136 CPPUNIT_ASSERT((k
= m_testEntries
.find(name
)) != m_testEntries
.end());
1137 TestEntry
*entry
= k
->second
;
1138 CPPUNIT_ASSERT((k
= m_testEntries
.find(name2
)) != m_testEntries
.end());
1139 TestEntry
*entry2
= k
->second
;
1141 size_t count
= 0, count2
= 0;
1142 size_t size
= entry
->GetSize(), size2
= entry2
->GetSize();
1143 const char *data
= entry
->GetData(), *data2
= entry2
->GetData();
1145 // read and check the two entries in parallel, character by character
1146 while (arc
->IsOk() || arc2
->IsOk()) {
1147 char ch
= arc
->GetC();
1148 if (arc
->LastRead() == 1) {
1149 CPPUNIT_ASSERT(count
< size
);
1150 CPPUNIT_ASSERT(ch
== data
[count
++]);
1152 char ch2
= arc2
->GetC();
1153 if (arc2
->LastRead() == 1) {
1154 CPPUNIT_ASSERT(count2
< size2
);
1155 CPPUNIT_ASSERT(ch2
== data2
[count2
++]);
1159 CPPUNIT_ASSERT(arc
->Eof());
1160 CPPUNIT_ASSERT(arc2
->Eof());
1161 CPPUNIT_ASSERT(count
== size
);
1162 CPPUNIT_ASSERT(count2
== size2
);
1165 // Nothing useful can be done with a generic notifier yet, so just test one
1168 template <class NotifierT
, class EntryT
>
1169 class ArchiveNotifier
: public NotifierT
1172 void OnEntryUpdated(EntryT
& WXUNUSED(entry
)) { }
1175 template <class ClassFactoryT
>
1176 void ArchiveTestCase
<ClassFactoryT
>::OnSetNotifier(EntryT
& entry
)
1178 static ArchiveNotifier
<NotifierT
, EntryT
> notifier
;
1179 entry
.SetNotifier(notifier
);
1183 ///////////////////////////////////////////////////////////////////////////////
1184 // An additional case to check that reading corrupt archives doesn't crash
1186 class CorruptionTestCase
: public CppUnit::TestCase
1189 CorruptionTestCase(std::string name
,
1190 wxArchiveClassFactory
*factory
,
1192 : CppUnit::TestCase(TestId::MakeId() + name
),
1198 // the entry point for the test
1201 void CreateArchive(wxOutputStream
& out
);
1202 void ExtractArchive(wxInputStream
& in
);
1204 auto_ptr
<wxArchiveClassFactory
> m_factory
; // factory to make classes
1205 int m_options
; // test options
1208 void CorruptionTestCase::runTest()
1210 TestOutputStream
out(m_options
);
1212 TestInputStream
in(out
, 0);
1213 wxFileOffset len
= in
.GetLength();
1215 // try flipping one byte in the archive
1217 for (pos
= 0; pos
< len
; pos
++) {
1225 // try zeroing one byte in the archive
1226 for (pos
= 0; pos
< len
; pos
++) {
1234 // try chopping the archive off
1235 for (int size
= 1; size
<= len
; size
++) {
1242 void CorruptionTestCase::CreateArchive(wxOutputStream
& out
)
1244 auto_ptr
<wxArchiveOutputStream
> arc(m_factory
->NewStream(out
));
1246 arc
->PutNextDirEntry(wxT("dir"));
1247 arc
->PutNextEntry(wxT("file"));
1248 arc
->Write(wxT("foo"), 3);
1251 void CorruptionTestCase::ExtractArchive(wxInputStream
& in
)
1253 auto_ptr
<wxArchiveInputStream
> arc(m_factory
->NewStream(in
));
1254 auto_ptr
<wxArchiveEntry
> entry(arc
->GetNextEntry());
1256 while (entry
.get() != NULL
) {
1260 arc
->Read(buf
, sizeof(buf
));
1262 auto_ptr
<wxArchiveEntry
> next(arc
->GetNextEntry());
1268 ///////////////////////////////////////////////////////////////////////////////
1271 int TestId::m_seed
= 6219;
1274 string
TestId::MakeId()
1276 m_seed
= (m_seed
* 171) % 30269;
1277 return string(wxString::Format(wxT("%-6d"), m_seed
).mb_str());
1281 ///////////////////////////////////////////////////////////////////////////////
1284 ArchiveTestSuite::ArchiveTestSuite(string name
)
1285 : CppUnit::TestSuite("archive/" + name
),
1286 m_name(name
.c_str(), *wxConvCurrent
)
1288 m_name
= wxT("wx") + m_name
.Left(1).Upper() + m_name
.Mid(1).Lower();
1289 m_path
.AddEnvList(wxT("PATH"));
1290 m_archivers
.push_back(wxT(""));
1291 m_unarchivers
.push_back(wxT(""));
1294 // add the command for an external archiver to the list, testing for it in
1297 void ArchiveTestSuite::AddCmd(wxArrayString
& cmdlist
, const wxString
& cmd
)
1300 cmdlist
.push_back(cmd
);
1303 bool ArchiveTestSuite::IsInPath(const wxString
& cmd
)
1305 wxString c
= cmd
.BeforeFirst(wxT(' '));
1309 return !m_path
.FindValidPath(c
).empty();
1312 // make the test suite
1314 ArchiveTestSuite
*ArchiveTestSuite::makeSuite()
1316 typedef wxArrayString::iterator Iter
;
1318 for (int generic
= 0; generic
< 2; generic
++)
1319 for (Iter i
= m_unarchivers
.begin(); i
!= m_unarchivers
.end(); ++i
)
1320 for (Iter j
= m_archivers
.begin(); j
!= m_archivers
.end(); ++j
)
1321 for (int options
= 0; options
<= AllOptions
; options
++)
1323 #ifdef WXARC_NO_POPEN
1324 // if no popen then can't pipe in/out of archiver
1325 if ((options
& PipeIn
) && !i
->empty())
1327 if ((options
& PipeOut
) && !j
->empty())
1330 string descr
= Description(m_name
, options
,
1331 generic
!= 0, *j
, *i
);
1333 CppUnit::Test
*test
= makeTest(descr
, options
,
1334 generic
!= 0, *j
, *i
);
1340 for (int options
= 0; options
<= PipeIn
; options
+= PipeIn
)
1342 wxObject
*pObj
= wxCreateDynamicObject(m_name
+ wxT("ClassFactory"));
1343 wxArchiveClassFactory
*factory
;
1344 factory
= wxDynamicCast(pObj
, wxArchiveClassFactory
);
1347 string
descr(m_name
.mb_str());
1348 descr
= "CorruptionTestCase (" + descr
+ ")";
1351 descr
+= " (PipeIn)";
1353 addTest(new CorruptionTestCase(descr
, factory
, options
));
1360 CppUnit::Test
*ArchiveTestSuite::makeTest(
1361 string
WXUNUSED(descr
),
1362 int WXUNUSED(options
),
1363 bool WXUNUSED(genericInterface
),
1364 const wxString
& WXUNUSED(archiver
),
1365 const wxString
& WXUNUSED(unarchiver
))
1370 // make a display string for the option bits
1372 string
ArchiveTestSuite::Description(const wxString
& type
,
1374 bool genericInterface
,
1375 const wxString
& archiver
,
1376 const wxString
& unarchiver
)
1380 if (genericInterface
)
1381 descr
<< wxT("wxArchive (") << type
<< wxT(")");
1385 if (!archiver
.empty()) {
1386 const wxChar
*fn
= (options
& PipeOut
) != 0 ? wxT("-") : wxT("file");
1387 const wxString cmd
= archiver
.Contains("%s")
1388 ? wxString::Format(archiver
, fn
)
1390 descr
<< wxT(" (") << cmd
<< wxT(")");
1392 if (!unarchiver
.empty()) {
1393 const wxChar
*fn
= (options
& PipeIn
) != 0 ? wxT("-") : wxT("file");
1394 const wxString cmd
= unarchiver
.Contains("%s")
1395 ? wxString::Format(unarchiver
, fn
)
1397 descr
<< wxT(" (") << cmd
<< wxT(")");
1402 if ((options
& PipeIn
) != 0)
1403 optstr
+= wxT("|PipeIn");
1404 if ((options
& PipeOut
) != 0)
1405 optstr
+= wxT("|PipeOut");
1406 if ((options
& Stub
) != 0)
1407 optstr
+= wxT("|Stub");
1408 if (!optstr
.empty())
1409 optstr
= wxT(" (") + optstr
.substr(1) + wxT(")");
1413 return string(descr
.mb_str());
1417 ///////////////////////////////////////////////////////////////////////////////
1420 template class ArchiveTestCase
<wxArchiveClassFactory
>;
1423 #include "wx/zipstrm.h"
1424 template class ArchiveTestCase
<wxZipClassFactory
>;
1428 #include "wx/tarstrm.h"
1429 template class ArchiveTestCase
<wxTarClassFactory
>;
1432 #endif // wxUSE_STREAMS && wxUSE_ARCHIVE_STREAMS