]> git.saurik.com Git - wxWidgets.git/blame - tests/streams/largefile.cpp
Suppress harmless MSVC warnings about int to bool conversions.
[wxWidgets.git] / tests / streams / largefile.cpp
CommitLineData
48714f74
RN
1///////////////////////////////////////////////////////////////////////////////
2// Name: tests/streams/largefile.cpp
3// Purpose: Tests for large file support
4// Author: Mike Wetherell
5// RCS-ID: $Id$
6// Copyright: (c) 2004 Mike Wetherell
526954c5 7// Licence: wxWindows licence
48714f74
RN
8///////////////////////////////////////////////////////////////////////////////
9
10//
086d7f31 11// We're interested in what happens around offsets 0x7fffffff and 0xffffffff
48714f74
RN
12// so the test writes a small chunk of test data just before and just after
13// these offsets, then reads them back.
14//
15// The tests can be run with:
16//
17// test --verbose largeFile
18//
9e477451
MW
19// On systems supporting sparse files they will also be registered in the
20// Streams subsuite so that they run by default.
48714f74
RN
21//
22
23// For compilers that support precompilation, includes "wx/wx.h".
24#include "testprec.h"
25
26#ifdef __BORLANDC__
27 #pragma hdrstop
28#endif
29
30// for all others, include the necessary headers
31#ifndef WX_PRECOMP
32 #include "wx/wx.h"
33#endif
34
35#include "wx/filename.h"
36#include "wx/wfstream.h"
37
38#ifdef __WXMSW__
845e4f9b
VZ
39 #include "wx/msw/wrapwin.h"
40 #ifdef __VISUALC__
41 // 'nonstandard extension used : nameless struct/union' occurs inside
42 // winioctl.h
43 #pragma warning(disable:4201)
44 #endif
45 #include <winioctl.h>
46 #ifdef __VISUALC__
47 #pragma warning(default:4201)
48 #endif
48714f74
RN
49#endif
50
d4b17832
VZ
51#ifdef __VISUALC__
52 #define fileno _fileno
53#endif
54
48714f74
RN
55using std::auto_ptr;
56
57
58///////////////////////////////////////////////////////////////////////////////
59// Helpers
60
61bool IsFAT(const wxString& path);
62void MakeSparse(const wxString& path, int fd);
63
64
65///////////////////////////////////////////////////////////////////////////////
66// Base class for the test cases - common code
67
68class LargeFileTest : public CppUnit::TestCase
69{
70public:
71 LargeFileTest(std::string name) : CppUnit::TestCase(name) { }
72 virtual ~LargeFileTest() { }
73
74protected:
75 void runTest();
76
77 virtual wxInputStream *MakeInStream(const wxString& name) const = 0;
78 virtual wxOutputStream *MakeOutStream(const wxString& name) const = 0;
79 virtual bool HasLFS() const = 0;
80};
81
82void LargeFileTest::runTest()
83{
84 // self deleting temp file
85 struct TmpFile {
9a83f860 86 TmpFile() : m_name(wxFileName::CreateTempFileName(wxT("wxlfs-"))) { }
48714f74
RN
87 ~TmpFile() { if (!m_name.empty()) wxRemoveFile(m_name); }
88 wxString m_name;
89 } tmpfile;
90
91 CPPUNIT_ASSERT(!tmpfile.m_name.empty());
92
93 bool haveLFS = true;
94 bool fourGigLimit = false;
95
96 if (!HasLFS()) {
97 haveLFS = false;
98 wxString n(getName().c_str(), *wxConvCurrent);
9a83f860 99 wxLogInfo(n + wxT(": No large file support, testing up to 2GB only"));
48714f74
RN
100 }
101 else if (IsFAT(tmpfile.m_name)) {
102 fourGigLimit = true;
103 wxString n(getName().c_str(), *wxConvCurrent);
9a83f860 104 wxLogInfo(n + wxT(": FAT volumes are limited to 4GB files"));
48714f74
RN
105 }
106
107 // size of the test blocks
108 const size_t size = 0x40;
109
110 // the test blocks
111 char upto2Gig[size];
112 char past2Gig[size];
113 char upto4Gig[size];
114 char past4Gig[size];
115 memset(upto2Gig, 'A', size);
116 memset(past2Gig, 'B', size);
117 memset(upto4Gig, 'X', size);
118 memset(past4Gig, 'Y', size);
119
120 wxFileOffset pos;
121
122 // write a large file
123 {
124 auto_ptr<wxOutputStream> out(MakeOutStream(tmpfile.m_name));
43b2d5e7 125
48714f74
RN
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);
130 pos += size;
131
132 if (haveLFS) {
133 // write 'B's at [ 0x7fffffff, 0x8000003f [
134 CPPUNIT_ASSERT(out->Write(past2Gig, size).LastWrite() == size);
135 pos += size;
136 CPPUNIT_ASSERT(out->TellO() == pos);
137
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);
142 pos += size;
143
144 if (!fourGigLimit) {
145 // write 'Y's at [ 0xffffffff, 0x10000003f [
146 CPPUNIT_ASSERT(out->Write(past4Gig, size).LastWrite() == size);
147 pos += size;
148 }
149 }
150
151 // check the file seems to be the right length
152 CPPUNIT_ASSERT(out->TellO() == pos);
153 CPPUNIT_ASSERT(out->GetLength() == pos);
154 }
155
156 // read the large file back
157 {
158 auto_ptr<wxInputStream> in(MakeInStream(tmpfile.m_name));
159 char buf[size];
160
161 if (haveLFS) {
162 CPPUNIT_ASSERT(in->GetLength() == pos);
163 pos = 0xffffffff;
164
165 if (!fourGigLimit) {
166 CPPUNIT_ASSERT(in->GetLength() > pos);
167
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);
172
173 CPPUNIT_ASSERT(in->TellI() == in->GetLength());
174 }
175
176 // read back the 'X's at [ 0xffffffbf, 0xffffffff [
177 pos -= size;
178 CPPUNIT_ASSERT(in->SeekI(pos) == pos);
179 CPPUNIT_ASSERT(in->Read(buf, size).LastRead() == size);
180 CPPUNIT_ASSERT(memcmp(buf, upto4Gig, size) == 0);
181 pos += size;
182 CPPUNIT_ASSERT(in->TellI() == pos);
183
184 // read back the 'B's at [ 0x7fffffff, 0x8000003f [
185 pos = 0x7fffffff;
186 CPPUNIT_ASSERT(in->SeekI(pos) == pos);
187 CPPUNIT_ASSERT(in->Read(buf, size).LastRead() == size);
188 CPPUNIT_ASSERT(memcmp(buf, past2Gig, size) == 0);
189 }
190 else {
191 CPPUNIT_ASSERT(in->GetLength() == 0x7fffffff);
192 pos = 0x7fffffff;
193 }
194
195 // read back the 'A's at [ 0x7fffffbf, 0x7fffffff [
196 pos -= size;
197 CPPUNIT_ASSERT(in->SeekI(pos) == pos);
198 CPPUNIT_ASSERT(in->Read(buf, size).LastRead() == size);
199 CPPUNIT_ASSERT(memcmp(buf, upto2Gig, size) == 0);
200 pos += size;
201 CPPUNIT_ASSERT(in->TellI() == pos);
202 }
203}
204
205
206///////////////////////////////////////////////////////////////////////////////
207// wxFile test case
208
209class LargeFileTest_wxFile : public LargeFileTest
210{
211public:
212 LargeFileTest_wxFile() : LargeFileTest("wxFile streams") { }
213
214protected:
215 wxInputStream *MakeInStream(const wxString& name) const;
216 wxOutputStream *MakeOutStream(const wxString& name) const;
217 bool HasLFS() const { return (wxFileOffset)0xffffffff > 0; }
218};
219
220wxInputStream *LargeFileTest_wxFile::MakeInStream(const wxString& name) const
221{
222 auto_ptr<wxFileInputStream> in(new wxFileInputStream(name));
a1b806b9 223 CPPUNIT_ASSERT(in->IsOk());
48714f74
RN
224 return in.release();
225}
226
227wxOutputStream *LargeFileTest_wxFile::MakeOutStream(const wxString& name) const
228{
229 wxFile file(name, wxFile::write);
230 CPPUNIT_ASSERT(file.IsOpened());
231 int fd = file.fd();
232 file.Detach();
233 MakeSparse(name, fd);
234 return new wxFileOutputStream(fd);
235}
236
237
238///////////////////////////////////////////////////////////////////////////////
239// wxFFile test case
240
241class LargeFileTest_wxFFile : public LargeFileTest
242{
243public:
244 LargeFileTest_wxFFile() : LargeFileTest("wxFFile streams") { }
245
246protected:
247 wxInputStream *MakeInStream(const wxString& name) const;
248 wxOutputStream *MakeOutStream(const wxString& name) const;
249 bool HasLFS() const;
250};
251
252wxInputStream *LargeFileTest_wxFFile::MakeInStream(const wxString& name) const
253{
254 auto_ptr<wxFFileInputStream> in(new wxFFileInputStream(name));
a1b806b9 255 CPPUNIT_ASSERT(in->IsOk());
48714f74
RN
256 return in.release();
257}
258
259wxOutputStream *LargeFileTest_wxFFile::MakeOutStream(const wxString& name) const
260{
9a83f860 261 wxFFile file(name, wxT("w"));
48714f74
RN
262 CPPUNIT_ASSERT(file.IsOpened());
263 FILE *fp = file.fp();
264 file.Detach();
265 MakeSparse(name, fileno(fp));
266 return new wxFFileOutputStream(fp);
267}
268
269bool LargeFileTest_wxFFile::HasLFS() const
270{
224d978f
MW
271#ifdef wxHAS_LARGE_FFILES
272 return true;
48714f74
RN
273#else
274 return false;
275#endif
276}
277
278
279///////////////////////////////////////////////////////////////////////////////
280// The suite
281
282class largeFile : public CppUnit::TestSuite
283{
284public:
285 largeFile() : CppUnit::TestSuite("largeFile") { }
286
287 static CppUnit::Test *suite();
288};
289
290CppUnit::Test *largeFile::suite()
291{
292 largeFile *suite = new largeFile;
293
294 suite->addTest(new LargeFileTest_wxFile);
295 suite->addTest(new LargeFileTest_wxFFile);
296
297 return suite;
298}
299
300
301///////////////////////////////////////////////////////////////////////////////
302// Implement the helpers
303//
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.
48714f74
RN
307
308#ifdef __WXMSW__
309
310#ifndef FILE_SUPPORTS_SPARSE_FILES
311#define FILE_SUPPORTS_SPARSE_FILES 0x00000040
312#endif
313
314#ifndef FSCTL_SET_SPARSE
315
43b2d5e7 316# ifndef FILE_SPECIAL_ACCESS
48714f74
RN
317# define FILE_SPECIAL_ACCESS FILE_ANY_ACCESS
318# endif
319# define FSCTL_SET_SPARSE CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 49, \
320 METHOD_BUFFERED, FILE_SPECIAL_ACCESS)
321#endif
322
323static DWORD volumeFlags;
324static wxChar volumeType[64];
325static bool volumeInfoInit;
326
327void GetVolumeInfo(const wxString& path)
328{
329 // extract the volume 'C:\' or '\\tooter\share\' from the path
330 wxString vol;
43b2d5e7 331
9a83f860 332 if (path.substr(1, 2) == wxT(":\\")) {
48714f74
RN
333 vol = path.substr(0, 3);
334 } else {
9a83f860
VZ
335 if (path.substr(0, 2) == wxT("\\\\")) {
336 size_t i = path.find(wxT('\\'), 2);
48714f74
RN
337
338 if (i != wxString::npos && i > 2) {
9a83f860 339 size_t j = path.find(wxT('\\'), ++i);
48714f74
RN
340
341 if (j != i)
9a83f860 342 vol = path.substr(0, j) + wxT("\\");
48714f74
RN
343 }
344 }
345 }
346
347 // NULL means the current volume
90adb904 348 const wxChar *pVol = vol.empty() ? (const wxChar *)NULL
48eba629 349 : vol.c_str();
48714f74
RN
350
351 if (!::GetVolumeInformation(pVol, NULL, 0, NULL, NULL,
43b2d5e7 352 &volumeFlags,
48714f74
RN
353 volumeType,
354 WXSIZEOF(volumeType)))
43b2d5e7 355 {
9a83f860 356 wxLogSysError(wxT("GetVolumeInformation() failed"));
43b2d5e7 357 }
48714f74
RN
358
359 volumeInfoInit = true;
360}
361
362bool IsFAT(const wxString& path)
363{
364 if (!volumeInfoInit)
365 GetVolumeInfo(path);
9a83f860 366 return wxString(volumeType).Upper().find(wxT("FAT")) != wxString::npos;
48714f74
RN
367}
368
369void MakeSparse(const wxString& path, int fd)
370{
371 DWORD cb;
372
373 if (!volumeInfoInit)
374 GetVolumeInfo(path);
43b2d5e7 375
48714f74
RN
376 if ((volumeFlags & FILE_SUPPORTS_SPARSE_FILES) != 0)
377 if (!::DeviceIoControl((HANDLE)_get_osfhandle(fd),
378 FSCTL_SET_SPARSE,
379 NULL, 0, NULL, 0, &cb, NULL))
086d7f31 380 volumeFlags &= ~FILE_SUPPORTS_SPARSE_FILES;
48714f74
RN
381}
382
9e477451
MW
383// return the suite if sparse files are supported, otherwise return NULL
384//
48714f74
RN
385CppUnit::Test* GetlargeFileSuite()
386{
387 if (!volumeInfoInit) {
9e477451
MW
388 wxString path;
389 {
390 wxFile file;
391 path = wxFileName::CreateTempFileName(wxT("wxlfs-"), &file);
392 MakeSparse(path, file.fd());
393 }
48714f74
RN
394 wxRemoveFile(path);
395 }
396
397 if ((volumeFlags & FILE_SUPPORTS_SPARSE_FILES) != 0)
398 return largeFile::suite();
399 else
400 return NULL;
401}
402
403#else // __WXMSW__
404
405bool IsFAT(const wxString& WXUNUSED(path)) { return false; }
406void MakeSparse(const wxString& WXUNUSED(path), int WXUNUSED(fd)) { }
407
9e477451
MW
408// return the suite if sparse files are supported, otherwise return NULL
409//
410CppUnit::Test* GetlargeFileSuite()
411{
412 wxString path;
413 struct stat st1, st2;
414 memset(&st1, 0, sizeof(st1));
415 memset(&st2, 0, sizeof(st2));
416
417 {
418 wxFile file;
419 path = wxFileName::CreateTempFileName(wxT("wxlfs-"), &file);
420
421 fstat(file.fd(), &st1);
422 file.Seek(st1.st_blksize);
423 file.Write("x", 1);
424 fstat(file.fd(), &st1);
425
426 file.Seek(0);
427 file.Write("x", 1);
428 fstat(file.fd(), &st2);
429 }
430
431 wxRemoveFile(path);
432
433 if (st1.st_blocks != st2.st_blocks)
434 return largeFile::suite();
435 else
436 return NULL;
437}
48714f74
RN
438
439#endif // __WXMSW__
440
441CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(largeFile, "largeFile");
442CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(largeFile, "Streams.largeFile");