da48b9e45d
* fixing compilation of tests that cannot build bracket code with the corresponding wxUSE… macros * adding directive for iOS * adding a switch for skipping the run - not the build of tests right now I don’t know yet, how to run the test binary in the iOS simulator, but building it is still a good test in itself * adding skipping of tests * increasing minimum deployment to get proper c++17 support * using --disable-sys-libs, restoring other targets even when the zlib in -isysroot is used, due to deployment on lower iOS versions inflateValidate may not be available and crash. The guards are evaluated using macros from the zlib from the SDK, not from the lowest version supported.
1440 lines
42 KiB
C++
1440 lines
42 KiB
C++
///////////////////////////////////////////////////////////////////////////////
|
|
// Name: tests/archive/archive.cpp
|
|
// Purpose: Test the archive classes
|
|
// Author: Mike Wetherell
|
|
// Copyright: (c) 2004 Mike Wetherell
|
|
// Licence: wxWindows licence
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "testprec.h"
|
|
|
|
#ifdef __BORLANDC__
|
|
# pragma hdrstop
|
|
#endif
|
|
|
|
#ifndef WX_PRECOMP
|
|
# include "wx/wx.h"
|
|
#endif
|
|
|
|
#if wxUSE_STREAMS && wxUSE_ARCHIVE_STREAMS
|
|
|
|
#include "archivetest.h"
|
|
#include "wx/dir.h"
|
|
#include <string>
|
|
#include <list>
|
|
#include <map>
|
|
#include <sys/stat.h>
|
|
|
|
using std::string;
|
|
|
|
|
|
// Check whether member templates can be used
|
|
//
|
|
#if defined __GNUC__ && \
|
|
(__GNUC__ >= 3 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95))
|
|
# define WXARC_MEMBER_TEMPLATES
|
|
#endif
|
|
#if defined _MSC_VER && _MSC_VER >= 1310 && !defined __WIN64__
|
|
# define WXARC_MEMBER_TEMPLATES
|
|
#endif
|
|
#if defined __BORLANDC__ && __BORLANDC__ >= 0x530
|
|
# define WXARC_MEMBER_TEMPLATES
|
|
#endif
|
|
#if defined __HP_aCC && __HP_aCC > 33300
|
|
# define WXARC_MEMBER_TEMPLATES
|
|
#endif
|
|
#if defined __SUNPRO_CC && __SUNPRO_CC > 0x500
|
|
# define WXARC_MEMBER_TEMPLATES
|
|
#endif
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// A class to hold a test entry
|
|
|
|
TestEntry::TestEntry(const wxDateTime& dt, int len, const char *data)
|
|
: m_dt(dt),
|
|
m_len(len),
|
|
m_isText(len > 0)
|
|
{
|
|
m_data = new char[len];
|
|
memcpy(m_data, data, len);
|
|
|
|
for (int i = 0; i < len && m_isText; i++)
|
|
m_isText = (signed char)m_data[i] > 0;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// TestOutputStream and TestInputStream are memory streams which can be
|
|
// seekable or non-seekable.
|
|
|
|
const size_t STUB_SIZE = 2048;
|
|
const size_t INITIAL_SIZE = 0x18000;
|
|
const wxFileOffset SEEK_LIMIT = 0x100000;
|
|
|
|
TestOutputStream::TestOutputStream(int options)
|
|
: m_options(options)
|
|
{
|
|
Init();
|
|
}
|
|
|
|
void TestOutputStream::Init()
|
|
{
|
|
m_data = NULL;
|
|
m_size = 0;
|
|
m_capacity = 0;
|
|
m_pos = 0;
|
|
|
|
if (m_options & Stub) {
|
|
wxCharBuffer buf(STUB_SIZE);
|
|
memset(buf.data(), 0, STUB_SIZE);
|
|
Write(buf, STUB_SIZE);
|
|
}
|
|
}
|
|
|
|
wxFileOffset TestOutputStream::OnSysSeek(wxFileOffset pos, wxSeekMode mode)
|
|
{
|
|
if ((m_options & PipeOut) == 0) {
|
|
switch (mode) {
|
|
case wxFromStart: break;
|
|
case wxFromCurrent: pos += m_pos; break;
|
|
case wxFromEnd: pos += m_size; break;
|
|
}
|
|
if (pos < 0 || pos > SEEK_LIMIT)
|
|
return wxInvalidOffset;
|
|
m_pos = (size_t)pos;
|
|
return m_pos;
|
|
}
|
|
return wxInvalidOffset;
|
|
}
|
|
|
|
wxFileOffset TestOutputStream::OnSysTell() const
|
|
{
|
|
return (m_options & PipeOut) == 0 ? (wxFileOffset)m_pos : wxInvalidOffset;
|
|
}
|
|
|
|
size_t TestOutputStream::OnSysWrite(const void *buffer, size_t size)
|
|
{
|
|
if (!IsOk() || !size)
|
|
return 0;
|
|
m_lasterror = wxSTREAM_WRITE_ERROR;
|
|
|
|
size_t newsize = m_pos + size;
|
|
wxCHECK(newsize > m_pos, 0);
|
|
|
|
if (m_capacity < newsize) {
|
|
size_t capacity = m_capacity ? m_capacity : INITIAL_SIZE;
|
|
|
|
while (capacity < newsize) {
|
|
capacity <<= 1;
|
|
wxCHECK(capacity > m_capacity, 0);
|
|
}
|
|
|
|
char *buf = new char[capacity];
|
|
if (m_data)
|
|
memcpy(buf, m_data, m_capacity);
|
|
delete [] m_data;
|
|
m_data = buf;
|
|
m_capacity = capacity;
|
|
}
|
|
|
|
memcpy(m_data + m_pos, buffer, size);
|
|
m_pos += size;
|
|
if (m_pos > m_size)
|
|
m_size = m_pos;
|
|
m_lasterror = wxSTREAM_NO_ERROR;
|
|
|
|
return size;
|
|
}
|
|
|
|
void TestOutputStream::GetData(char*& data, size_t& size)
|
|
{
|
|
data = m_data;
|
|
size = m_size;
|
|
|
|
if (m_options & Stub) {
|
|
char *d = m_data;
|
|
size += STUB_SIZE;
|
|
|
|
if (size > m_capacity) {
|
|
d = new char[size];
|
|
memcpy(d + STUB_SIZE, m_data, m_size);
|
|
delete [] m_data;
|
|
}
|
|
else {
|
|
memmove(d + STUB_SIZE, d, m_size);
|
|
}
|
|
|
|
memset(d, 0, STUB_SIZE);
|
|
data = d;
|
|
}
|
|
|
|
Init();
|
|
Reset();
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// TestOutputStream and TestInputStream are memory streams which can be
|
|
// seekable or non-seekable.
|
|
|
|
TestInputStream::TestInputStream(const TestInputStream& in)
|
|
: wxInputStream(),
|
|
m_options(in.m_options),
|
|
m_pos(in.m_pos),
|
|
m_size(in.m_size),
|
|
m_eoftype(in.m_eoftype)
|
|
{
|
|
m_data = new char[m_size];
|
|
memcpy(m_data, in.m_data, m_size);
|
|
}
|
|
|
|
void TestInputStream::Rewind()
|
|
{
|
|
if ((m_options & Stub) && (m_options & PipeIn))
|
|
m_pos = STUB_SIZE * 2;
|
|
else
|
|
m_pos = 0;
|
|
|
|
if (m_wbacksize) {
|
|
free(m_wback);
|
|
m_wback = NULL;
|
|
m_wbacksize = 0;
|
|
m_wbackcur = 0;
|
|
}
|
|
|
|
Reset();
|
|
}
|
|
|
|
void TestInputStream::SetData(TestOutputStream& out)
|
|
{
|
|
delete [] m_data;
|
|
m_options = out.GetOptions();
|
|
out.GetData(m_data, m_size);
|
|
Rewind();
|
|
}
|
|
|
|
wxFileOffset TestInputStream::OnSysSeek(wxFileOffset pos, wxSeekMode mode)
|
|
{
|
|
if ((m_options & PipeIn) == 0) {
|
|
switch (mode) {
|
|
case wxFromStart: break;
|
|
case wxFromCurrent: pos += m_pos; break;
|
|
case wxFromEnd: pos += m_size; break;
|
|
}
|
|
if (pos < 0 || pos > SEEK_LIMIT)
|
|
return wxInvalidOffset;
|
|
m_pos = (size_t)pos;
|
|
return m_pos;
|
|
}
|
|
return wxInvalidOffset;
|
|
}
|
|
|
|
wxFileOffset TestInputStream::OnSysTell() const
|
|
{
|
|
return (m_options & PipeIn) == 0 ? (wxFileOffset)m_pos : wxInvalidOffset;
|
|
}
|
|
|
|
size_t TestInputStream::OnSysRead(void *buffer, size_t size)
|
|
{
|
|
if (!IsOk() || !size)
|
|
return 0;
|
|
|
|
size_t count;
|
|
|
|
if (m_pos >= m_size)
|
|
count = 0;
|
|
else if (m_size - m_pos < size)
|
|
count = m_size - m_pos;
|
|
else
|
|
count = size;
|
|
|
|
if (count) {
|
|
memcpy(buffer, m_data + m_pos, count);
|
|
m_pos += count;
|
|
}
|
|
|
|
if (((m_eoftype & AtLast) != 0 && m_pos >= m_size) || count < size)
|
|
{
|
|
if ((m_eoftype & WithError) != 0)
|
|
m_lasterror = wxSTREAM_READ_ERROR;
|
|
else
|
|
m_lasterror = wxSTREAM_EOF;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// minimal non-intrusive reference counting pointer for testing the iterators
|
|
|
|
template <class T> class Ptr
|
|
{
|
|
public:
|
|
explicit Ptr(T* p = NULL) : m_p(p), m_count(new int) { *m_count = 1; }
|
|
Ptr(const Ptr& sp) : m_p(sp.m_p), m_count(sp.m_count) { ++*m_count; }
|
|
~Ptr() { Free(); }
|
|
|
|
Ptr& operator =(const Ptr& sp) {
|
|
if (&sp != this) {
|
|
Free();
|
|
m_p = sp.m_p;
|
|
m_count = sp.m_count;
|
|
++*m_count;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
T* get() const { return m_p; }
|
|
T* operator->() const { return m_p; }
|
|
T& operator*() const { return *m_p; }
|
|
|
|
private:
|
|
void Free() {
|
|
if (--*m_count == 0) {
|
|
delete m_p;
|
|
delete m_count;
|
|
}
|
|
}
|
|
|
|
T *m_p;
|
|
int *m_count;
|
|
};
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Clean-up for temp directory
|
|
|
|
class TempDir
|
|
{
|
|
public:
|
|
TempDir();
|
|
~TempDir();
|
|
wxString GetName() const { return m_tmp; }
|
|
|
|
private:
|
|
void RemoveDir(wxString& path);
|
|
wxString m_tmp;
|
|
wxString m_original;
|
|
};
|
|
|
|
TempDir::TempDir()
|
|
{
|
|
wxString tmp = wxFileName::CreateTempFileName(wxT("arctest-"));
|
|
if (!tmp.empty()) {
|
|
wxRemoveFile(tmp);
|
|
m_original = wxGetCwd();
|
|
CPPUNIT_ASSERT(wxMkdir(tmp, 0700));
|
|
m_tmp = tmp;
|
|
CPPUNIT_ASSERT(wxSetWorkingDirectory(tmp));
|
|
}
|
|
}
|
|
|
|
TempDir::~TempDir()
|
|
{
|
|
if (!m_tmp.empty()) {
|
|
wxSetWorkingDirectory(m_original);
|
|
RemoveDir(m_tmp);
|
|
}
|
|
}
|
|
|
|
void TempDir::RemoveDir(wxString& path)
|
|
{
|
|
wxCHECK_RET(!m_tmp.empty() && path.substr(0, m_tmp.length()) == m_tmp,
|
|
wxT("remove '") + path + wxT("' fails safety check"));
|
|
|
|
const wxChar *files[] = {
|
|
wxT("text/empty"),
|
|
wxT("text/small"),
|
|
wxT("bin/bin1000"),
|
|
wxT("bin/bin4095"),
|
|
wxT("bin/bin4096"),
|
|
wxT("bin/bin4097"),
|
|
wxT("bin/bin16384"),
|
|
wxT("zero/zero5"),
|
|
wxT("zero/zero1024"),
|
|
wxT("zero/zero32768"),
|
|
wxT("zero/zero16385"),
|
|
wxT("zero/newname"),
|
|
wxT("newfile"),
|
|
};
|
|
|
|
const wxChar *dirs[] = {
|
|
wxT("text/"), wxT("bin/"), wxT("zero/"), wxT("empty/")
|
|
};
|
|
|
|
wxString tmp = m_tmp + wxFileName::GetPathSeparator();
|
|
size_t i;
|
|
|
|
for (i = 0; i < WXSIZEOF(files); i++)
|
|
wxRemoveFile(tmp + wxFileName(files[i], wxPATH_UNIX).GetFullPath());
|
|
|
|
for (i = 0; i < WXSIZEOF(dirs); i++)
|
|
wxRmdir(tmp + wxFileName(dirs[i], wxPATH_UNIX).GetFullPath());
|
|
|
|
if (!wxRmdir(m_tmp))
|
|
{
|
|
wxLogSysError(wxT("can't remove temporary dir '%s'"), m_tmp.c_str());
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// wxFFile streams for piping to/from an external program
|
|
|
|
#if defined __UNIX__ || defined __MINGW32__
|
|
# define WXARC_popen popen
|
|
# define WXARC_pclose pclose
|
|
#elif defined _MSC_VER || defined __BORLANDC__
|
|
# define WXARC_popen _popen
|
|
# define WXARC_pclose _pclose
|
|
#else
|
|
# define WXARC_NO_POPEN
|
|
# define WXARC_popen(cmd, type) NULL
|
|
# define WXARC_pclose(fp)
|
|
#endif
|
|
|
|
#ifdef __WINDOWS__
|
|
# define WXARC_b "b"
|
|
#else
|
|
# define WXARC_b
|
|
#endif
|
|
|
|
PFileInputStream::PFileInputStream(const wxString& cmd)
|
|
: wxFFileInputStream(WXARC_popen(cmd.mb_str(), "r" WXARC_b))
|
|
{
|
|
}
|
|
|
|
PFileInputStream::~PFileInputStream()
|
|
{
|
|
WXARC_pclose(m_file->fp()); m_file->Detach();
|
|
}
|
|
|
|
PFileOutputStream::PFileOutputStream(const wxString& cmd)
|
|
: wxFFileOutputStream(WXARC_popen(cmd.mb_str(), "w" WXARC_b))
|
|
{
|
|
}
|
|
|
|
PFileOutputStream::~PFileOutputStream()
|
|
{
|
|
WXARC_pclose(m_file->fp()); m_file->Detach();
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// The test case
|
|
|
|
template <class ClassFactoryT>
|
|
ArchiveTestCase<ClassFactoryT>::ArchiveTestCase(
|
|
string name,
|
|
ClassFactoryT *factory,
|
|
int options,
|
|
const wxString& archiver,
|
|
const wxString& unarchiver)
|
|
:
|
|
CppUnit::TestCase(TestId::MakeId() + name),
|
|
m_factory(factory),
|
|
m_options(options),
|
|
m_timeStamp(1, wxDateTime::Mar, 2004, 12, 0),
|
|
m_id(TestId::GetId()),
|
|
m_archiver(archiver),
|
|
m_unarchiver(unarchiver)
|
|
{
|
|
wxASSERT(m_factory.get() != NULL);
|
|
}
|
|
|
|
template <class ClassFactoryT>
|
|
ArchiveTestCase<ClassFactoryT>::~ArchiveTestCase()
|
|
{
|
|
TestEntries::iterator it;
|
|
for (it = m_testEntries.begin(); it != m_testEntries.end(); ++it)
|
|
delete it->second;
|
|
}
|
|
|
|
template <class ClassFactoryT>
|
|
void ArchiveTestCase<ClassFactoryT>::runTest()
|
|
{
|
|
TestOutputStream out(m_options);
|
|
|
|
CreateTestData();
|
|
|
|
if (m_archiver.empty())
|
|
CreateArchive(out);
|
|
else
|
|
{
|
|
#ifndef __WXOSX_IPHONE__
|
|
CreateArchive(out, m_archiver);
|
|
#else
|
|
CPPUNIT_FAIL("using external archivers is not supported on iOS");
|
|
#endif
|
|
}
|
|
|
|
// check archive could be created
|
|
CPPUNIT_ASSERT(out.GetLength() > 0);
|
|
|
|
TestInputStream in(out, m_id % ((m_options & PipeIn) ? 4 : 3));
|
|
|
|
TestIterator(in);
|
|
in.Rewind();
|
|
TestPairIterator(in);
|
|
in.Rewind();
|
|
TestSmartIterator(in);
|
|
in.Rewind();
|
|
TestSmartPairIterator(in);
|
|
in.Rewind();
|
|
|
|
if ((m_options & PipeIn) == 0) {
|
|
ReadSimultaneous(in);
|
|
in.Rewind();
|
|
}
|
|
|
|
ModifyArchive(in, out);
|
|
in.SetData(out);
|
|
|
|
if (m_unarchiver.empty())
|
|
ExtractArchive(in);
|
|
else
|
|
{
|
|
#ifndef __WXOSX_IPHONE__
|
|
ExtractArchive(in, m_unarchiver);
|
|
#else
|
|
CPPUNIT_FAIL("using external archivers is not supported on iOS");
|
|
#endif
|
|
}
|
|
|
|
// check that all the test entries were found in the archive
|
|
CPPUNIT_ASSERT(m_testEntries.empty());
|
|
}
|
|
|
|
template <class ClassFactoryT>
|
|
void ArchiveTestCase<ClassFactoryT>::CreateTestData()
|
|
{
|
|
Add("text/");
|
|
Add("text/empty", "");
|
|
Add("text/small", "Small text file for testing\n"
|
|
"archive streams in wxWidgets\n");
|
|
|
|
Add("bin/");
|
|
Add("bin/bin1000", 1000);
|
|
Add("bin/bin4095", 4095);
|
|
Add("bin/bin4096", 4096);
|
|
Add("bin/bin4097", 4097);
|
|
Add("bin/bin16384", 16384);
|
|
|
|
Add("zero/");
|
|
Add("zero/zero5", 5, 0);
|
|
Add("zero/zero1024", 1024, 109);
|
|
Add("zero/zero32768", 32768, 106);
|
|
Add("zero/zero16385", 16385, 119);
|
|
|
|
Add("empty/");
|
|
}
|
|
|
|
template <class ClassFactoryT>
|
|
TestEntry& ArchiveTestCase<ClassFactoryT>::Add(const char *name,
|
|
const char *data,
|
|
int len /*=-1*/)
|
|
{
|
|
if (len == -1)
|
|
len = strlen(data);
|
|
TestEntry*& entry = m_testEntries[wxString(name, *wxConvCurrent)];
|
|
wxASSERT(entry == NULL);
|
|
entry = new TestEntry(m_timeStamp, len, data);
|
|
m_timeStamp += wxTimeSpan(0, 1, 30);
|
|
return *entry;
|
|
}
|
|
|
|
template <class ClassFactoryT>
|
|
TestEntry& ArchiveTestCase<ClassFactoryT>::Add(const char *name,
|
|
int len /*=0*/,
|
|
int value /*=EOF*/)
|
|
{
|
|
wxCharBuffer buf(len);
|
|
for (int i = 0; i < len; i++)
|
|
buf.data()[i] = (char)(value == EOF ? rand() : value);
|
|
return Add(name, buf, len);
|
|
}
|
|
|
|
// Create an archive using the wx archive classes, write it to 'out'
|
|
//
|
|
template <class ClassFactoryT>
|
|
void ArchiveTestCase<ClassFactoryT>::CreateArchive(wxOutputStream& out)
|
|
{
|
|
wxScopedPtr<OutputStreamT> arc(m_factory->NewStream(out));
|
|
TestEntries::iterator it;
|
|
|
|
OnCreateArchive(*arc);
|
|
|
|
// We want to try creating entries in various different ways, 'choices'
|
|
// is just a number used to select between all the various possibilities.
|
|
int choices = m_id;
|
|
|
|
for (it = m_testEntries.begin(); it != m_testEntries.end(); ++it) {
|
|
choices += 5;
|
|
TestEntry& testEntry = *it->second;
|
|
wxString name = it->first;
|
|
|
|
// It should be possible to create a directory entry just by supplying
|
|
// a name that looks like a directory, or alternatively any old name
|
|
// can be identified as a directory using SetIsDir or PutNextDirEntry
|
|
bool setIsDir = name.Last() == wxT('/') && (choices & 1);
|
|
if (setIsDir)
|
|
name.erase(name.length() - 1);
|
|
|
|
// provide some context for the error message so that we know which
|
|
// iteration of the loop we were on
|
|
string error_entry((wxT(" '") + name + wxT("'")).mb_str());
|
|
string error_context(" failed for entry" + error_entry);
|
|
|
|
if ((choices & 2) || testEntry.IsText()) {
|
|
// try PutNextEntry(EntryT *pEntry)
|
|
wxScopedPtr<EntryT> entry(m_factory->NewEntry());
|
|
entry->SetName(name, wxPATH_UNIX);
|
|
if (setIsDir)
|
|
entry->SetIsDir();
|
|
entry->SetDateTime(testEntry.GetDateTime());
|
|
entry->SetSize(testEntry.GetLength());
|
|
OnCreateEntry(*arc, testEntry, entry.get());
|
|
CPPUNIT_ASSERT_MESSAGE("PutNextEntry" + error_context,
|
|
arc->PutNextEntry(entry.release()));
|
|
}
|
|
else {
|
|
// try the convenience methods
|
|
OnCreateEntry(*arc, testEntry);
|
|
if (setIsDir)
|
|
CPPUNIT_ASSERT_MESSAGE("PutNextDirEntry" + error_context,
|
|
arc->PutNextDirEntry(name, testEntry.GetDateTime()));
|
|
else
|
|
CPPUNIT_ASSERT_MESSAGE("PutNextEntry" + error_context,
|
|
arc->PutNextEntry(name, testEntry.GetDateTime(),
|
|
testEntry.GetLength()));
|
|
}
|
|
|
|
if (it->first.Last() != wxT('/')) {
|
|
// for non-dirs write the data
|
|
arc->Write(testEntry.GetData(), testEntry.GetSize());
|
|
CPPUNIT_ASSERT_MESSAGE("LastWrite check" + error_context,
|
|
arc->LastWrite() == testEntry.GetSize());
|
|
// should work with or without explicit CloseEntry
|
|
if (choices & 3)
|
|
CPPUNIT_ASSERT_MESSAGE("CloseEntry" + error_context,
|
|
arc->CloseEntry());
|
|
}
|
|
|
|
CPPUNIT_ASSERT_MESSAGE("IsOk" + error_context, arc->IsOk());
|
|
}
|
|
|
|
// should work with or without explicit Close
|
|
if (m_id % 2)
|
|
CPPUNIT_ASSERT(arc->Close());
|
|
}
|
|
|
|
// Create an archive using an external archive program
|
|
//
|
|
#ifndef __WXOSX_IPHONE__
|
|
template <class ClassFactoryT>
|
|
void ArchiveTestCase<ClassFactoryT>::CreateArchive(wxOutputStream& out,
|
|
const wxString& archiver)
|
|
{
|
|
// for an external archiver the test data need to be written to
|
|
// temp files
|
|
TempDir tmpdir;
|
|
|
|
// write the files
|
|
TestEntries::iterator i;
|
|
for (i = m_testEntries.begin(); i != m_testEntries.end(); ++i) {
|
|
wxFileName fn(i->first, wxPATH_UNIX);
|
|
TestEntry& entry = *i->second;
|
|
|
|
if (fn.IsDir()) {
|
|
wxFileName::Mkdir(fn.GetPath(), 0777, wxPATH_MKDIR_FULL);
|
|
} else {
|
|
wxFileName::Mkdir(fn.GetPath(), 0777, wxPATH_MKDIR_FULL);
|
|
wxFFileOutputStream fileout(fn.GetFullPath());
|
|
fileout.Write(entry.GetData(), entry.GetSize());
|
|
}
|
|
}
|
|
|
|
for (i = m_testEntries.begin(); i != m_testEntries.end(); ++i) {
|
|
wxFileName fn(i->first, wxPATH_UNIX);
|
|
TestEntry& entry = *i->second;
|
|
wxDateTime dt = entry.GetDateTime();
|
|
#ifdef __WINDOWS__
|
|
if (fn.IsDir())
|
|
entry.SetDateTime(wxDateTime());
|
|
else
|
|
#endif
|
|
fn.SetTimes(NULL, &dt, NULL);
|
|
}
|
|
|
|
if ((m_options & PipeOut) == 0) {
|
|
wxFileName fn(tmpdir.GetName());
|
|
fn.SetExt(wxT("arc"));
|
|
wxString tmparc = fn.GetPath(wxPATH_GET_SEPARATOR) + fn.GetFullName();
|
|
|
|
// call the archiver to create an archive file
|
|
if ( system(wxString::Format(archiver, tmparc.c_str()).mb_str()) == -1 )
|
|
{
|
|
wxLogError("Failed to run acrhiver command \"%s\"", archiver);
|
|
}
|
|
|
|
// then load the archive file
|
|
{
|
|
wxFFileInputStream in(tmparc);
|
|
if (in.IsOk())
|
|
out.Write(in);
|
|
}
|
|
|
|
wxRemoveFile(tmparc);
|
|
}
|
|
else {
|
|
// for the non-seekable test, have the archiver output to "-"
|
|
// and read the archive via a pipe
|
|
PFileInputStream in(wxString::Format(archiver, wxT("-")));
|
|
if (in.IsOk())
|
|
out.Write(in);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Do a standard set of modification on an archive, delete an entry,
|
|
// rename an entry and add an entry
|
|
//
|
|
template <class ClassFactoryT>
|
|
void ArchiveTestCase<ClassFactoryT>::ModifyArchive(wxInputStream& in,
|
|
wxOutputStream& out)
|
|
{
|
|
wxScopedPtr<InputStreamT> arcIn(m_factory->NewStream(in));
|
|
wxScopedPtr<OutputStreamT> arcOut(m_factory->NewStream(out));
|
|
EntryT *pEntry;
|
|
|
|
const wxString deleteName = wxT("bin/bin1000");
|
|
const wxString renameFrom = wxT("zero/zero1024");
|
|
const wxString renameTo = wxT("zero/newname");
|
|
const wxString newName = wxT("newfile");
|
|
const char *newData = "New file added as a test\n";
|
|
|
|
arcOut->CopyArchiveMetaData(*arcIn);
|
|
|
|
while ((pEntry = arcIn->GetNextEntry()) != NULL) {
|
|
wxScopedPtr<EntryT> entry(pEntry);
|
|
OnSetNotifier(*entry);
|
|
wxString name = entry->GetName(wxPATH_UNIX);
|
|
|
|
// provide some context for the error message so that we know which
|
|
// iteration of the loop we were on
|
|
string error_entry((wxT(" '") + name + wxT("'")).mb_str());
|
|
string error_context(" failed for entry" + error_entry);
|
|
|
|
if (name == deleteName) {
|
|
TestEntries::iterator it = m_testEntries.find(name);
|
|
CPPUNIT_ASSERT_MESSAGE(
|
|
"deletion failed (already deleted?) for" + error_entry,
|
|
it != m_testEntries.end());
|
|
TestEntry *p = it->second;
|
|
m_testEntries.erase(it);
|
|
delete p;
|
|
}
|
|
else {
|
|
if (name == renameFrom) {
|
|
entry->SetName(renameTo);
|
|
TestEntries::iterator it = m_testEntries.find(renameFrom);
|
|
CPPUNIT_ASSERT_MESSAGE(
|
|
"rename failed (already renamed?) for" + error_entry,
|
|
it != m_testEntries.end());
|
|
TestEntry *p = it->second;
|
|
m_testEntries.erase(it);
|
|
m_testEntries[renameTo] = p;
|
|
}
|
|
|
|
CPPUNIT_ASSERT_MESSAGE("CopyEntry" + error_context,
|
|
arcOut->CopyEntry(entry.release(), *arcIn));
|
|
}
|
|
}
|
|
|
|
// check that the deletion and rename were done
|
|
CPPUNIT_ASSERT(m_testEntries.count(deleteName) == 0);
|
|
CPPUNIT_ASSERT(m_testEntries.count(renameFrom) == 0);
|
|
CPPUNIT_ASSERT(m_testEntries.count(renameTo) == 1);
|
|
|
|
// check that the end of the input archive was reached without error
|
|
CPPUNIT_ASSERT(arcIn->Eof());
|
|
|
|
// try adding a new entry
|
|
TestEntry& testEntry = Add(newName.mb_str(), newData);
|
|
wxScopedPtr<EntryT> newentry(m_factory->NewEntry());
|
|
newentry->SetName(newName);
|
|
newentry->SetDateTime(testEntry.GetDateTime());
|
|
newentry->SetSize(testEntry.GetLength());
|
|
OnCreateEntry(*arcOut, testEntry, newentry.get());
|
|
OnSetNotifier(*newentry);
|
|
CPPUNIT_ASSERT(arcOut->PutNextEntry(newentry.release()));
|
|
CPPUNIT_ASSERT(arcOut->Write(newData, strlen(newData)).IsOk());
|
|
|
|
// should work with or without explicit Close
|
|
if (m_id % 2)
|
|
CPPUNIT_ASSERT(arcOut->Close());
|
|
}
|
|
|
|
// Extract an archive using the wx archive classes
|
|
//
|
|
template <class ClassFactoryT>
|
|
void ArchiveTestCase<ClassFactoryT>::ExtractArchive(wxInputStream& in)
|
|
{
|
|
typedef Ptr<EntryT> EntryPtr;
|
|
typedef std::list<EntryPtr> Entries;
|
|
typedef typename Entries::iterator EntryIter;
|
|
|
|
wxScopedPtr<InputStreamT> arc(m_factory->NewStream(in));
|
|
int expectedTotal = m_testEntries.size();
|
|
EntryPtr entry;
|
|
Entries entries;
|
|
|
|
if ((m_options & PipeIn) == 0)
|
|
OnArchiveExtracted(*arc, expectedTotal);
|
|
|
|
while (entry = EntryPtr(arc->GetNextEntry()), entry.get() != NULL) {
|
|
wxString name = entry->GetName(wxPATH_UNIX);
|
|
|
|
// provide some context for the error message so that we know which
|
|
// iteration of the loop we were on
|
|
string error_entry((wxT(" '") + name + wxT("'")).mb_str());
|
|
string error_context(" failed for entry" + error_entry);
|
|
|
|
TestEntries::iterator it = m_testEntries.find(name);
|
|
CPPUNIT_ASSERT_MESSAGE(
|
|
"archive contains an entry that shouldn't be there" + error_entry,
|
|
it != m_testEntries.end());
|
|
|
|
const TestEntry& testEntry = *it->second;
|
|
|
|
#ifndef __WINDOWS__
|
|
// On Windows some archivers compensate for Windows DST handling, but
|
|
// other don't, so disable the test for now.
|
|
wxDateTime dt = testEntry.GetDateTime();
|
|
if (dt.IsValid())
|
|
CPPUNIT_ASSERT_MESSAGE("timestamp check" + error_context,
|
|
dt == entry->GetDateTime());
|
|
#endif
|
|
|
|
// non-seekable entries are allowed to have GetSize == wxInvalidOffset
|
|
// until the end of the entry's data has been read past
|
|
CPPUNIT_ASSERT_MESSAGE("entry size check" + error_context,
|
|
(testEntry.GetLength() == entry->GetSize() ||
|
|
((m_options & PipeIn) != 0 && entry->GetSize() == wxInvalidOffset)));
|
|
CPPUNIT_ASSERT_MESSAGE(
|
|
"arc->GetLength() == entry->GetSize()" + error_context,
|
|
arc->GetLength() == entry->GetSize());
|
|
|
|
if (name.Last() != wxT('/'))
|
|
{
|
|
CPPUNIT_ASSERT_MESSAGE("!IsDir" + error_context,
|
|
!entry->IsDir());
|
|
wxCharBuffer buf(testEntry.GetSize() + 1);
|
|
CPPUNIT_ASSERT_MESSAGE("Read until Eof" + error_context,
|
|
arc->Read(buf.data(), testEntry.GetSize() + 1).Eof());
|
|
CPPUNIT_ASSERT_MESSAGE("LastRead check" + error_context,
|
|
arc->LastRead() == testEntry.GetSize());
|
|
CPPUNIT_ASSERT_MESSAGE("data compare" + error_context,
|
|
!memcmp(buf.data(), testEntry.GetData(), testEntry.GetSize()));
|
|
} else {
|
|
CPPUNIT_ASSERT_MESSAGE("IsDir" + error_context, entry->IsDir());
|
|
}
|
|
|
|
// GetSize() must return the right result in all cases after all the
|
|
// data has been read
|
|
CPPUNIT_ASSERT_MESSAGE("entry size check" + error_context,
|
|
testEntry.GetLength() == entry->GetSize());
|
|
CPPUNIT_ASSERT_MESSAGE(
|
|
"arc->GetLength() == entry->GetSize()" + error_context,
|
|
arc->GetLength() == entry->GetSize());
|
|
|
|
if ((m_options & PipeIn) == 0) {
|
|
OnEntryExtracted(*entry, testEntry, arc.get());
|
|
delete it->second;
|
|
m_testEntries.erase(it);
|
|
} else {
|
|
entries.push_back(entry);
|
|
}
|
|
}
|
|
|
|
// check that the end of the input archive was reached without error
|
|
CPPUNIT_ASSERT(arc->Eof());
|
|
|
|
// for non-seekable streams these data are only guaranteed to be
|
|
// available once the end of the archive has been reached
|
|
if (m_options & PipeIn) {
|
|
for (EntryIter i = entries.begin(); i != entries.end(); ++i) {
|
|
wxString name = (*i)->GetName(wxPATH_UNIX);
|
|
TestEntries::iterator j = m_testEntries.find(name);
|
|
OnEntryExtracted(**i, *j->second);
|
|
delete j->second;
|
|
m_testEntries.erase(j);
|
|
}
|
|
OnArchiveExtracted(*arc, expectedTotal);
|
|
}
|
|
}
|
|
|
|
// Extract an archive using an external unarchive program
|
|
//
|
|
#ifndef __WXOSX_IPHONE__
|
|
template <class ClassFactoryT>
|
|
void ArchiveTestCase<ClassFactoryT>::ExtractArchive(wxInputStream& in,
|
|
const wxString& unarchiver)
|
|
{
|
|
// for an external unarchiver, unarchive to a tempdir
|
|
TempDir tmpdir;
|
|
|
|
if ((m_options & PipeIn) == 0) {
|
|
wxFileName fn(tmpdir.GetName());
|
|
fn.SetExt(wxT("arc"));
|
|
wxString tmparc = fn.GetPath(wxPATH_GET_SEPARATOR) + fn.GetFullName();
|
|
|
|
if (m_options & Stub)
|
|
in.SeekI(STUB_SIZE * 2);
|
|
|
|
// write the archive to a temporary file
|
|
{
|
|
wxFFileOutputStream out(tmparc);
|
|
if (out.IsOk())
|
|
out.Write(in);
|
|
}
|
|
|
|
// call unarchiver
|
|
if ( system(wxString::Format(unarchiver, tmparc.c_str()).mb_str()) == -1 )
|
|
{
|
|
wxLogError("Failed to run unarchiver command \"%s\"", unarchiver);
|
|
}
|
|
|
|
wxRemoveFile(tmparc);
|
|
}
|
|
else {
|
|
// for the non-seekable test, have the archiver extract "-" and
|
|
// feed it the archive via a pipe
|
|
PFileOutputStream out(wxString::Format(unarchiver, wxT("-")));
|
|
if (out.IsOk())
|
|
out.Write(in);
|
|
}
|
|
|
|
wxString dir = tmpdir.GetName();
|
|
VerifyDir(dir);
|
|
}
|
|
#endif
|
|
|
|
// Verifies the files produced by an external unarchiver are as expected
|
|
//
|
|
template <class ClassFactoryT>
|
|
void ArchiveTestCase<ClassFactoryT>::VerifyDir(wxString& path,
|
|
size_t rootlen /*=0*/)
|
|
{
|
|
wxDir dir;
|
|
path += wxFileName::GetPathSeparator();
|
|
int pos = path.length();
|
|
wxString name;
|
|
|
|
if (!rootlen)
|
|
rootlen = pos;
|
|
|
|
if (dir.Open(path) && dir.GetFirst(&name)) {
|
|
do {
|
|
path.replace(pos, wxString::npos, name);
|
|
name = m_factory->GetInternalName(
|
|
path.substr(rootlen, wxString::npos));
|
|
|
|
bool isDir = wxDirExists(path);
|
|
if (isDir)
|
|
name += wxT("/");
|
|
|
|
// provide some context for the error message so that we know which
|
|
// iteration of the loop we were on
|
|
string error_entry((wxT(" '") + name + wxT("'")).mb_str());
|
|
string error_context(" failed for entry" + error_entry);
|
|
|
|
TestEntries::iterator it = m_testEntries.find(name);
|
|
CPPUNIT_ASSERT_MESSAGE(
|
|
"archive contains an entry that shouldn't be there"
|
|
+ error_entry,
|
|
it != m_testEntries.end());
|
|
|
|
const TestEntry& testEntry = *it->second;
|
|
|
|
#if 0 //ndef __WINDOWS__
|
|
CPPUNIT_ASSERT_MESSAGE("timestamp check" + error_context,
|
|
testEntry.GetDateTime() ==
|
|
wxFileName(path).GetModificationTime());
|
|
#endif
|
|
if (!isDir) {
|
|
wxFFileInputStream in(path);
|
|
CPPUNIT_ASSERT_MESSAGE(
|
|
"entry not found in archive" + error_entry, in.IsOk());
|
|
|
|
size_t size = (size_t)in.GetLength();
|
|
wxCharBuffer buf(size);
|
|
CPPUNIT_ASSERT_MESSAGE("Read" + error_context,
|
|
in.Read(buf.data(), size).LastRead() == size);
|
|
CPPUNIT_ASSERT_MESSAGE("size check" + error_context,
|
|
testEntry.GetSize() == size);
|
|
CPPUNIT_ASSERT_MESSAGE("data compare" + error_context,
|
|
memcmp(buf.data(), testEntry.GetData(), size) == 0);
|
|
}
|
|
else {
|
|
VerifyDir(path, rootlen);
|
|
}
|
|
|
|
delete it->second;
|
|
m_testEntries.erase(it);
|
|
}
|
|
while (dir.GetNext(&name));
|
|
}
|
|
}
|
|
|
|
// test the simple iterators that give away ownership of an entry
|
|
//
|
|
template <class ClassFactoryT>
|
|
void ArchiveTestCase<ClassFactoryT>::TestIterator(wxInputStream& in)
|
|
{
|
|
typedef std::list<EntryT*> ArchiveCatalog;
|
|
typedef typename ArchiveCatalog::iterator CatalogIter;
|
|
|
|
wxScopedPtr<InputStreamT> arc(m_factory->NewStream(in));
|
|
size_t count = 0;
|
|
|
|
#ifdef WXARC_MEMBER_TEMPLATES
|
|
ArchiveCatalog cat((IterT)*arc, IterT());
|
|
#else
|
|
ArchiveCatalog cat;
|
|
for (IterT i(*arc); i != IterT(); ++i)
|
|
cat.push_back(*i);
|
|
#endif
|
|
|
|
for (CatalogIter it = cat.begin(); it != cat.end(); ++it) {
|
|
wxScopedPtr<EntryT> entry(*it);
|
|
count += m_testEntries.count(entry->GetName(wxPATH_UNIX));
|
|
}
|
|
|
|
CPPUNIT_ASSERT(m_testEntries.size() == cat.size());
|
|
CPPUNIT_ASSERT(count == cat.size());
|
|
}
|
|
|
|
// test the pair iterators that can be used to load a std::map or wxHashMap
|
|
// these also give away ownership of entries
|
|
//
|
|
template <class ClassFactoryT>
|
|
void ArchiveTestCase<ClassFactoryT>::TestPairIterator(wxInputStream& in)
|
|
{
|
|
typedef std::map<wxString, EntryT*> ArchiveCatalog;
|
|
typedef typename ArchiveCatalog::iterator CatalogIter;
|
|
|
|
wxScopedPtr<InputStreamT> arc(m_factory->NewStream(in));
|
|
size_t count = 0;
|
|
|
|
#ifdef WXARC_MEMBER_TEMPLATES
|
|
ArchiveCatalog cat((PairIterT)*arc, PairIterT());
|
|
#else
|
|
ArchiveCatalog cat;
|
|
for (PairIterT i(*arc); i != PairIterT(); ++i)
|
|
cat.insert(*i);
|
|
#endif
|
|
|
|
for (CatalogIter it = cat.begin(); it != cat.end(); ++it) {
|
|
wxScopedPtr<EntryT> entry(it->second);
|
|
count += m_testEntries.count(entry->GetName(wxPATH_UNIX));
|
|
}
|
|
|
|
CPPUNIT_ASSERT(m_testEntries.size() == cat.size());
|
|
CPPUNIT_ASSERT(count == cat.size());
|
|
}
|
|
|
|
// simple iterators using smart pointers, no need to worry about ownership
|
|
//
|
|
template <class ClassFactoryT>
|
|
void ArchiveTestCase<ClassFactoryT>::TestSmartIterator(wxInputStream& in)
|
|
{
|
|
typedef std::list<Ptr<EntryT> > ArchiveCatalog;
|
|
typedef typename ArchiveCatalog::iterator CatalogIter;
|
|
typedef wxArchiveIterator<InputStreamT, Ptr<EntryT> > Iter;
|
|
|
|
wxScopedPtr<InputStreamT> arc(m_factory->NewStream(in));
|
|
|
|
#ifdef WXARC_MEMBER_TEMPLATES
|
|
ArchiveCatalog cat((Iter)*arc, Iter());
|
|
#else
|
|
ArchiveCatalog cat;
|
|
for (Iter i(*arc); i != Iter(); ++i)
|
|
cat.push_back(*i);
|
|
#endif
|
|
|
|
CPPUNIT_ASSERT(m_testEntries.size() == cat.size());
|
|
|
|
for (CatalogIter it = cat.begin(); it != cat.end(); ++it)
|
|
CPPUNIT_ASSERT(m_testEntries.count((*it)->GetName(wxPATH_UNIX)));
|
|
}
|
|
|
|
// pair iterator using smart pointers
|
|
//
|
|
template <class ClassFactoryT>
|
|
void ArchiveTestCase<ClassFactoryT>::TestSmartPairIterator(wxInputStream& in)
|
|
{
|
|
#if defined _MSC_VER && defined _MSC_VER < 1200
|
|
// With VC++ 5.0 the '=' operator of std::pair breaks when the second
|
|
// type is Ptr<EntryT>, so this iterator can't be made to work.
|
|
(void)in;
|
|
#else
|
|
typedef std::map<wxString, Ptr<EntryT> > ArchiveCatalog;
|
|
typedef typename ArchiveCatalog::iterator CatalogIter;
|
|
typedef wxArchiveIterator<InputStreamT,
|
|
std::pair<wxString, Ptr<EntryT> > > PairIter;
|
|
|
|
wxScopedPtr<InputStreamT> arc(m_factory->NewStream(in));
|
|
|
|
#ifdef WXARC_MEMBER_TEMPLATES
|
|
ArchiveCatalog cat((PairIter)*arc, PairIter());
|
|
#else
|
|
ArchiveCatalog cat;
|
|
for (PairIter i(*arc); i != PairIter(); ++i)
|
|
cat.insert(*i);
|
|
#endif
|
|
|
|
CPPUNIT_ASSERT(m_testEntries.size() == cat.size());
|
|
|
|
for (CatalogIter it = cat.begin(); it != cat.end(); ++it)
|
|
CPPUNIT_ASSERT(m_testEntries.count(it->second->GetName(wxPATH_UNIX)));
|
|
#endif
|
|
}
|
|
|
|
// try reading two entries at the same time
|
|
//
|
|
template <class ClassFactoryT>
|
|
void ArchiveTestCase<ClassFactoryT>::ReadSimultaneous(TestInputStream& in)
|
|
{
|
|
typedef std::map<wxString, Ptr<EntryT> > ArchiveCatalog;
|
|
typedef wxArchiveIterator<InputStreamT,
|
|
std::pair<wxString, Ptr<EntryT> > > PairIter;
|
|
|
|
// create two archive input streams
|
|
TestInputStream in2(in);
|
|
wxScopedPtr<InputStreamT> arc(m_factory->NewStream(in));
|
|
wxScopedPtr<InputStreamT> arc2(m_factory->NewStream(in2));
|
|
|
|
// load the catalog
|
|
#ifdef WXARC_MEMBER_TEMPLATES
|
|
ArchiveCatalog cat((PairIter)*arc, PairIter());
|
|
#else
|
|
ArchiveCatalog cat;
|
|
for (PairIter i(*arc); i != PairIter(); ++i)
|
|
cat.insert(*i);
|
|
#endif
|
|
|
|
// the names of two entries to read
|
|
const wxChar *name = wxT("text/small");
|
|
const wxChar *name2 = wxT("bin/bin1000");
|
|
|
|
// open them
|
|
typename ArchiveCatalog::iterator j;
|
|
CPPUNIT_ASSERT((j = cat.find(name)) != cat.end());
|
|
CPPUNIT_ASSERT(arc->OpenEntry(*j->second));
|
|
CPPUNIT_ASSERT((j = cat.find(name2)) != cat.end());
|
|
CPPUNIT_ASSERT(arc2->OpenEntry(*j->second));
|
|
|
|
// get pointers to the expected data
|
|
TestEntries::iterator k;
|
|
CPPUNIT_ASSERT((k = m_testEntries.find(name)) != m_testEntries.end());
|
|
TestEntry *entry = k->second;
|
|
CPPUNIT_ASSERT((k = m_testEntries.find(name2)) != m_testEntries.end());
|
|
TestEntry *entry2 = k->second;
|
|
|
|
size_t count = 0, count2 = 0;
|
|
size_t size = entry->GetSize(), size2 = entry2->GetSize();
|
|
const char *data = entry->GetData(), *data2 = entry2->GetData();
|
|
|
|
// read and check the two entries in parallel, character by character
|
|
while (arc->IsOk() || arc2->IsOk()) {
|
|
char ch = arc->GetC();
|
|
if (arc->LastRead() == 1) {
|
|
CPPUNIT_ASSERT(count < size);
|
|
CPPUNIT_ASSERT(ch == data[count++]);
|
|
}
|
|
char ch2 = arc2->GetC();
|
|
if (arc2->LastRead() == 1) {
|
|
CPPUNIT_ASSERT(count2 < size2);
|
|
CPPUNIT_ASSERT(ch2 == data2[count2++]);
|
|
}
|
|
}
|
|
|
|
CPPUNIT_ASSERT(arc->Eof());
|
|
CPPUNIT_ASSERT(arc2->Eof());
|
|
CPPUNIT_ASSERT(count == size);
|
|
CPPUNIT_ASSERT(count2 == size2);
|
|
}
|
|
|
|
// Nothing useful can be done with a generic notifier yet, so just test one
|
|
// can be set
|
|
//
|
|
template <class NotifierT, class EntryT>
|
|
class ArchiveNotifier : public NotifierT
|
|
{
|
|
public:
|
|
void OnEntryUpdated(EntryT& WXUNUSED(entry)) wxOVERRIDE { }
|
|
};
|
|
|
|
template <class ClassFactoryT>
|
|
void ArchiveTestCase<ClassFactoryT>::OnSetNotifier(EntryT& entry)
|
|
{
|
|
static ArchiveNotifier<NotifierT, EntryT> notifier;
|
|
entry.SetNotifier(notifier);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// An additional case to check that reading corrupt archives doesn't crash
|
|
|
|
class CorruptionTestCase : public CppUnit::TestCase
|
|
{
|
|
public:
|
|
CorruptionTestCase(std::string name,
|
|
wxArchiveClassFactory *factory,
|
|
int options)
|
|
: CppUnit::TestCase(TestId::MakeId() + name),
|
|
m_factory(factory),
|
|
m_options(options)
|
|
{ }
|
|
|
|
// the entry point for the test
|
|
void runTest() wxOVERRIDE;
|
|
|
|
protected:
|
|
void CreateArchive(wxOutputStream& out);
|
|
void ExtractArchive(wxInputStream& in);
|
|
|
|
wxScopedPtr<wxArchiveClassFactory> m_factory; // factory to make classes
|
|
int m_options; // test options
|
|
};
|
|
|
|
void CorruptionTestCase::runTest()
|
|
{
|
|
TestOutputStream out(m_options);
|
|
CreateArchive(out);
|
|
TestInputStream in(out, 0);
|
|
wxFileOffset len = in.GetLength();
|
|
|
|
// try flipping one byte in the archive
|
|
int pos;
|
|
for (pos = 0; pos < len; pos++) {
|
|
char n = in[pos];
|
|
in[pos] = ~n;
|
|
ExtractArchive(in);
|
|
in.Rewind();
|
|
in[pos] = n;
|
|
}
|
|
|
|
// try zeroing one byte in the archive
|
|
for (pos = 0; pos < len; pos++) {
|
|
char n = in[pos];
|
|
in[pos] = 0;
|
|
ExtractArchive(in);
|
|
in.Rewind();
|
|
in[pos] = n;
|
|
}
|
|
|
|
// try chopping the archive off
|
|
for (int size = 1; size <= len; size++) {
|
|
in.Chop(size);
|
|
ExtractArchive(in);
|
|
in.Rewind();
|
|
}
|
|
}
|
|
|
|
void CorruptionTestCase::CreateArchive(wxOutputStream& out)
|
|
{
|
|
wxScopedPtr<wxArchiveOutputStream> arc(m_factory->NewStream(out));
|
|
|
|
arc->PutNextDirEntry(wxT("dir"));
|
|
arc->PutNextEntry(wxT("file"));
|
|
arc->Write(wxT("foo"), 3);
|
|
}
|
|
|
|
void CorruptionTestCase::ExtractArchive(wxInputStream& in)
|
|
{
|
|
wxScopedPtr<wxArchiveInputStream> arc(m_factory->NewStream(in));
|
|
wxScopedPtr<wxArchiveEntry> entry(arc->GetNextEntry());
|
|
|
|
while (entry.get() != NULL) {
|
|
char buf[1024];
|
|
|
|
while (arc->IsOk())
|
|
arc->Read(buf, sizeof(buf));
|
|
|
|
entry.reset(arc->GetNextEntry());
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Make the ids
|
|
|
|
int TestId::m_seed = 6219;
|
|
|
|
// static
|
|
string TestId::MakeId()
|
|
{
|
|
m_seed = (m_seed * 171) % 30269;
|
|
return string(wxString::Format(wxT("%-6d"), m_seed).mb_str());
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Suite base
|
|
|
|
ArchiveTestSuite::ArchiveTestSuite(string name)
|
|
: CppUnit::TestSuite("archive/" + name),
|
|
m_name(name.c_str(), *wxConvCurrent)
|
|
{
|
|
m_name = wxT("wx") + m_name.Left(1).Upper() + m_name.Mid(1).Lower();
|
|
m_path.AddEnvList(wxT("PATH"));
|
|
m_archivers.push_back(wxT(""));
|
|
m_unarchivers.push_back(wxT(""));
|
|
}
|
|
|
|
// add the command for an external archiver to the list, testing for it in
|
|
// the path first
|
|
//
|
|
void ArchiveTestSuite::AddCmd(wxArrayString& cmdlist, const wxString& cmd)
|
|
{
|
|
if (IsInPath(cmd))
|
|
cmdlist.push_back(cmd);
|
|
}
|
|
|
|
bool ArchiveTestSuite::IsInPath(const wxString& cmd)
|
|
{
|
|
wxString c = cmd.BeforeFirst(wxT(' '));
|
|
#ifdef __WINDOWS__
|
|
c += wxT(".exe");
|
|
#endif
|
|
return !m_path.FindValidPath(c).empty();
|
|
}
|
|
|
|
// run all the tests in the test suite
|
|
//
|
|
void ArchiveTestSuite::DoRunTest()
|
|
{
|
|
typedef wxArrayString::iterator Iter;
|
|
|
|
for (int generic = 0; generic < 2; generic++)
|
|
for (Iter i = m_unarchivers.begin(); i != m_unarchivers.end(); ++i)
|
|
for (Iter j = m_archivers.begin(); j != m_archivers.end(); ++j)
|
|
for (int options = 0; options <= AllOptions; options++)
|
|
{
|
|
#ifdef WXARC_NO_POPEN
|
|
// if no popen then can't pipe in/out of archiver
|
|
if ((options & PipeIn) && !i->empty())
|
|
continue;
|
|
if ((options & PipeOut) && !j->empty())
|
|
continue;
|
|
#endif
|
|
string descr = Description(m_name, options,
|
|
generic != 0, *j, *i);
|
|
|
|
CppUnit::Test *test = makeTest(descr, options,
|
|
generic != 0, *j, *i);
|
|
|
|
if (test)
|
|
{
|
|
test->runTest();
|
|
delete test;
|
|
}
|
|
}
|
|
|
|
for (int options = 0; options <= PipeIn; options += PipeIn)
|
|
{
|
|
wxObject *pObj = wxCreateDynamicObject(m_name + wxT("ClassFactory"));
|
|
wxArchiveClassFactory *factory;
|
|
factory = wxDynamicCast(pObj, wxArchiveClassFactory);
|
|
|
|
if (factory) {
|
|
string descr(m_name.mb_str());
|
|
descr = "CorruptionTestCase (" + descr + ")";
|
|
|
|
if (options)
|
|
descr += " (PipeIn)";
|
|
|
|
CorruptionTestCase test(descr, factory, options);
|
|
test.runTest();
|
|
}
|
|
}
|
|
}
|
|
|
|
CppUnit::Test *ArchiveTestSuite::makeTest(
|
|
string WXUNUSED(descr),
|
|
int WXUNUSED(options),
|
|
bool WXUNUSED(genericInterface),
|
|
const wxString& WXUNUSED(archiver),
|
|
const wxString& WXUNUSED(unarchiver))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// make a display string for the option bits
|
|
//
|
|
string ArchiveTestSuite::Description(const wxString& type,
|
|
int options,
|
|
bool genericInterface,
|
|
const wxString& archiver,
|
|
const wxString& unarchiver)
|
|
{
|
|
wxString descr;
|
|
|
|
if (genericInterface)
|
|
descr << wxT("wxArchive (") << type << wxT(")");
|
|
else
|
|
descr << type;
|
|
|
|
if (!archiver.empty()) {
|
|
const wxChar *fn = (options & PipeOut) != 0 ? wxT("-") : wxT("file");
|
|
const wxString cmd = archiver.Contains("%s")
|
|
? wxString::Format(archiver, fn)
|
|
: archiver;
|
|
descr << wxT(" (") << cmd << wxT(")");
|
|
}
|
|
if (!unarchiver.empty()) {
|
|
const wxChar *fn = (options & PipeIn) != 0 ? wxT("-") : wxT("file");
|
|
const wxString cmd = unarchiver.Contains("%s")
|
|
? wxString::Format(unarchiver, fn)
|
|
: unarchiver;
|
|
descr << wxT(" (") << cmd << wxT(")");
|
|
}
|
|
|
|
wxString optstr;
|
|
|
|
if ((options & PipeIn) != 0)
|
|
optstr += wxT("|PipeIn");
|
|
if ((options & PipeOut) != 0)
|
|
optstr += wxT("|PipeOut");
|
|
if ((options & Stub) != 0)
|
|
optstr += wxT("|Stub");
|
|
if (!optstr.empty())
|
|
optstr = wxT(" (") + optstr.substr(1) + wxT(")");
|
|
|
|
descr << optstr;
|
|
|
|
return string(descr.mb_str());
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Instantiations
|
|
|
|
template class ArchiveTestCase<wxArchiveClassFactory>;
|
|
|
|
#if wxUSE_ZIPSTREAM
|
|
#include "wx/zipstrm.h"
|
|
template class ArchiveTestCase<wxZipClassFactory>;
|
|
#endif
|
|
|
|
#if wxUSE_TARSTREAM
|
|
#include "wx/tarstrm.h"
|
|
template class ArchiveTestCase<wxTarClassFactory>;
|
|
#endif
|
|
|
|
#endif // wxUSE_STREAMS && wxUSE_ARCHIVE_STREAMS
|