archivetest.cpp 42 KB

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