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: wxWindows 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 will also be registered in the
20 // Streams subsuite so that they run by default.
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"
41 // 'nonstandard extension used : nameless struct/union' occurs inside
43 #pragma warning(disable:4201)
47 #pragma warning(default:4201)
52 #define fileno _fileno
58 ///////////////////////////////////////////////////////////////////////////////
61 bool IsFAT(const wxString
& path
);
62 void MakeSparse(const wxString
& path
, int fd
);
65 ///////////////////////////////////////////////////////////////////////////////
66 // Base class for the test cases - common code
68 class LargeFileTest
: public CppUnit::TestCase
71 LargeFileTest(std::string name
) : CppUnit::TestCase(name
) { }
72 virtual ~LargeFileTest() { }
77 virtual wxInputStream
*MakeInStream(const wxString
& name
) const = 0;
78 virtual wxOutputStream
*MakeOutStream(const wxString
& name
) const = 0;
79 virtual bool HasLFS() const = 0;
82 void LargeFileTest::runTest()
84 // self deleting temp file
86 TmpFile() : m_name(wxFileName::CreateTempFileName(wxT("wxlfs-"))) { }
87 ~TmpFile() { if (!m_name
.empty()) wxRemoveFile(m_name
); }
91 CPPUNIT_ASSERT(!tmpfile
.m_name
.empty());
94 bool fourGigLimit
= false;
98 wxString
n(getName().c_str(), *wxConvCurrent
);
99 wxLogInfo(n
+ wxT(": No large file support, testing up to 2GB only"));
101 else if (IsFAT(tmpfile
.m_name
)) {
103 wxString
n(getName().c_str(), *wxConvCurrent
);
104 wxLogInfo(n
+ wxT(": FAT volumes are limited to 4GB files"));
107 // size of the test blocks
108 const size_t size
= 0x40;
115 memset(upto2Gig
, 'A', size
);
116 memset(past2Gig
, 'B', size
);
117 memset(upto4Gig
, 'X', size
);
118 memset(past4Gig
, 'Y', size
);
122 // write a large file
124 auto_ptr
<wxOutputStream
> out(MakeOutStream(tmpfile
.m_name
));
126 // write 'A's at [ 0x7fffffbf, 0x7fffffff [
127 pos
= 0x7fffffff - size
;
128 CPPUNIT_ASSERT(out
->SeekO(pos
) == pos
);
129 CPPUNIT_ASSERT(out
->Write(upto2Gig
, size
).LastWrite() == size
);
133 // write 'B's at [ 0x7fffffff, 0x8000003f [
134 CPPUNIT_ASSERT(out
->Write(past2Gig
, size
).LastWrite() == size
);
136 CPPUNIT_ASSERT(out
->TellO() == pos
);
138 // write 'X's at [ 0xffffffbf, 0xffffffff [
139 pos
= 0xffffffff - size
;
140 CPPUNIT_ASSERT(out
->SeekO(pos
) == pos
);
141 CPPUNIT_ASSERT(out
->Write(upto4Gig
, size
).LastWrite() == size
);
145 // write 'Y's at [ 0xffffffff, 0x10000003f [
146 CPPUNIT_ASSERT(out
->Write(past4Gig
, size
).LastWrite() == size
);
151 // check the file seems to be the right length
152 CPPUNIT_ASSERT(out
->TellO() == pos
);
153 CPPUNIT_ASSERT(out
->GetLength() == pos
);
156 // read the large file back
158 auto_ptr
<wxInputStream
> in(MakeInStream(tmpfile
.m_name
));
162 CPPUNIT_ASSERT(in
->GetLength() == pos
);
166 CPPUNIT_ASSERT(in
->GetLength() > pos
);
168 // read back the 'Y's at [ 0xffffffff, 0x10000003f [
169 CPPUNIT_ASSERT(in
->SeekI(pos
) == pos
);
170 CPPUNIT_ASSERT(in
->Read(buf
, size
).LastRead() == size
);
171 CPPUNIT_ASSERT(memcmp(buf
, past4Gig
, size
) == 0);
173 CPPUNIT_ASSERT(in
->TellI() == in
->GetLength());
176 // read back the 'X's at [ 0xffffffbf, 0xffffffff [
178 CPPUNIT_ASSERT(in
->SeekI(pos
) == pos
);
179 CPPUNIT_ASSERT(in
->Read(buf
, size
).LastRead() == size
);
180 CPPUNIT_ASSERT(memcmp(buf
, upto4Gig
, size
) == 0);
182 CPPUNIT_ASSERT(in
->TellI() == pos
);
184 // read back the 'B's at [ 0x7fffffff, 0x8000003f [
186 CPPUNIT_ASSERT(in
->SeekI(pos
) == pos
);
187 CPPUNIT_ASSERT(in
->Read(buf
, size
).LastRead() == size
);
188 CPPUNIT_ASSERT(memcmp(buf
, past2Gig
, size
) == 0);
191 CPPUNIT_ASSERT(in
->GetLength() == 0x7fffffff);
195 // read back the 'A's at [ 0x7fffffbf, 0x7fffffff [
197 CPPUNIT_ASSERT(in
->SeekI(pos
) == pos
);
198 CPPUNIT_ASSERT(in
->Read(buf
, size
).LastRead() == size
);
199 CPPUNIT_ASSERT(memcmp(buf
, upto2Gig
, size
) == 0);
201 CPPUNIT_ASSERT(in
->TellI() == pos
);
206 ///////////////////////////////////////////////////////////////////////////////
209 class LargeFileTest_wxFile
: public LargeFileTest
212 LargeFileTest_wxFile() : LargeFileTest("wxFile streams") { }
215 wxInputStream
*MakeInStream(const wxString
& name
) const;
216 wxOutputStream
*MakeOutStream(const wxString
& name
) const;
217 bool HasLFS() const { return (wxFileOffset
)0xffffffff > 0; }
220 wxInputStream
*LargeFileTest_wxFile::MakeInStream(const wxString
& name
) const
222 auto_ptr
<wxFileInputStream
> in(new wxFileInputStream(name
));
223 CPPUNIT_ASSERT(in
->IsOk());
227 wxOutputStream
*LargeFileTest_wxFile::MakeOutStream(const wxString
& name
) const
229 wxFile
file(name
, wxFile::write
);
230 CPPUNIT_ASSERT(file
.IsOpened());
233 MakeSparse(name
, fd
);
234 return new wxFileOutputStream(fd
);
238 ///////////////////////////////////////////////////////////////////////////////
241 class LargeFileTest_wxFFile
: public LargeFileTest
244 LargeFileTest_wxFFile() : LargeFileTest("wxFFile streams") { }
247 wxInputStream
*MakeInStream(const wxString
& name
) const;
248 wxOutputStream
*MakeOutStream(const wxString
& name
) const;
252 wxInputStream
*LargeFileTest_wxFFile::MakeInStream(const wxString
& name
) const
254 auto_ptr
<wxFFileInputStream
> in(new wxFFileInputStream(name
));
255 CPPUNIT_ASSERT(in
->IsOk());
259 wxOutputStream
*LargeFileTest_wxFFile::MakeOutStream(const wxString
& name
) const
261 wxFFile
file(name
, wxT("w"));
262 CPPUNIT_ASSERT(file
.IsOpened());
263 FILE *fp
= file
.fp();
265 MakeSparse(name
, fileno(fp
));
266 return new wxFFileOutputStream(fp
);
269 bool LargeFileTest_wxFFile::HasLFS() const
271 #ifdef wxHAS_LARGE_FFILES
279 ///////////////////////////////////////////////////////////////////////////////
282 class largeFile
: public CppUnit::TestSuite
285 largeFile() : CppUnit::TestSuite("largeFile") { }
287 static CppUnit::Test
*suite();
290 CppUnit::Test
*largeFile::suite()
292 largeFile
*suite
= new largeFile
;
294 suite
->addTest(new LargeFileTest_wxFile
);
295 suite
->addTest(new LargeFileTest_wxFFile
);
301 ///////////////////////////////////////////////////////////////////////////////
302 // Implement the helpers
304 // Ideally these tests will be part of the default suite so that regressions
305 // are picked up. However this is only possible when sparse files are
306 // supported otherwise the tests require too much disk space.
310 #ifndef FILE_SUPPORTS_SPARSE_FILES
311 #define FILE_SUPPORTS_SPARSE_FILES 0x00000040
314 #ifndef FSCTL_SET_SPARSE
316 # ifndef FILE_SPECIAL_ACCESS
317 # define FILE_SPECIAL_ACCESS FILE_ANY_ACCESS
319 # define FSCTL_SET_SPARSE CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 49, \
320 METHOD_BUFFERED, FILE_SPECIAL_ACCESS)
323 static DWORD volumeFlags
;
324 static wxChar volumeType
[64];
325 static bool volumeInfoInit
;
327 void GetVolumeInfo(const wxString
& path
)
329 // extract the volume 'C:\' or '\\tooter\share\' from the path
332 if (path
.substr(1, 2) == wxT(":\\")) {
333 vol
= path
.substr(0, 3);
335 if (path
.substr(0, 2) == wxT("\\\\")) {
336 size_t i
= path
.find(wxT('\\'), 2);
338 if (i
!= wxString::npos
&& i
> 2) {
339 size_t j
= path
.find(wxT('\\'), ++i
);
342 vol
= path
.substr(0, j
) + wxT("\\");
347 // NULL means the current volume
348 const wxChar
*pVol
= vol
.empty() ? (const wxChar
*)NULL
351 if (!::GetVolumeInformation(pVol
, NULL
, 0, NULL
, NULL
,
354 WXSIZEOF(volumeType
)))
356 wxLogSysError(wxT("GetVolumeInformation() failed"));
359 volumeInfoInit
= true;
362 bool IsFAT(const wxString
& path
)
366 return wxString(volumeType
).Upper().find(wxT("FAT")) != wxString::npos
;
369 void MakeSparse(const wxString
& path
, int fd
)
376 if ((volumeFlags
& FILE_SUPPORTS_SPARSE_FILES
) != 0)
377 if (!::DeviceIoControl((HANDLE
)_get_osfhandle(fd
),
379 NULL
, 0, NULL
, 0, &cb
, NULL
))
380 volumeFlags
&= ~FILE_SUPPORTS_SPARSE_FILES
;
383 // return the suite if sparse files are supported, otherwise return NULL
385 CppUnit::Test
* GetlargeFileSuite()
387 if (!volumeInfoInit
) {
391 path
= wxFileName::CreateTempFileName(wxT("wxlfs-"), &file
);
392 MakeSparse(path
, file
.fd());
397 if ((volumeFlags
& FILE_SUPPORTS_SPARSE_FILES
) != 0)
398 return largeFile::suite();
405 bool IsFAT(const wxString
& WXUNUSED(path
)) { return false; }
406 void MakeSparse(const wxString
& WXUNUSED(path
), int WXUNUSED(fd
)) { }
408 // return the suite if sparse files are supported, otherwise return NULL
410 CppUnit::Test
* GetlargeFileSuite()
413 struct stat st1
, st2
;
414 memset(&st1
, 0, sizeof(st1
));
415 memset(&st2
, 0, sizeof(st2
));
419 path
= wxFileName::CreateTempFileName(wxT("wxlfs-"), &file
);
421 fstat(file
.fd(), &st1
);
422 file
.Seek(st1
.st_blksize
);
424 fstat(file
.fd(), &st1
);
428 fstat(file
.fd(), &st2
);
433 if (st1
.st_blocks
!= st2
.st_blocks
)
434 return largeFile::suite();
441 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(largeFile
, "largeFile");
442 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(largeFile
, "Streams.largeFile");