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