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