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