1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: tests/streams/largefile.cpp
3 // Purpose: Tests for large file support
4 // Author: Mike Wetherell
5 // Copyright: (c) 2004 Mike Wetherell
6 // Licence: wxWindows licence
7 ///////////////////////////////////////////////////////////////////////////////
10 // We're interested in what happens around offsets 0x7fffffff and 0xffffffff
11 // so the test writes a small chunk of test data just before and just after
12 // these offsets, then reads them back.
14 // The tests can be run with:
16 // test --verbose largeFile
18 // On systems supporting sparse files they will also be registered in the
19 // Streams subsuite so that they run by default.
22 // For compilers that support precompilation, includes "wx/wx.h".
29 // for all others, include the necessary headers
34 #include "wx/filename.h"
35 #include "wx/wfstream.h"
38 #include "wx/msw/wrapwin.h"
40 // 'nonstandard extension used : nameless struct/union' occurs inside
42 #pragma warning(disable:4201)
46 #pragma warning(default:4201)
51 #define fileno _fileno
57 ///////////////////////////////////////////////////////////////////////////////
60 bool IsFAT(const wxString
& path
);
61 void MakeSparse(const wxString
& path
, int fd
);
64 ///////////////////////////////////////////////////////////////////////////////
65 // Base class for the test cases - common code
67 class LargeFileTest
: public CppUnit::TestCase
70 LargeFileTest(std::string name
) : CppUnit::TestCase(name
) { }
71 virtual ~LargeFileTest() { }
76 virtual wxInputStream
*MakeInStream(const wxString
& name
) const = 0;
77 virtual wxOutputStream
*MakeOutStream(const wxString
& name
) const = 0;
78 virtual bool HasLFS() const = 0;
81 void LargeFileTest::runTest()
83 // self deleting temp file
85 TmpFile() : m_name(wxFileName::CreateTempFileName(wxT("wxlfs-"))) { }
86 ~TmpFile() { if (!m_name
.empty()) wxRemoveFile(m_name
); }
90 CPPUNIT_ASSERT(!tmpfile
.m_name
.empty());
93 bool fourGigLimit
= false;
97 wxString
n(getName().c_str(), *wxConvCurrent
);
98 wxLogInfo(n
+ wxT(": No large file support, testing up to 2GB only"));
100 else if (IsFAT(tmpfile
.m_name
)) {
102 wxString
n(getName().c_str(), *wxConvCurrent
);
103 wxLogInfo(n
+ wxT(": FAT volumes are limited to 4GB files"));
106 // size of the test blocks
107 const size_t size
= 0x40;
114 memset(upto2Gig
, 'A', size
);
115 memset(past2Gig
, 'B', size
);
116 memset(upto4Gig
, 'X', size
);
117 memset(past4Gig
, 'Y', size
);
121 // write a large file
123 auto_ptr
<wxOutputStream
> out(MakeOutStream(tmpfile
.m_name
));
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
);
132 // write 'B's at [ 0x7fffffff, 0x8000003f [
133 CPPUNIT_ASSERT(out
->Write(past2Gig
, size
).LastWrite() == size
);
135 CPPUNIT_ASSERT(out
->TellO() == pos
);
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
);
144 // write 'Y's at [ 0xffffffff, 0x10000003f [
145 CPPUNIT_ASSERT(out
->Write(past4Gig
, size
).LastWrite() == size
);
150 // check the file seems to be the right length
151 CPPUNIT_ASSERT(out
->TellO() == pos
);
152 CPPUNIT_ASSERT(out
->GetLength() == pos
);
155 // read the large file back
157 auto_ptr
<wxInputStream
> in(MakeInStream(tmpfile
.m_name
));
161 CPPUNIT_ASSERT(in
->GetLength() == pos
);
165 CPPUNIT_ASSERT(in
->GetLength() > pos
);
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);
172 CPPUNIT_ASSERT(in
->TellI() == in
->GetLength());
175 // read back the 'X's at [ 0xffffffbf, 0xffffffff [
177 CPPUNIT_ASSERT(in
->SeekI(pos
) == pos
);
178 CPPUNIT_ASSERT(in
->Read(buf
, size
).LastRead() == size
);
179 CPPUNIT_ASSERT(memcmp(buf
, upto4Gig
, size
) == 0);
181 CPPUNIT_ASSERT(in
->TellI() == pos
);
183 // read back the 'B's at [ 0x7fffffff, 0x8000003f [
185 CPPUNIT_ASSERT(in
->SeekI(pos
) == pos
);
186 CPPUNIT_ASSERT(in
->Read(buf
, size
).LastRead() == size
);
187 CPPUNIT_ASSERT(memcmp(buf
, past2Gig
, size
) == 0);
190 CPPUNIT_ASSERT(in
->GetLength() == 0x7fffffff);
194 // read back the 'A's at [ 0x7fffffbf, 0x7fffffff [
196 CPPUNIT_ASSERT(in
->SeekI(pos
) == pos
);
197 CPPUNIT_ASSERT(in
->Read(buf
, size
).LastRead() == size
);
198 CPPUNIT_ASSERT(memcmp(buf
, upto2Gig
, size
) == 0);
200 CPPUNIT_ASSERT(in
->TellI() == pos
);
205 ///////////////////////////////////////////////////////////////////////////////
208 class LargeFileTest_wxFile
: public LargeFileTest
211 LargeFileTest_wxFile() : LargeFileTest("wxFile streams") { }
214 wxInputStream
*MakeInStream(const wxString
& name
) const;
215 wxOutputStream
*MakeOutStream(const wxString
& name
) const;
216 bool HasLFS() const { return (wxFileOffset
)0xffffffff > 0; }
219 wxInputStream
*LargeFileTest_wxFile::MakeInStream(const wxString
& name
) const
221 auto_ptr
<wxFileInputStream
> in(new wxFileInputStream(name
));
222 CPPUNIT_ASSERT(in
->IsOk());
226 wxOutputStream
*LargeFileTest_wxFile::MakeOutStream(const wxString
& name
) const
228 wxFile
file(name
, wxFile::write
);
229 CPPUNIT_ASSERT(file
.IsOpened());
232 MakeSparse(name
, fd
);
233 return new wxFileOutputStream(fd
);
237 ///////////////////////////////////////////////////////////////////////////////
240 class LargeFileTest_wxFFile
: public LargeFileTest
243 LargeFileTest_wxFFile() : LargeFileTest("wxFFile streams") { }
246 wxInputStream
*MakeInStream(const wxString
& name
) const;
247 wxOutputStream
*MakeOutStream(const wxString
& name
) const;
251 wxInputStream
*LargeFileTest_wxFFile::MakeInStream(const wxString
& name
) const
253 auto_ptr
<wxFFileInputStream
> in(new wxFFileInputStream(name
));
254 CPPUNIT_ASSERT(in
->IsOk());
258 wxOutputStream
*LargeFileTest_wxFFile::MakeOutStream(const wxString
& name
) const
260 wxFFile
file(name
, wxT("w"));
261 CPPUNIT_ASSERT(file
.IsOpened());
262 FILE *fp
= file
.fp();
264 MakeSparse(name
, fileno(fp
));
265 return new wxFFileOutputStream(fp
);
268 bool LargeFileTest_wxFFile::HasLFS() const
270 #ifdef wxHAS_LARGE_FFILES
278 ///////////////////////////////////////////////////////////////////////////////
281 class largeFile
: public CppUnit::TestSuite
284 largeFile() : CppUnit::TestSuite("largeFile") { }
286 static CppUnit::Test
*suite();
289 CppUnit::Test
*largeFile::suite()
291 largeFile
*suite
= new largeFile
;
293 suite
->addTest(new LargeFileTest_wxFile
);
294 suite
->addTest(new LargeFileTest_wxFFile
);
300 ///////////////////////////////////////////////////////////////////////////////
301 // Implement the helpers
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.
309 #ifndef FILE_SUPPORTS_SPARSE_FILES
310 #define FILE_SUPPORTS_SPARSE_FILES 0x00000040
313 #ifndef FSCTL_SET_SPARSE
315 # ifndef FILE_SPECIAL_ACCESS
316 # define FILE_SPECIAL_ACCESS FILE_ANY_ACCESS
318 # define FSCTL_SET_SPARSE CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 49, \
319 METHOD_BUFFERED, FILE_SPECIAL_ACCESS)
322 static DWORD volumeFlags
;
323 static wxChar volumeType
[64];
324 static bool volumeInfoInit
;
326 void GetVolumeInfo(const wxString
& path
)
328 // extract the volume 'C:\' or '\\tooter\share\' from the path
331 if (path
.substr(1, 2) == wxT(":\\")) {
332 vol
= path
.substr(0, 3);
334 if (path
.substr(0, 2) == wxT("\\\\")) {
335 size_t i
= path
.find(wxT('\\'), 2);
337 if (i
!= wxString::npos
&& i
> 2) {
338 size_t j
= path
.find(wxT('\\'), ++i
);
341 vol
= path
.substr(0, j
) + wxT("\\");
346 // NULL means the current volume
347 const wxChar
*pVol
= vol
.empty() ? (const wxChar
*)NULL
350 if (!::GetVolumeInformation(pVol
, NULL
, 0, NULL
, NULL
,
353 WXSIZEOF(volumeType
)))
355 wxLogSysError(wxT("GetVolumeInformation() failed"));
358 volumeInfoInit
= true;
361 bool IsFAT(const wxString
& path
)
365 return wxString(volumeType
).Upper().find(wxT("FAT")) != wxString::npos
;
368 void MakeSparse(const wxString
& path
, int fd
)
375 if ((volumeFlags
& FILE_SUPPORTS_SPARSE_FILES
) != 0)
376 if (!::DeviceIoControl((HANDLE
)_get_osfhandle(fd
),
378 NULL
, 0, NULL
, 0, &cb
, NULL
))
379 volumeFlags
&= ~FILE_SUPPORTS_SPARSE_FILES
;
382 // return the suite if sparse files are supported, otherwise return NULL
384 CppUnit::Test
* GetlargeFileSuite()
386 if (!volumeInfoInit
) {
390 path
= wxFileName::CreateTempFileName(wxT("wxlfs-"), &file
);
391 MakeSparse(path
, file
.fd());
396 if ((volumeFlags
& FILE_SUPPORTS_SPARSE_FILES
) != 0)
397 return largeFile::suite();
404 bool IsFAT(const wxString
& WXUNUSED(path
)) { return false; }
405 void MakeSparse(const wxString
& WXUNUSED(path
), int WXUNUSED(fd
)) { }
407 // return the suite if sparse files are supported, otherwise return NULL
409 CppUnit::Test
* GetlargeFileSuite()
412 struct stat st1
, st2
;
413 memset(&st1
, 0, sizeof(st1
));
414 memset(&st2
, 0, sizeof(st2
));
418 path
= wxFileName::CreateTempFileName(wxT("wxlfs-"), &file
);
420 fstat(file
.fd(), &st1
);
421 file
.Seek(st1
.st_blksize
);
423 fstat(file
.fd(), &st1
);
427 fstat(file
.fd(), &st2
);
432 if (st1
.st_blocks
!= st2
.st_blocks
)
433 return largeFile::suite();
438 #endif // __WINDOWS__
440 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(largeFile
, "largeFile");
441 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(largeFile
, "Streams.largeFile");