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"
45 ///////////////////////////////////////////////////////////////////////////////
48 bool IsFAT(const wxString
& path
);
49 void MakeSparse(const wxString
& path
, int fd
);
52 ///////////////////////////////////////////////////////////////////////////////
53 // Base class for the test cases - common code
55 class LargeFileTest
: public CppUnit::TestCase
58 LargeFileTest(std::string name
) : CppUnit::TestCase(name
) { }
59 virtual ~LargeFileTest() { }
64 virtual wxInputStream
*MakeInStream(const wxString
& name
) const = 0;
65 virtual wxOutputStream
*MakeOutStream(const wxString
& name
) const = 0;
66 virtual bool HasLFS() const = 0;
69 void LargeFileTest::runTest()
71 // self deleting temp file
73 TmpFile() : m_name(wxFileName::CreateTempFileName(_T("wxlfs-"))) { }
74 ~TmpFile() { if (!m_name
.empty()) wxRemoveFile(m_name
); }
78 CPPUNIT_ASSERT(!tmpfile
.m_name
.empty());
81 bool fourGigLimit
= false;
85 wxString
n(getName().c_str(), *wxConvCurrent
);
86 wxLogInfo(n
+ _T(": No large file support, testing up to 2GB only"));
88 else if (IsFAT(tmpfile
.m_name
)) {
90 wxString
n(getName().c_str(), *wxConvCurrent
);
91 wxLogInfo(n
+ _T(": FAT volumes are limited to 4GB files"));
94 // size of the test blocks
95 const size_t size
= 0x40;
102 memset(upto2Gig
, 'A', size
);
103 memset(past2Gig
, 'B', size
);
104 memset(upto4Gig
, 'X', size
);
105 memset(past4Gig
, 'Y', size
);
109 // write a large file
111 auto_ptr
<wxOutputStream
> out(MakeOutStream(tmpfile
.m_name
));
113 // write 'A's at [ 0x7fffffbf, 0x7fffffff [
114 pos
= 0x7fffffff - size
;
115 CPPUNIT_ASSERT(out
->SeekO(pos
) == pos
);
116 CPPUNIT_ASSERT(out
->Write(upto2Gig
, size
).LastWrite() == size
);
120 // write 'B's at [ 0x7fffffff, 0x8000003f [
121 CPPUNIT_ASSERT(out
->Write(past2Gig
, size
).LastWrite() == size
);
123 CPPUNIT_ASSERT(out
->TellO() == pos
);
125 // write 'X's at [ 0xffffffbf, 0xffffffff [
126 pos
= 0xffffffff - size
;
127 CPPUNIT_ASSERT(out
->SeekO(pos
) == pos
);
128 CPPUNIT_ASSERT(out
->Write(upto4Gig
, size
).LastWrite() == size
);
132 // write 'Y's at [ 0xffffffff, 0x10000003f [
133 CPPUNIT_ASSERT(out
->Write(past4Gig
, size
).LastWrite() == size
);
138 // check the file seems to be the right length
139 CPPUNIT_ASSERT(out
->TellO() == pos
);
140 CPPUNIT_ASSERT(out
->GetLength() == pos
);
143 // read the large file back
145 auto_ptr
<wxInputStream
> in(MakeInStream(tmpfile
.m_name
));
149 CPPUNIT_ASSERT(in
->GetLength() == pos
);
153 CPPUNIT_ASSERT(in
->GetLength() > pos
);
155 // read back the 'Y's at [ 0xffffffff, 0x10000003f [
156 CPPUNIT_ASSERT(in
->SeekI(pos
) == pos
);
157 CPPUNIT_ASSERT(in
->Read(buf
, size
).LastRead() == size
);
158 CPPUNIT_ASSERT(memcmp(buf
, past4Gig
, size
) == 0);
160 CPPUNIT_ASSERT(in
->TellI() == in
->GetLength());
163 // read back the 'X's at [ 0xffffffbf, 0xffffffff [
165 CPPUNIT_ASSERT(in
->SeekI(pos
) == pos
);
166 CPPUNIT_ASSERT(in
->Read(buf
, size
).LastRead() == size
);
167 CPPUNIT_ASSERT(memcmp(buf
, upto4Gig
, size
) == 0);
169 CPPUNIT_ASSERT(in
->TellI() == pos
);
171 // read back the 'B's at [ 0x7fffffff, 0x8000003f [
173 CPPUNIT_ASSERT(in
->SeekI(pos
) == pos
);
174 CPPUNIT_ASSERT(in
->Read(buf
, size
).LastRead() == size
);
175 CPPUNIT_ASSERT(memcmp(buf
, past2Gig
, size
) == 0);
178 CPPUNIT_ASSERT(in
->GetLength() == 0x7fffffff);
182 // read back the 'A's at [ 0x7fffffbf, 0x7fffffff [
184 CPPUNIT_ASSERT(in
->SeekI(pos
) == pos
);
185 CPPUNIT_ASSERT(in
->Read(buf
, size
).LastRead() == size
);
186 CPPUNIT_ASSERT(memcmp(buf
, upto2Gig
, size
) == 0);
188 CPPUNIT_ASSERT(in
->TellI() == pos
);
193 ///////////////////////////////////////////////////////////////////////////////
196 class LargeFileTest_wxFile
: public LargeFileTest
199 LargeFileTest_wxFile() : LargeFileTest("wxFile streams") { }
202 wxInputStream
*MakeInStream(const wxString
& name
) const;
203 wxOutputStream
*MakeOutStream(const wxString
& name
) const;
204 bool HasLFS() const { return (wxFileOffset
)0xffffffff > 0; }
207 wxInputStream
*LargeFileTest_wxFile::MakeInStream(const wxString
& name
) const
209 auto_ptr
<wxFileInputStream
> in(new wxFileInputStream(name
));
210 CPPUNIT_ASSERT(in
->Ok());
214 wxOutputStream
*LargeFileTest_wxFile::MakeOutStream(const wxString
& name
) const
216 wxFile
file(name
, wxFile::write
);
217 CPPUNIT_ASSERT(file
.IsOpened());
220 MakeSparse(name
, fd
);
221 return new wxFileOutputStream(fd
);
225 ///////////////////////////////////////////////////////////////////////////////
228 class LargeFileTest_wxFFile
: public LargeFileTest
231 LargeFileTest_wxFFile() : LargeFileTest("wxFFile streams") { }
234 wxInputStream
*MakeInStream(const wxString
& name
) const;
235 wxOutputStream
*MakeOutStream(const wxString
& name
) const;
239 wxInputStream
*LargeFileTest_wxFFile::MakeInStream(const wxString
& name
) const
241 auto_ptr
<wxFFileInputStream
> in(new wxFFileInputStream(name
));
242 CPPUNIT_ASSERT(in
->Ok());
246 wxOutputStream
*LargeFileTest_wxFFile::MakeOutStream(const wxString
& name
) const
248 wxFFile
file(name
, _T("w"));
249 CPPUNIT_ASSERT(file
.IsOpened());
250 FILE *fp
= file
.fp();
252 MakeSparse(name
, fileno(fp
));
253 return new wxFFileOutputStream(fp
);
256 bool LargeFileTest_wxFFile::HasLFS() const
259 return (wxFileOffset
)0xffffffff > 0;
266 ///////////////////////////////////////////////////////////////////////////////
269 class largeFile
: public CppUnit::TestSuite
272 largeFile() : CppUnit::TestSuite("largeFile") { }
274 static CppUnit::Test
*suite();
277 CppUnit::Test
*largeFile::suite()
279 largeFile
*suite
= new largeFile
;
281 suite
->addTest(new LargeFileTest_wxFile
);
282 suite
->addTest(new LargeFileTest_wxFFile
);
288 ///////////////////////////////////////////////////////////////////////////////
289 // Implement the helpers
291 // Ideally these tests will be part of the default suite so that regressions
292 // are picked up. However this is only possible when sparse files are
293 // supported otherwise the tests require too much disk space.
295 // On unix, most filesystems support sparse files, though not all. So for now
296 // I'm not assuming sparse file support on unix. On Windows it's possible to
297 // test, and sparse files should be available on Win 5+ with NTFS.
301 #ifndef FILE_SUPPORTS_SPARSE_FILES
302 #define FILE_SUPPORTS_SPARSE_FILES 0x00000040
305 #ifndef FSCTL_SET_SPARSE
307 # ifndef FILE_SPECIAL_ACCESS
308 # define FILE_SPECIAL_ACCESS FILE_ANY_ACCESS
310 # define FSCTL_SET_SPARSE CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 49, \
311 METHOD_BUFFERED, FILE_SPECIAL_ACCESS)
314 static DWORD volumeFlags
;
315 static wxChar volumeType
[64];
316 static bool volumeInfoInit
;
318 void GetVolumeInfo(const wxString
& path
)
320 // extract the volume 'C:\' or '\\tooter\share\' from the path
323 if (path
.substr(1, 2) == _T(":\\")) {
324 vol
= path
.substr(0, 3);
326 if (path
.substr(0, 2) == _T("\\\\")) {
327 size_t i
= path
.find(_T('\\'), 2);
329 if (i
!= wxString::npos
&& i
> 2) {
330 size_t j
= path
.find(_T('\\'), ++i
);
333 vol
= path
.substr(0, j
) + _T("\\");
338 // NULL means the current volume
339 const wxChar
*pVol
= vol
.empty() ? NULL
: vol
.c_str();
341 if (!::GetVolumeInformation(pVol
, NULL
, 0, NULL
, NULL
,
344 WXSIZEOF(volumeType
)))
345 wxLogSysError(_T("GetVolumeInformation() failed"));
347 volumeInfoInit
= true;
350 bool IsFAT(const wxString
& path
)
354 return wxString(volumeType
).Upper().find(_T("FAT")) != wxString::npos
;
357 void MakeSparse(const wxString
& path
, int fd
)
364 if ((volumeFlags
& FILE_SUPPORTS_SPARSE_FILES
) != 0)
365 if (!::DeviceIoControl((HANDLE
)_get_osfhandle(fd
),
367 NULL
, 0, NULL
, 0, &cb
, NULL
))
368 volumeFlags
&= ~FILE_SUPPORTS_SPARSE_FILES
;
371 CppUnit::Test
* GetlargeFileSuite()
373 if (!volumeInfoInit
) {
375 wxString path
= wxFileName::CreateTempFileName(_T("wxlfs-"), &file
);
376 MakeSparse(path
, file
.fd());
380 if ((volumeFlags
& FILE_SUPPORTS_SPARSE_FILES
) != 0)
381 return largeFile::suite();
388 bool IsFAT(const wxString
& WXUNUSED(path
)) { return false; }
389 void MakeSparse(const wxString
& WXUNUSED(path
), int WXUNUSED(fd
)) { }
391 CppUnit::Test
* GetlargeFileSuite() { return NULL
; }
395 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(largeFile
, "largeFile");
396 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(largeFile
, "Streams.largeFile");