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"
46 ///////////////////////////////////////////////////////////////////////////////
49 bool IsFAT(const wxString
& path
);
50 void MakeSparse(const wxString
& path
, int fd
);
53 ///////////////////////////////////////////////////////////////////////////////
54 // Base class for the test cases - common code
56 class LargeFileTest
: public CppUnit::TestCase
59 LargeFileTest(std::string name
) : CppUnit::TestCase(name
) { }
60 virtual ~LargeFileTest() { }
65 virtual wxInputStream
*MakeInStream(const wxString
& name
) const = 0;
66 virtual wxOutputStream
*MakeOutStream(const wxString
& name
) const = 0;
67 virtual bool HasLFS() const = 0;
70 void LargeFileTest::runTest()
72 // self deleting temp file
74 TmpFile() : m_name(wxFileName::CreateTempFileName(_T("wxlfs-"))) { }
75 ~TmpFile() { if (!m_name
.empty()) wxRemoveFile(m_name
); }
79 CPPUNIT_ASSERT(!tmpfile
.m_name
.empty());
82 bool fourGigLimit
= false;
86 wxString
n(getName().c_str(), *wxConvCurrent
);
87 wxLogInfo(n
+ _T(": No large file support, testing up to 2GB only"));
89 else if (IsFAT(tmpfile
.m_name
)) {
91 wxString
n(getName().c_str(), *wxConvCurrent
);
92 wxLogInfo(n
+ _T(": FAT volumes are limited to 4GB files"));
95 // size of the test blocks
96 const size_t size
= 0x40;
103 memset(upto2Gig
, 'A', size
);
104 memset(past2Gig
, 'B', size
);
105 memset(upto4Gig
, 'X', size
);
106 memset(past4Gig
, 'Y', size
);
110 // write a large file
112 auto_ptr
<wxOutputStream
> out(MakeOutStream(tmpfile
.m_name
));
114 // write 'A's at [ 0x7fffffbf, 0x7fffffff [
115 pos
= 0x7fffffff - size
;
116 CPPUNIT_ASSERT(out
->SeekO(pos
) == pos
);
117 CPPUNIT_ASSERT(out
->Write(upto2Gig
, size
).LastWrite() == size
);
121 // write 'B's at [ 0x7fffffff, 0x8000003f [
122 CPPUNIT_ASSERT(out
->Write(past2Gig
, size
).LastWrite() == size
);
124 CPPUNIT_ASSERT(out
->TellO() == pos
);
126 // write 'X's at [ 0xffffffbf, 0xffffffff [
127 pos
= 0xffffffff - size
;
128 CPPUNIT_ASSERT(out
->SeekO(pos
) == pos
);
129 CPPUNIT_ASSERT(out
->Write(upto4Gig
, size
).LastWrite() == size
);
133 // write 'Y's at [ 0xffffffff, 0x10000003f [
134 CPPUNIT_ASSERT(out
->Write(past4Gig
, size
).LastWrite() == size
);
139 // check the file seems to be the right length
140 CPPUNIT_ASSERT(out
->TellO() == pos
);
141 CPPUNIT_ASSERT(out
->GetLength() == pos
);
144 // read the large file back
146 auto_ptr
<wxInputStream
> in(MakeInStream(tmpfile
.m_name
));
150 CPPUNIT_ASSERT(in
->GetLength() == pos
);
154 CPPUNIT_ASSERT(in
->GetLength() > pos
);
156 // read back the 'Y's at [ 0xffffffff, 0x10000003f [
157 CPPUNIT_ASSERT(in
->SeekI(pos
) == pos
);
158 CPPUNIT_ASSERT(in
->Read(buf
, size
).LastRead() == size
);
159 CPPUNIT_ASSERT(memcmp(buf
, past4Gig
, size
) == 0);
161 CPPUNIT_ASSERT(in
->TellI() == in
->GetLength());
164 // read back the 'X's at [ 0xffffffbf, 0xffffffff [
166 CPPUNIT_ASSERT(in
->SeekI(pos
) == pos
);
167 CPPUNIT_ASSERT(in
->Read(buf
, size
).LastRead() == size
);
168 CPPUNIT_ASSERT(memcmp(buf
, upto4Gig
, size
) == 0);
170 CPPUNIT_ASSERT(in
->TellI() == pos
);
172 // read back the 'B's at [ 0x7fffffff, 0x8000003f [
174 CPPUNIT_ASSERT(in
->SeekI(pos
) == pos
);
175 CPPUNIT_ASSERT(in
->Read(buf
, size
).LastRead() == size
);
176 CPPUNIT_ASSERT(memcmp(buf
, past2Gig
, size
) == 0);
179 CPPUNIT_ASSERT(in
->GetLength() == 0x7fffffff);
183 // read back the 'A's at [ 0x7fffffbf, 0x7fffffff [
185 CPPUNIT_ASSERT(in
->SeekI(pos
) == pos
);
186 CPPUNIT_ASSERT(in
->Read(buf
, size
).LastRead() == size
);
187 CPPUNIT_ASSERT(memcmp(buf
, upto2Gig
, size
) == 0);
189 CPPUNIT_ASSERT(in
->TellI() == pos
);
194 ///////////////////////////////////////////////////////////////////////////////
197 class LargeFileTest_wxFile
: public LargeFileTest
200 LargeFileTest_wxFile() : LargeFileTest("wxFile streams") { }
203 wxInputStream
*MakeInStream(const wxString
& name
) const;
204 wxOutputStream
*MakeOutStream(const wxString
& name
) const;
205 bool HasLFS() const { return (wxFileOffset
)0xffffffff > 0; }
208 wxInputStream
*LargeFileTest_wxFile::MakeInStream(const wxString
& name
) const
210 auto_ptr
<wxFileInputStream
> in(new wxFileInputStream(name
));
211 CPPUNIT_ASSERT(in
->Ok());
215 wxOutputStream
*LargeFileTest_wxFile::MakeOutStream(const wxString
& name
) const
217 wxFile
file(name
, wxFile::write
);
218 CPPUNIT_ASSERT(file
.IsOpened());
221 MakeSparse(name
, fd
);
222 return new wxFileOutputStream(fd
);
226 ///////////////////////////////////////////////////////////////////////////////
229 class LargeFileTest_wxFFile
: public LargeFileTest
232 LargeFileTest_wxFFile() : LargeFileTest("wxFFile streams") { }
235 wxInputStream
*MakeInStream(const wxString
& name
) const;
236 wxOutputStream
*MakeOutStream(const wxString
& name
) const;
240 wxInputStream
*LargeFileTest_wxFFile::MakeInStream(const wxString
& name
) const
242 auto_ptr
<wxFFileInputStream
> in(new wxFFileInputStream(name
));
243 CPPUNIT_ASSERT(in
->Ok());
247 wxOutputStream
*LargeFileTest_wxFFile::MakeOutStream(const wxString
& name
) const
249 wxFFile
file(name
, _T("w"));
250 CPPUNIT_ASSERT(file
.IsOpened());
251 FILE *fp
= file
.fp();
253 MakeSparse(name
, fileno(fp
));
254 return new wxFFileOutputStream(fp
);
257 bool LargeFileTest_wxFFile::HasLFS() const
260 return (wxFileOffset
)0xffffffff > 0;
267 ///////////////////////////////////////////////////////////////////////////////
270 class largeFile
: public CppUnit::TestSuite
273 largeFile() : CppUnit::TestSuite("largeFile") { }
275 static CppUnit::Test
*suite();
278 CppUnit::Test
*largeFile::suite()
280 largeFile
*suite
= new largeFile
;
282 suite
->addTest(new LargeFileTest_wxFile
);
283 suite
->addTest(new LargeFileTest_wxFFile
);
289 ///////////////////////////////////////////////////////////////////////////////
290 // Implement the helpers
292 // Ideally these tests will be part of the default suite so that regressions
293 // are picked up. However this is only possible when sparse files are
294 // supported otherwise the tests require too much disk space.
296 // On unix, most filesystems support sparse files, though not all. So for now
297 // I'm not assuming sparse file support on unix. On Windows it's possible to
298 // test, and sparse files should be available on Win 5+ with NTFS.
302 #ifndef FILE_SUPPORTS_SPARSE_FILES
303 #define FILE_SUPPORTS_SPARSE_FILES 0x00000040
306 #ifndef FSCTL_SET_SPARSE
308 # ifndef FILE_SPECIAL_ACCESS
309 # define FILE_SPECIAL_ACCESS FILE_ANY_ACCESS
311 # define FSCTL_SET_SPARSE CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 49, \
312 METHOD_BUFFERED, FILE_SPECIAL_ACCESS)
315 static DWORD volumeFlags
;
316 static wxChar volumeType
[64];
317 static bool volumeInfoInit
;
319 void GetVolumeInfo(const wxString
& path
)
321 // extract the volume 'C:\' or '\\tooter\share\' from the path
324 if (path
.substr(1, 2) == _T(":\\")) {
325 vol
= path
.substr(0, 3);
327 if (path
.substr(0, 2) == _T("\\\\")) {
328 size_t i
= path
.find(_T('\\'), 2);
330 if (i
!= wxString::npos
&& i
> 2) {
331 size_t j
= path
.find(_T('\\'), ++i
);
334 vol
= path
.substr(0, j
) + _T("\\");
339 // NULL means the current volume
340 const wxChar
*pVol
= vol
.empty() ? (const wxChar
*)NULL
343 if (!::GetVolumeInformation(pVol
, NULL
, 0, NULL
, NULL
,
346 WXSIZEOF(volumeType
)))
347 wxLogSysError(_T("GetVolumeInformation() failed"));
349 volumeInfoInit
= true;
352 bool IsFAT(const wxString
& path
)
356 return wxString(volumeType
).Upper().find(_T("FAT")) != wxString::npos
;
359 void MakeSparse(const wxString
& path
, int fd
)
366 if ((volumeFlags
& FILE_SUPPORTS_SPARSE_FILES
) != 0)
367 if (!::DeviceIoControl((HANDLE
)_get_osfhandle(fd
),
369 NULL
, 0, NULL
, 0, &cb
, NULL
))
370 volumeFlags
&= ~FILE_SUPPORTS_SPARSE_FILES
;
373 CppUnit::Test
* GetlargeFileSuite()
375 if (!volumeInfoInit
) {
377 wxString path
= wxFileName::CreateTempFileName(_T("wxlfs-"), &file
);
378 MakeSparse(path
, file
.fd());
382 if ((volumeFlags
& FILE_SUPPORTS_SPARSE_FILES
) != 0)
383 return largeFile::suite();
390 bool IsFAT(const wxString
& WXUNUSED(path
)) { return false; }
391 void MakeSparse(const wxString
& WXUNUSED(path
), int WXUNUSED(fd
)) { }
393 CppUnit::Test
* GetlargeFileSuite() { return NULL
; }
397 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(largeFile
, "largeFile");
398 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(largeFile
, "Streams.largeFile");