1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: tests/streams/largefile.cpp
3 // Purpose: Tests for large file support
4 // Author: Mike Wetherell
6 // Copyright: (c) 2004 Mike Wetherell
7 // Licence: wxWidgets licence
8 ///////////////////////////////////////////////////////////////////////////////
11 // We're interested in what happens around offsets 0x7fffffff and 0xffffffff
12 // so the test writes a small chunk of test data just before and just after
13 // these offsets, then reads them back.
15 // The tests can be run with:
17 // test --verbose largeFile
19 // On systems supporting sparse files they may also be registered in the
23 // For compilers that support precompilation, includes "wx/wx.h".
30 // for all others, include the necessary headers
35 #include "wx/filename.h"
36 #include "wx/wfstream.h"
39 #include "wx/msw/wrapwin.h"
44 #define fileno _fileno
50 ///////////////////////////////////////////////////////////////////////////////
53 bool IsFAT(const wxString
& path
);
54 void MakeSparse(const wxString
& path
, int fd
);
57 ///////////////////////////////////////////////////////////////////////////////
58 // Base class for the test cases - common code
60 class LargeFileTest
: public CppUnit
::TestCase
63 LargeFileTest(std
::string name
) : CppUnit
::TestCase(name
) { }
64 virtual ~LargeFileTest() { }
69 virtual wxInputStream
*MakeInStream(const wxString
& name
) const = 0;
70 virtual wxOutputStream
*MakeOutStream(const wxString
& name
) const = 0;
71 virtual bool HasLFS() const = 0;
74 void LargeFileTest
::runTest()
76 // self deleting temp file
78 TmpFile() : m_name(wxFileName
::CreateTempFileName(_T("wxlfs-"))) { }
79 ~TmpFile() { if (!m_name
.empty()) wxRemoveFile(m_name
); }
83 CPPUNIT_ASSERT(!tmpfile
.m_name
.empty());
86 bool fourGigLimit
= false;
90 wxString
n(getName().c_str(), *wxConvCurrent
);
91 wxLogInfo(n
+ _T(": No large file support, testing up to 2GB only"));
93 else if (IsFAT(tmpfile
.m_name
)) {
95 wxString
n(getName().c_str(), *wxConvCurrent
);
96 wxLogInfo(n
+ _T(": FAT volumes are limited to 4GB files"));
99 // size of the test blocks
100 const size_t size
= 0x40;
107 memset(upto2Gig
, 'A', size
);
108 memset(past2Gig
, 'B', size
);
109 memset(upto4Gig
, 'X', size
);
110 memset(past4Gig
, 'Y', size
);
114 // write a large file
116 auto_ptr
<wxOutputStream
> out(MakeOutStream(tmpfile
.m_name
));
118 // write 'A's at [ 0x7fffffbf, 0x7fffffff [
119 pos
= 0x7fffffff - size
;
120 CPPUNIT_ASSERT(out
->SeekO(pos
) == pos
);
121 CPPUNIT_ASSERT(out
->Write(upto2Gig
, size
).LastWrite() == size
);
125 // write 'B's at [ 0x7fffffff, 0x8000003f [
126 CPPUNIT_ASSERT(out
->Write(past2Gig
, size
).LastWrite() == size
);
128 CPPUNIT_ASSERT(out
->TellO() == pos
);
130 // write 'X's at [ 0xffffffbf, 0xffffffff [
131 pos
= 0xffffffff - size
;
132 CPPUNIT_ASSERT(out
->SeekO(pos
) == pos
);
133 CPPUNIT_ASSERT(out
->Write(upto4Gig
, size
).LastWrite() == size
);
137 // write 'Y's at [ 0xffffffff, 0x10000003f [
138 CPPUNIT_ASSERT(out
->Write(past4Gig
, size
).LastWrite() == size
);
143 // check the file seems to be the right length
144 CPPUNIT_ASSERT(out
->TellO() == pos
);
145 CPPUNIT_ASSERT(out
->GetLength() == pos
);
148 // read the large file back
150 auto_ptr
<wxInputStream
> in(MakeInStream(tmpfile
.m_name
));
154 CPPUNIT_ASSERT(in
->GetLength() == pos
);
158 CPPUNIT_ASSERT(in
->GetLength() > pos
);
160 // read back the 'Y's at [ 0xffffffff, 0x10000003f [
161 CPPUNIT_ASSERT(in
->SeekI(pos
) == pos
);
162 CPPUNIT_ASSERT(in
->Read(buf
, size
).LastRead() == size
);
163 CPPUNIT_ASSERT(memcmp(buf
, past4Gig
, size
) == 0);
165 CPPUNIT_ASSERT(in
->TellI() == in
->GetLength());
168 // read back the 'X's at [ 0xffffffbf, 0xffffffff [
170 CPPUNIT_ASSERT(in
->SeekI(pos
) == pos
);
171 CPPUNIT_ASSERT(in
->Read(buf
, size
).LastRead() == size
);
172 CPPUNIT_ASSERT(memcmp(buf
, upto4Gig
, size
) == 0);
174 CPPUNIT_ASSERT(in
->TellI() == pos
);
176 // read back the 'B's at [ 0x7fffffff, 0x8000003f [
178 CPPUNIT_ASSERT(in
->SeekI(pos
) == pos
);
179 CPPUNIT_ASSERT(in
->Read(buf
, size
).LastRead() == size
);
180 CPPUNIT_ASSERT(memcmp(buf
, past2Gig
, size
) == 0);
183 CPPUNIT_ASSERT(in
->GetLength() == 0x7fffffff);
187 // read back the 'A's at [ 0x7fffffbf, 0x7fffffff [
189 CPPUNIT_ASSERT(in
->SeekI(pos
) == pos
);
190 CPPUNIT_ASSERT(in
->Read(buf
, size
).LastRead() == size
);
191 CPPUNIT_ASSERT(memcmp(buf
, upto2Gig
, size
) == 0);
193 CPPUNIT_ASSERT(in
->TellI() == pos
);
198 ///////////////////////////////////////////////////////////////////////////////
201 class LargeFileTest_wxFile
: public LargeFileTest
204 LargeFileTest_wxFile() : LargeFileTest("wxFile streams") { }
207 wxInputStream
*MakeInStream(const wxString
& name
) const;
208 wxOutputStream
*MakeOutStream(const wxString
& name
) const;
209 bool HasLFS() const { return (wxFileOffset
)0xffffffff > 0; }
212 wxInputStream
*LargeFileTest_wxFile
::MakeInStream(const wxString
& name
) const
214 auto_ptr
<wxFileInputStream
> in(new wxFileInputStream(name
));
215 CPPUNIT_ASSERT(in
->Ok());
219 wxOutputStream
*LargeFileTest_wxFile
::MakeOutStream(const wxString
& name
) const
221 wxFile
file(name
, wxFile
::write
);
222 CPPUNIT_ASSERT(file
.IsOpened());
225 MakeSparse(name
, fd
);
226 return new wxFileOutputStream(fd
);
230 ///////////////////////////////////////////////////////////////////////////////
233 class LargeFileTest_wxFFile
: public LargeFileTest
236 LargeFileTest_wxFFile() : LargeFileTest("wxFFile streams") { }
239 wxInputStream
*MakeInStream(const wxString
& name
) const;
240 wxOutputStream
*MakeOutStream(const wxString
& name
) const;
244 wxInputStream
*LargeFileTest_wxFFile
::MakeInStream(const wxString
& name
) const
246 auto_ptr
<wxFFileInputStream
> in(new wxFFileInputStream(name
));
247 CPPUNIT_ASSERT(in
->Ok());
251 wxOutputStream
*LargeFileTest_wxFFile
::MakeOutStream(const wxString
& name
) const
253 wxFFile
file(name
, _T("w"));
254 CPPUNIT_ASSERT(file
.IsOpened());
255 FILE *fp
= file
.fp();
257 MakeSparse(name
, fileno(fp
));
258 return new wxFFileOutputStream(fp
);
261 bool LargeFileTest_wxFFile
::HasLFS() const
264 return (wxFileOffset
)0xffffffff > 0;
271 ///////////////////////////////////////////////////////////////////////////////
274 class largeFile
: public CppUnit
::TestSuite
277 largeFile() : CppUnit
::TestSuite("largeFile") { }
279 static CppUnit
::Test
*suite();
282 CppUnit
::Test
*largeFile
::suite()
284 largeFile
*suite
= new largeFile
;
286 suite
->addTest(new LargeFileTest_wxFile
);
287 suite
->addTest(new LargeFileTest_wxFFile
);
293 ///////////////////////////////////////////////////////////////////////////////
294 // Implement the helpers
296 // Ideally these tests will be part of the default suite so that regressions
297 // are picked up. However this is only possible when sparse files are
298 // supported otherwise the tests require too much disk space.
300 // On unix, most filesystems support sparse files, though not all. So for now
301 // I'm not assuming sparse file support on unix. On Windows it's possible to
302 // test, and sparse files should be available on Win 5+ with NTFS.
306 #ifndef FILE_SUPPORTS_SPARSE_FILES
307 #define FILE_SUPPORTS_SPARSE_FILES 0x00000040
310 #ifndef FSCTL_SET_SPARSE
312 # ifndef FILE_SPECIAL_ACCESS
313 # define FILE_SPECIAL_ACCESS FILE_ANY_ACCESS
315 # define FSCTL_SET_SPARSE CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 49, \
316 METHOD_BUFFERED, FILE_SPECIAL_ACCESS)
319 static DWORD volumeFlags
;
320 static wxChar volumeType
[64];
321 static bool volumeInfoInit
;
323 void GetVolumeInfo(const wxString
& path
)
325 // extract the volume 'C:\' or '\\tooter\share\' from the path
328 if (path
.substr(1, 2) == _T(":\\")) {
329 vol
= path
.substr(0, 3);
331 if (path
.substr(0, 2) == _T("\\\\")) {
332 size_t i
= path
.find(_T('\\'), 2);
334 if (i
!= wxString
::npos
&& i
> 2) {
335 size_t j
= path
.find(_T('\\'), ++i
);
338 vol
= path
.substr(0, j
) + _T("\\");
343 // NULL means the current volume
344 const wxChar
*pVol
= vol
.empty() ?
(const wxChar
*)NULL
347 if (!::GetVolumeInformation(pVol
, NULL
, 0, NULL
, NULL
,
350 WXSIZEOF(volumeType
)))
351 wxLogSysError(_T("GetVolumeInformation() failed"));
353 volumeInfoInit
= true;
356 bool IsFAT(const wxString
& path
)
360 return wxString(volumeType
).Upper().find(_T("FAT")) != wxString
::npos
;
363 void MakeSparse(const wxString
& path
, int fd
)
370 if ((volumeFlags
& FILE_SUPPORTS_SPARSE_FILES
) != 0)
371 if (!::DeviceIoControl((HANDLE
)_get_osfhandle(fd
),
373 NULL
, 0, NULL
, 0, &cb
, NULL
))
374 volumeFlags
&= ~FILE_SUPPORTS_SPARSE_FILES
;
377 CppUnit
::Test
* GetlargeFileSuite()
379 if (!volumeInfoInit
) {
381 wxString path
= wxFileName
::CreateTempFileName(_T("wxlfs-"), &file
);
382 MakeSparse(path
, file
.fd());
386 if ((volumeFlags
& FILE_SUPPORTS_SPARSE_FILES
) != 0)
387 return largeFile
::suite();
394 bool IsFAT(const wxString
& WXUNUSED(path
)) { return false; }
395 void MakeSparse(const wxString
& WXUNUSED(path
), int WXUNUSED(fd
)) { }
397 CppUnit
::Test
* GetlargeFileSuite() { return NULL
; }
401 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(largeFile
, "largeFile");
402 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(largeFile
, "Streams.largeFile");