largefile.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Name: tests/streams/largefile.cpp
  3. // Purpose: Tests for large file support
  4. // Author: Mike Wetherell
  5. // Copyright: (c) 2004 Mike Wetherell
  6. // Licence: wxWindows licence
  7. ///////////////////////////////////////////////////////////////////////////////
  8. //
  9. // We're interested in what happens around offsets 0x7fffffff and 0xffffffff
  10. // so the test writes a small chunk of test data just before and just after
  11. // these offsets, then reads them back.
  12. //
  13. // The tests can be run with:
  14. //
  15. // test --verbose largeFile
  16. //
  17. // On systems supporting sparse files they will also be registered in the
  18. // Streams subsuite so that they run by default.
  19. //
  20. // For compilers that support precompilation, includes "wx/wx.h".
  21. #include "testprec.h"
  22. #ifdef __BORLANDC__
  23. #pragma hdrstop
  24. #endif
  25. // for all others, include the necessary headers
  26. #ifndef WX_PRECOMP
  27. #include "wx/wx.h"
  28. #endif
  29. #include "wx/filename.h"
  30. #include "wx/wfstream.h"
  31. #ifdef __WINDOWS__
  32. #include "wx/msw/wrapwin.h"
  33. #ifdef __VISUALC__
  34. // 'nonstandard extension used : nameless struct/union' occurs inside
  35. // winioctl.h
  36. #pragma warning(disable:4201)
  37. #endif
  38. #include <winioctl.h>
  39. #ifdef __VISUALC__
  40. #pragma warning(default:4201)
  41. #endif
  42. #endif
  43. #ifdef __VISUALC__
  44. #define fileno _fileno
  45. #endif
  46. using std::auto_ptr;
  47. ///////////////////////////////////////////////////////////////////////////////
  48. // Helpers
  49. bool IsFAT(const wxString& path);
  50. void MakeSparse(const wxString& path, int fd);
  51. ///////////////////////////////////////////////////////////////////////////////
  52. // Base class for the test cases - common code
  53. class LargeFileTest : public CppUnit::TestCase
  54. {
  55. public:
  56. LargeFileTest(std::string name) : CppUnit::TestCase(name) { }
  57. virtual ~LargeFileTest() { }
  58. protected:
  59. void runTest();
  60. virtual wxInputStream *MakeInStream(const wxString& name) const = 0;
  61. virtual wxOutputStream *MakeOutStream(const wxString& name) const = 0;
  62. virtual bool HasLFS() const = 0;
  63. };
  64. void LargeFileTest::runTest()
  65. {
  66. // self deleting temp file
  67. struct TmpFile {
  68. TmpFile() : m_name(wxFileName::CreateTempFileName(wxT("wxlfs-"))) { }
  69. ~TmpFile() { if (!m_name.empty()) wxRemoveFile(m_name); }
  70. wxString m_name;
  71. } tmpfile;
  72. CPPUNIT_ASSERT(!tmpfile.m_name.empty());
  73. bool haveLFS = true;
  74. bool fourGigLimit = false;
  75. if (!HasLFS()) {
  76. haveLFS = false;
  77. wxString n(getName().c_str(), *wxConvCurrent);
  78. wxLogInfo(n + wxT(": No large file support, testing up to 2GB only"));
  79. }
  80. else if (IsFAT(tmpfile.m_name)) {
  81. fourGigLimit = true;
  82. wxString n(getName().c_str(), *wxConvCurrent);
  83. wxLogInfo(n + wxT(": FAT volumes are limited to 4GB files"));
  84. }
  85. // size of the test blocks
  86. const size_t size = 0x40;
  87. // the test blocks
  88. char upto2Gig[size];
  89. char past2Gig[size];
  90. char upto4Gig[size];
  91. char past4Gig[size];
  92. memset(upto2Gig, 'A', size);
  93. memset(past2Gig, 'B', size);
  94. memset(upto4Gig, 'X', size);
  95. memset(past4Gig, 'Y', size);
  96. wxFileOffset pos;
  97. // write a large file
  98. {
  99. auto_ptr<wxOutputStream> out(MakeOutStream(tmpfile.m_name));
  100. // write 'A's at [ 0x7fffffbf, 0x7fffffff [
  101. pos = 0x7fffffff - size;
  102. CPPUNIT_ASSERT(out->SeekO(pos) == pos);
  103. CPPUNIT_ASSERT(out->Write(upto2Gig, size).LastWrite() == size);
  104. pos += size;
  105. if (haveLFS) {
  106. // write 'B's at [ 0x7fffffff, 0x8000003f [
  107. CPPUNIT_ASSERT(out->Write(past2Gig, size).LastWrite() == size);
  108. pos += size;
  109. CPPUNIT_ASSERT(out->TellO() == pos);
  110. // write 'X's at [ 0xffffffbf, 0xffffffff [
  111. pos = 0xffffffff - size;
  112. CPPUNIT_ASSERT(out->SeekO(pos) == pos);
  113. CPPUNIT_ASSERT(out->Write(upto4Gig, size).LastWrite() == size);
  114. pos += size;
  115. if (!fourGigLimit) {
  116. // write 'Y's at [ 0xffffffff, 0x10000003f [
  117. CPPUNIT_ASSERT(out->Write(past4Gig, size).LastWrite() == size);
  118. pos += size;
  119. }
  120. }
  121. // check the file seems to be the right length
  122. CPPUNIT_ASSERT(out->TellO() == pos);
  123. CPPUNIT_ASSERT(out->GetLength() == pos);
  124. }
  125. // read the large file back
  126. {
  127. auto_ptr<wxInputStream> in(MakeInStream(tmpfile.m_name));
  128. char buf[size];
  129. if (haveLFS) {
  130. CPPUNIT_ASSERT(in->GetLength() == pos);
  131. pos = 0xffffffff;
  132. if (!fourGigLimit) {
  133. CPPUNIT_ASSERT(in->GetLength() > pos);
  134. // read back the 'Y's at [ 0xffffffff, 0x10000003f [
  135. CPPUNIT_ASSERT(in->SeekI(pos) == pos);
  136. CPPUNIT_ASSERT(in->Read(buf, size).LastRead() == size);
  137. CPPUNIT_ASSERT(memcmp(buf, past4Gig, size) == 0);
  138. CPPUNIT_ASSERT(in->TellI() == in->GetLength());
  139. }
  140. // read back the 'X's at [ 0xffffffbf, 0xffffffff [
  141. pos -= size;
  142. CPPUNIT_ASSERT(in->SeekI(pos) == pos);
  143. CPPUNIT_ASSERT(in->Read(buf, size).LastRead() == size);
  144. CPPUNIT_ASSERT(memcmp(buf, upto4Gig, size) == 0);
  145. pos += size;
  146. CPPUNIT_ASSERT(in->TellI() == pos);
  147. // read back the 'B's at [ 0x7fffffff, 0x8000003f [
  148. pos = 0x7fffffff;
  149. CPPUNIT_ASSERT(in->SeekI(pos) == pos);
  150. CPPUNIT_ASSERT(in->Read(buf, size).LastRead() == size);
  151. CPPUNIT_ASSERT(memcmp(buf, past2Gig, size) == 0);
  152. }
  153. else {
  154. CPPUNIT_ASSERT(in->GetLength() == 0x7fffffff);
  155. pos = 0x7fffffff;
  156. }
  157. // read back the 'A's at [ 0x7fffffbf, 0x7fffffff [
  158. pos -= size;
  159. CPPUNIT_ASSERT(in->SeekI(pos) == pos);
  160. CPPUNIT_ASSERT(in->Read(buf, size).LastRead() == size);
  161. CPPUNIT_ASSERT(memcmp(buf, upto2Gig, size) == 0);
  162. pos += size;
  163. CPPUNIT_ASSERT(in->TellI() == pos);
  164. }
  165. }
  166. ///////////////////////////////////////////////////////////////////////////////
  167. // wxFile test case
  168. class LargeFileTest_wxFile : public LargeFileTest
  169. {
  170. public:
  171. LargeFileTest_wxFile() : LargeFileTest("wxFile streams") { }
  172. protected:
  173. wxInputStream *MakeInStream(const wxString& name) const;
  174. wxOutputStream *MakeOutStream(const wxString& name) const;
  175. bool HasLFS() const { return (wxFileOffset)0xffffffff > 0; }
  176. };
  177. wxInputStream *LargeFileTest_wxFile::MakeInStream(const wxString& name) const
  178. {
  179. auto_ptr<wxFileInputStream> in(new wxFileInputStream(name));
  180. CPPUNIT_ASSERT(in->IsOk());
  181. return in.release();
  182. }
  183. wxOutputStream *LargeFileTest_wxFile::MakeOutStream(const wxString& name) const
  184. {
  185. wxFile file(name, wxFile::write);
  186. CPPUNIT_ASSERT(file.IsOpened());
  187. int fd = file.fd();
  188. file.Detach();
  189. MakeSparse(name, fd);
  190. return new wxFileOutputStream(fd);
  191. }
  192. ///////////////////////////////////////////////////////////////////////////////
  193. // wxFFile test case
  194. class LargeFileTest_wxFFile : public LargeFileTest
  195. {
  196. public:
  197. LargeFileTest_wxFFile() : LargeFileTest("wxFFile streams") { }
  198. protected:
  199. wxInputStream *MakeInStream(const wxString& name) const;
  200. wxOutputStream *MakeOutStream(const wxString& name) const;
  201. bool HasLFS() const;
  202. };
  203. wxInputStream *LargeFileTest_wxFFile::MakeInStream(const wxString& name) const
  204. {
  205. auto_ptr<wxFFileInputStream> in(new wxFFileInputStream(name));
  206. CPPUNIT_ASSERT(in->IsOk());
  207. return in.release();
  208. }
  209. wxOutputStream *LargeFileTest_wxFFile::MakeOutStream(const wxString& name) const
  210. {
  211. wxFFile file(name, wxT("w"));
  212. CPPUNIT_ASSERT(file.IsOpened());
  213. FILE *fp = file.fp();
  214. file.Detach();
  215. MakeSparse(name, fileno(fp));
  216. return new wxFFileOutputStream(fp);
  217. }
  218. bool LargeFileTest_wxFFile::HasLFS() const
  219. {
  220. #ifdef wxHAS_LARGE_FFILES
  221. return true;
  222. #else
  223. return false;
  224. #endif
  225. }
  226. ///////////////////////////////////////////////////////////////////////////////
  227. // The suite
  228. class largeFile : public CppUnit::TestSuite
  229. {
  230. public:
  231. largeFile() : CppUnit::TestSuite("largeFile") { }
  232. static CppUnit::Test *suite();
  233. };
  234. CppUnit::Test *largeFile::suite()
  235. {
  236. largeFile *suite = new largeFile;
  237. suite->addTest(new LargeFileTest_wxFile);
  238. suite->addTest(new LargeFileTest_wxFFile);
  239. return suite;
  240. }
  241. ///////////////////////////////////////////////////////////////////////////////
  242. // Implement the helpers
  243. //
  244. // Ideally these tests will be part of the default suite so that regressions
  245. // are picked up. However this is only possible when sparse files are
  246. // supported otherwise the tests require too much disk space.
  247. #ifdef __WINDOWS__
  248. #ifndef FILE_SUPPORTS_SPARSE_FILES
  249. #define FILE_SUPPORTS_SPARSE_FILES 0x00000040
  250. #endif
  251. #ifndef FSCTL_SET_SPARSE
  252. # ifndef FILE_SPECIAL_ACCESS
  253. # define FILE_SPECIAL_ACCESS FILE_ANY_ACCESS
  254. # endif
  255. # define FSCTL_SET_SPARSE CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 49, \
  256. METHOD_BUFFERED, FILE_SPECIAL_ACCESS)
  257. #endif
  258. static DWORD volumeFlags;
  259. static wxChar volumeType[64];
  260. static bool volumeInfoInit;
  261. void GetVolumeInfo(const wxString& path)
  262. {
  263. // extract the volume 'C:\' or '\\tooter\share\' from the path
  264. wxString vol;
  265. if (path.substr(1, 2) == wxT(":\\")) {
  266. vol = path.substr(0, 3);
  267. } else {
  268. if (path.substr(0, 2) == wxT("\\\\")) {
  269. size_t i = path.find(wxT('\\'), 2);
  270. if (i != wxString::npos && i > 2) {
  271. size_t j = path.find(wxT('\\'), ++i);
  272. if (j != i)
  273. vol = path.substr(0, j) + wxT("\\");
  274. }
  275. }
  276. }
  277. // NULL means the current volume
  278. const wxChar *pVol = vol.empty() ? (const wxChar *)NULL
  279. : vol.c_str();
  280. if (!::GetVolumeInformation(pVol, NULL, 0, NULL, NULL,
  281. &volumeFlags,
  282. volumeType,
  283. WXSIZEOF(volumeType)))
  284. {
  285. wxLogSysError(wxT("GetVolumeInformation() failed"));
  286. }
  287. volumeInfoInit = true;
  288. }
  289. bool IsFAT(const wxString& path)
  290. {
  291. if (!volumeInfoInit)
  292. GetVolumeInfo(path);
  293. return wxString(volumeType).Upper().find(wxT("FAT")) != wxString::npos;
  294. }
  295. void MakeSparse(const wxString& path, int fd)
  296. {
  297. DWORD cb;
  298. if (!volumeInfoInit)
  299. GetVolumeInfo(path);
  300. if ((volumeFlags & FILE_SUPPORTS_SPARSE_FILES) != 0)
  301. if (!::DeviceIoControl((HANDLE)_get_osfhandle(fd),
  302. FSCTL_SET_SPARSE,
  303. NULL, 0, NULL, 0, &cb, NULL))
  304. volumeFlags &= ~FILE_SUPPORTS_SPARSE_FILES;
  305. }
  306. // return the suite if sparse files are supported, otherwise return NULL
  307. //
  308. CppUnit::Test* GetlargeFileSuite()
  309. {
  310. if (!volumeInfoInit) {
  311. wxString path;
  312. {
  313. wxFile file;
  314. path = wxFileName::CreateTempFileName(wxT("wxlfs-"), &file);
  315. MakeSparse(path, file.fd());
  316. }
  317. wxRemoveFile(path);
  318. }
  319. if ((volumeFlags & FILE_SUPPORTS_SPARSE_FILES) != 0)
  320. return largeFile::suite();
  321. else
  322. return NULL;
  323. }
  324. #else // __WINDOWS__
  325. bool IsFAT(const wxString& WXUNUSED(path)) { return false; }
  326. void MakeSparse(const wxString& WXUNUSED(path), int WXUNUSED(fd)) { }
  327. // return the suite if sparse files are supported, otherwise return NULL
  328. //
  329. CppUnit::Test* GetlargeFileSuite()
  330. {
  331. wxString path;
  332. struct stat st1, st2;
  333. memset(&st1, 0, sizeof(st1));
  334. memset(&st2, 0, sizeof(st2));
  335. {
  336. wxFile file;
  337. path = wxFileName::CreateTempFileName(wxT("wxlfs-"), &file);
  338. fstat(file.fd(), &st1);
  339. file.Seek(st1.st_blksize);
  340. file.Write("x", 1);
  341. fstat(file.fd(), &st1);
  342. file.Seek(0);
  343. file.Write("x", 1);
  344. fstat(file.fd(), &st2);
  345. }
  346. wxRemoveFile(path);
  347. if (st1.st_blocks != st2.st_blocks)
  348. return largeFile::suite();
  349. else
  350. return NULL;
  351. }
  352. #endif // __WINDOWS__
  353. CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(largeFile, "largeFile");
  354. CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(largeFile, "Streams.largeFile");