]> git.saurik.com Git - wxWidgets.git/blame - tests/streams/largefile.cpp
making sure min and max sizes can be overridden
[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
7// Licence: wxWidgets licence
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//
19// On systems supporting sparse files they may also be registered in the
20// Streams subsuite.
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__
8b686715
MW
39#include "wx/msw/wrapwin.h"
40#include <winioctl.h>
48714f74
RN
41#endif
42
43using std::auto_ptr;
44
45
46///////////////////////////////////////////////////////////////////////////////
47// Helpers
48
49bool IsFAT(const wxString& path);
50void MakeSparse(const wxString& path, int fd);
51
52
53///////////////////////////////////////////////////////////////////////////////
54// Base class for the test cases - common code
55
56class LargeFileTest : public CppUnit::TestCase
57{
58public:
59 LargeFileTest(std::string name) : CppUnit::TestCase(name) { }
60 virtual ~LargeFileTest() { }
61
62protected:
63 void runTest();
64
65 virtual wxInputStream *MakeInStream(const wxString& name) const = 0;
66 virtual wxOutputStream *MakeOutStream(const wxString& name) const = 0;
67 virtual bool HasLFS() const = 0;
68};
69
70void LargeFileTest::runTest()
71{
72 // self deleting temp file
73 struct TmpFile {
74 TmpFile() : m_name(wxFileName::CreateTempFileName(_T("wxlfs-"))) { }
75 ~TmpFile() { if (!m_name.empty()) wxRemoveFile(m_name); }
76 wxString m_name;
77 } tmpfile;
78
79 CPPUNIT_ASSERT(!tmpfile.m_name.empty());
80
81 bool haveLFS = true;
82 bool fourGigLimit = false;
83
84 if (!HasLFS()) {
85 haveLFS = false;
86 wxString n(getName().c_str(), *wxConvCurrent);
87 wxLogInfo(n + _T(": No large file support, testing up to 2GB only"));
88 }
89 else if (IsFAT(tmpfile.m_name)) {
90 fourGigLimit = true;
91 wxString n(getName().c_str(), *wxConvCurrent);
92 wxLogInfo(n + _T(": FAT volumes are limited to 4GB files"));
93 }
94
95 // size of the test blocks
96 const size_t size = 0x40;
97
98 // the test blocks
99 char upto2Gig[size];
100 char past2Gig[size];
101 char upto4Gig[size];
102 char past4Gig[size];
103 memset(upto2Gig, 'A', size);
104 memset(past2Gig, 'B', size);
105 memset(upto4Gig, 'X', size);
106 memset(past4Gig, 'Y', size);
107
108 wxFileOffset pos;
109
110 // write a large file
111 {
112 auto_ptr<wxOutputStream> out(MakeOutStream(tmpfile.m_name));
113
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);
118 pos += size;
119
120 if (haveLFS) {
121 // write 'B's at [ 0x7fffffff, 0x8000003f [
122 CPPUNIT_ASSERT(out->Write(past2Gig, size).LastWrite() == size);
123 pos += size;
124 CPPUNIT_ASSERT(out->TellO() == pos);
125
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);
130 pos += size;
131
132 if (!fourGigLimit) {
133 // write 'Y's at [ 0xffffffff, 0x10000003f [
134 CPPUNIT_ASSERT(out->Write(past4Gig, size).LastWrite() == size);
135 pos += size;
136 }
137 }
138
139 // check the file seems to be the right length
140 CPPUNIT_ASSERT(out->TellO() == pos);
141 CPPUNIT_ASSERT(out->GetLength() == pos);
142 }
143
144 // read the large file back
145 {
146 auto_ptr<wxInputStream> in(MakeInStream(tmpfile.m_name));
147 char buf[size];
148
149 if (haveLFS) {
150 CPPUNIT_ASSERT(in->GetLength() == pos);
151 pos = 0xffffffff;
152
153 if (!fourGigLimit) {
154 CPPUNIT_ASSERT(in->GetLength() > pos);
155
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);
160
161 CPPUNIT_ASSERT(in->TellI() == in->GetLength());
162 }
163
164 // read back the 'X's at [ 0xffffffbf, 0xffffffff [
165 pos -= size;
166 CPPUNIT_ASSERT(in->SeekI(pos) == pos);
167 CPPUNIT_ASSERT(in->Read(buf, size).LastRead() == size);
168 CPPUNIT_ASSERT(memcmp(buf, upto4Gig, size) == 0);
169 pos += size;
170 CPPUNIT_ASSERT(in->TellI() == pos);
171
172 // read back the 'B's at [ 0x7fffffff, 0x8000003f [
173 pos = 0x7fffffff;
174 CPPUNIT_ASSERT(in->SeekI(pos) == pos);
175 CPPUNIT_ASSERT(in->Read(buf, size).LastRead() == size);
176 CPPUNIT_ASSERT(memcmp(buf, past2Gig, size) == 0);
177 }
178 else {
179 CPPUNIT_ASSERT(in->GetLength() == 0x7fffffff);
180 pos = 0x7fffffff;
181 }
182
183 // read back the 'A's at [ 0x7fffffbf, 0x7fffffff [
184 pos -= size;
185 CPPUNIT_ASSERT(in->SeekI(pos) == pos);
186 CPPUNIT_ASSERT(in->Read(buf, size).LastRead() == size);
187 CPPUNIT_ASSERT(memcmp(buf, upto2Gig, size) == 0);
188 pos += size;
189 CPPUNIT_ASSERT(in->TellI() == pos);
190 }
191}
192
193
194///////////////////////////////////////////////////////////////////////////////
195// wxFile test case
196
197class LargeFileTest_wxFile : public LargeFileTest
198{
199public:
200 LargeFileTest_wxFile() : LargeFileTest("wxFile streams") { }
201
202protected:
203 wxInputStream *MakeInStream(const wxString& name) const;
204 wxOutputStream *MakeOutStream(const wxString& name) const;
205 bool HasLFS() const { return (wxFileOffset)0xffffffff > 0; }
206};
207
208wxInputStream *LargeFileTest_wxFile::MakeInStream(const wxString& name) const
209{
210 auto_ptr<wxFileInputStream> in(new wxFileInputStream(name));
211 CPPUNIT_ASSERT(in->Ok());
212 return in.release();
213}
214
215wxOutputStream *LargeFileTest_wxFile::MakeOutStream(const wxString& name) const
216{
217 wxFile file(name, wxFile::write);
218 CPPUNIT_ASSERT(file.IsOpened());
219 int fd = file.fd();
220 file.Detach();
221 MakeSparse(name, fd);
222 return new wxFileOutputStream(fd);
223}
224
225
226///////////////////////////////////////////////////////////////////////////////
227// wxFFile test case
228
229class LargeFileTest_wxFFile : public LargeFileTest
230{
231public:
232 LargeFileTest_wxFFile() : LargeFileTest("wxFFile streams") { }
233
234protected:
235 wxInputStream *MakeInStream(const wxString& name) const;
236 wxOutputStream *MakeOutStream(const wxString& name) const;
237 bool HasLFS() const;
238};
239
240wxInputStream *LargeFileTest_wxFFile::MakeInStream(const wxString& name) const
241{
242 auto_ptr<wxFFileInputStream> in(new wxFFileInputStream(name));
243 CPPUNIT_ASSERT(in->Ok());
244 return in.release();
245}
246
247wxOutputStream *LargeFileTest_wxFFile::MakeOutStream(const wxString& name) const
248{
249 wxFFile file(name, _T("w"));
250 CPPUNIT_ASSERT(file.IsOpened());
251 FILE *fp = file.fp();
252 file.Detach();
253 MakeSparse(name, fileno(fp));
254 return new wxFFileOutputStream(fp);
255}
256
257bool LargeFileTest_wxFFile::HasLFS() const
258{
0c094518 259#ifdef HAVE_FSEEKO
48714f74
RN
260 return (wxFileOffset)0xffffffff > 0;
261#else
262 return false;
263#endif
264}
265
266
267///////////////////////////////////////////////////////////////////////////////
268// The suite
269
270class largeFile : public CppUnit::TestSuite
271{
272public:
273 largeFile() : CppUnit::TestSuite("largeFile") { }
274
275 static CppUnit::Test *suite();
276};
277
278CppUnit::Test *largeFile::suite()
279{
280 largeFile *suite = new largeFile;
281
282 suite->addTest(new LargeFileTest_wxFile);
283 suite->addTest(new LargeFileTest_wxFFile);
284
285 return suite;
286}
287
288
289///////////////////////////////////////////////////////////////////////////////
290// Implement the helpers
291//
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.
295//
086d7f31
RN
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.
48714f74
RN
299
300#ifdef __WXMSW__
301
302#ifndef FILE_SUPPORTS_SPARSE_FILES
303#define FILE_SUPPORTS_SPARSE_FILES 0x00000040
304#endif
305
306#ifndef FSCTL_SET_SPARSE
307
308# ifndef FILE_SPECIAL_ACCESS
309# define FILE_SPECIAL_ACCESS FILE_ANY_ACCESS
310# endif
311# define FSCTL_SET_SPARSE CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 49, \
312 METHOD_BUFFERED, FILE_SPECIAL_ACCESS)
313#endif
314
315static DWORD volumeFlags;
316static wxChar volumeType[64];
317static bool volumeInfoInit;
318
319void GetVolumeInfo(const wxString& path)
320{
321 // extract the volume 'C:\' or '\\tooter\share\' from the path
322 wxString vol;
323
324 if (path.substr(1, 2) == _T(":\\")) {
325 vol = path.substr(0, 3);
326 } else {
327 if (path.substr(0, 2) == _T("\\\\")) {
328 size_t i = path.find(_T('\\'), 2);
329
330 if (i != wxString::npos && i > 2) {
331 size_t j = path.find(_T('\\'), ++i);
332
333 if (j != i)
334 vol = path.substr(0, j) + _T("\\");
335 }
336 }
337 }
338
339 // NULL means the current volume
90adb904 340 const wxChar *pVol = vol.empty() ? (const wxChar *)NULL
48eba629 341 : vol.c_str();
48714f74
RN
342
343 if (!::GetVolumeInformation(pVol, NULL, 0, NULL, NULL,
344 &volumeFlags,
345 volumeType,
346 WXSIZEOF(volumeType)))
347 wxLogSysError(_T("GetVolumeInformation() failed"));
348
349 volumeInfoInit = true;
350}
351
352bool IsFAT(const wxString& path)
353{
354 if (!volumeInfoInit)
355 GetVolumeInfo(path);
356 return wxString(volumeType).Upper().find(_T("FAT")) != wxString::npos;
357}
358
359void MakeSparse(const wxString& path, int fd)
360{
361 DWORD cb;
362
363 if (!volumeInfoInit)
364 GetVolumeInfo(path);
365
366 if ((volumeFlags & FILE_SUPPORTS_SPARSE_FILES) != 0)
367 if (!::DeviceIoControl((HANDLE)_get_osfhandle(fd),
368 FSCTL_SET_SPARSE,
369 NULL, 0, NULL, 0, &cb, NULL))
086d7f31 370 volumeFlags &= ~FILE_SUPPORTS_SPARSE_FILES;
48714f74
RN
371}
372
373CppUnit::Test* GetlargeFileSuite()
374{
375 if (!volumeInfoInit) {
376 wxFile file;
377 wxString path = wxFileName::CreateTempFileName(_T("wxlfs-"), &file);
378 MakeSparse(path, file.fd());
379 wxRemoveFile(path);
380 }
381
382 if ((volumeFlags & FILE_SUPPORTS_SPARSE_FILES) != 0)
383 return largeFile::suite();
384 else
385 return NULL;
386}
387
388#else // __WXMSW__
389
390bool IsFAT(const wxString& WXUNUSED(path)) { return false; }
391void MakeSparse(const wxString& WXUNUSED(path), int WXUNUSED(fd)) { }
392
48714f74 393CppUnit::Test* GetlargeFileSuite() { return NULL; }
48714f74
RN
394
395#endif // __WXMSW__
396
397CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(largeFile, "largeFile");
398CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(largeFile, "Streams.largeFile");