as part of the default suite for more platforms. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@63024 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
		
			
				
	
	
		
			443 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			443 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| ///////////////////////////////////////////////////////////////////////////////
 | |
| // Name:        tests/streams/largefile.cpp
 | |
| // Purpose:     Tests for large file support
 | |
| // Author:      Mike Wetherell
 | |
| // RCS-ID:      $Id$
 | |
| // Copyright:   (c) 2004 Mike Wetherell
 | |
| // Licence:     wxWidgets licence
 | |
| ///////////////////////////////////////////////////////////////////////////////
 | |
| 
 | |
| //
 | |
| // We're interested in what happens around offsets 0x7fffffff and 0xffffffff
 | |
| // so the test writes a small chunk of test data just before and just after
 | |
| // these offsets, then reads them back.
 | |
| //
 | |
| // The tests can be run with:
 | |
| //
 | |
| //  test --verbose largeFile
 | |
| //
 | |
| // On systems supporting sparse files they will also be registered in the
 | |
| // Streams subsuite so that they run by default.
 | |
| //
 | |
| 
 | |
| // For compilers that support precompilation, includes "wx/wx.h".
 | |
| #include "testprec.h"
 | |
| 
 | |
| #ifdef __BORLANDC__
 | |
|     #pragma hdrstop
 | |
| #endif
 | |
| 
 | |
| // for all others, include the necessary headers
 | |
| #ifndef WX_PRECOMP
 | |
|     #include "wx/wx.h"
 | |
| #endif
 | |
| 
 | |
| #include "wx/filename.h"
 | |
| #include "wx/wfstream.h"
 | |
| 
 | |
| #ifdef __WXMSW__
 | |
|     #include "wx/msw/wrapwin.h"
 | |
|     #ifdef __VISUALC__
 | |
|         // 'nonstandard extension used : nameless struct/union' occurs inside
 | |
|         // winioctl.h
 | |
|         #pragma warning(disable:4201)
 | |
|     #endif
 | |
|     #include <winioctl.h>
 | |
|     #ifdef __VISUALC__
 | |
|         #pragma warning(default:4201)
 | |
|     #endif
 | |
| #endif
 | |
| 
 | |
| #ifdef __VISUALC__
 | |
|     #define fileno _fileno
 | |
| #endif
 | |
| 
 | |
| using std::auto_ptr;
 | |
| 
 | |
| 
 | |
| ///////////////////////////////////////////////////////////////////////////////
 | |
| // Helpers
 | |
| 
 | |
| bool IsFAT(const wxString& path);
 | |
| void MakeSparse(const wxString& path, int fd);
 | |
| 
 | |
| 
 | |
| ///////////////////////////////////////////////////////////////////////////////
 | |
| // Base class for the test cases - common code
 | |
| 
 | |
| class LargeFileTest : public CppUnit::TestCase
 | |
| {
 | |
| public:
 | |
|     LargeFileTest(std::string name) : CppUnit::TestCase(name) { }
 | |
|     virtual ~LargeFileTest() { }
 | |
| 
 | |
| protected:
 | |
|     void runTest();
 | |
| 
 | |
|     virtual wxInputStream *MakeInStream(const wxString& name) const = 0;
 | |
|     virtual wxOutputStream *MakeOutStream(const wxString& name) const = 0;
 | |
|     virtual bool HasLFS() const = 0;
 | |
| };
 | |
| 
 | |
| void LargeFileTest::runTest()
 | |
| {
 | |
|     // self deleting temp file
 | |
|     struct TmpFile {
 | |
|         TmpFile() : m_name(wxFileName::CreateTempFileName(wxT("wxlfs-"))) { }
 | |
|         ~TmpFile() { if (!m_name.empty()) wxRemoveFile(m_name); }
 | |
|         wxString m_name;
 | |
|     } tmpfile;
 | |
| 
 | |
|     CPPUNIT_ASSERT(!tmpfile.m_name.empty());
 | |
| 
 | |
|     bool haveLFS = true;
 | |
|     bool fourGigLimit = false;
 | |
| 
 | |
|     if (!HasLFS()) {
 | |
|         haveLFS = false;
 | |
|         wxString n(getName().c_str(), *wxConvCurrent);
 | |
|         wxLogInfo(n + wxT(": No large file support, testing up to 2GB only"));
 | |
|     }
 | |
|     else if (IsFAT(tmpfile.m_name)) {
 | |
|         fourGigLimit = true;
 | |
|         wxString n(getName().c_str(), *wxConvCurrent);
 | |
|         wxLogInfo(n + wxT(": FAT volumes are limited to 4GB files"));
 | |
|     }
 | |
| 
 | |
|     // size of the test blocks
 | |
|     const size_t size = 0x40;
 | |
| 
 | |
|     // the test blocks
 | |
|     char upto2Gig[size];
 | |
|     char past2Gig[size];
 | |
|     char upto4Gig[size];
 | |
|     char past4Gig[size];
 | |
|     memset(upto2Gig, 'A', size);
 | |
|     memset(past2Gig, 'B', size);
 | |
|     memset(upto4Gig, 'X', size);
 | |
|     memset(past4Gig, 'Y', size);
 | |
| 
 | |
|     wxFileOffset pos;
 | |
| 
 | |
|     // write a large file
 | |
|     {
 | |
|         auto_ptr<wxOutputStream> out(MakeOutStream(tmpfile.m_name));
 | |
| 
 | |
|         // write 'A's at [ 0x7fffffbf, 0x7fffffff [
 | |
|         pos = 0x7fffffff - size;
 | |
|         CPPUNIT_ASSERT(out->SeekO(pos) == pos);
 | |
|         CPPUNIT_ASSERT(out->Write(upto2Gig, size).LastWrite() == size);
 | |
|         pos += size;
 | |
| 
 | |
|         if (haveLFS) {
 | |
|             // write 'B's at [ 0x7fffffff, 0x8000003f [
 | |
|             CPPUNIT_ASSERT(out->Write(past2Gig, size).LastWrite() == size);
 | |
|             pos += size;
 | |
|             CPPUNIT_ASSERT(out->TellO() == pos);
 | |
| 
 | |
|             // write 'X's at [ 0xffffffbf, 0xffffffff [
 | |
|             pos = 0xffffffff - size;
 | |
|             CPPUNIT_ASSERT(out->SeekO(pos) == pos);
 | |
|             CPPUNIT_ASSERT(out->Write(upto4Gig, size).LastWrite() == size);
 | |
|             pos += size;
 | |
| 
 | |
|             if (!fourGigLimit) {
 | |
|                 // write 'Y's at [ 0xffffffff, 0x10000003f [
 | |
|                 CPPUNIT_ASSERT(out->Write(past4Gig, size).LastWrite() == size);
 | |
|                 pos += size;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         // check the file seems to be the right length
 | |
|         CPPUNIT_ASSERT(out->TellO() == pos);
 | |
|         CPPUNIT_ASSERT(out->GetLength() == pos);
 | |
|     }
 | |
| 
 | |
|     // read the large file back
 | |
|     {
 | |
|         auto_ptr<wxInputStream> in(MakeInStream(tmpfile.m_name));
 | |
|         char buf[size];
 | |
| 
 | |
|         if (haveLFS) {
 | |
|             CPPUNIT_ASSERT(in->GetLength() == pos);
 | |
|             pos = 0xffffffff;
 | |
| 
 | |
|             if (!fourGigLimit) {
 | |
|                 CPPUNIT_ASSERT(in->GetLength() > pos);
 | |
| 
 | |
|                 // read back the 'Y's at [ 0xffffffff, 0x10000003f [
 | |
|                 CPPUNIT_ASSERT(in->SeekI(pos) == pos);
 | |
|                 CPPUNIT_ASSERT(in->Read(buf, size).LastRead() == size);
 | |
|                 CPPUNIT_ASSERT(memcmp(buf, past4Gig, size) == 0);
 | |
| 
 | |
|                 CPPUNIT_ASSERT(in->TellI() == in->GetLength());
 | |
|             }
 | |
| 
 | |
|             // read back the 'X's at [ 0xffffffbf, 0xffffffff [
 | |
|             pos -= size;
 | |
|             CPPUNIT_ASSERT(in->SeekI(pos) == pos);
 | |
|             CPPUNIT_ASSERT(in->Read(buf, size).LastRead() == size);
 | |
|             CPPUNIT_ASSERT(memcmp(buf, upto4Gig, size) == 0);
 | |
|             pos += size;
 | |
|             CPPUNIT_ASSERT(in->TellI() == pos);
 | |
| 
 | |
|             // read back the 'B's at [ 0x7fffffff, 0x8000003f [
 | |
|             pos = 0x7fffffff;
 | |
|             CPPUNIT_ASSERT(in->SeekI(pos) == pos);
 | |
|             CPPUNIT_ASSERT(in->Read(buf, size).LastRead() == size);
 | |
|             CPPUNIT_ASSERT(memcmp(buf, past2Gig, size) == 0);
 | |
|         }
 | |
|         else {
 | |
|             CPPUNIT_ASSERT(in->GetLength() == 0x7fffffff);
 | |
|             pos = 0x7fffffff;
 | |
|         }
 | |
| 
 | |
|         // read back the 'A's at [ 0x7fffffbf, 0x7fffffff [
 | |
|         pos -= size;
 | |
|         CPPUNIT_ASSERT(in->SeekI(pos) == pos);
 | |
|         CPPUNIT_ASSERT(in->Read(buf, size).LastRead() == size);
 | |
|         CPPUNIT_ASSERT(memcmp(buf, upto2Gig, size) == 0);
 | |
|         pos += size;
 | |
|         CPPUNIT_ASSERT(in->TellI() == pos);
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| ///////////////////////////////////////////////////////////////////////////////
 | |
| // wxFile test case
 | |
| 
 | |
| class LargeFileTest_wxFile : public LargeFileTest
 | |
| {
 | |
| public:
 | |
|     LargeFileTest_wxFile() : LargeFileTest("wxFile streams") { }
 | |
| 
 | |
| protected:
 | |
|     wxInputStream *MakeInStream(const wxString& name) const;
 | |
|     wxOutputStream *MakeOutStream(const wxString& name) const;
 | |
|     bool HasLFS() const { return (wxFileOffset)0xffffffff > 0; }
 | |
| };
 | |
| 
 | |
| wxInputStream *LargeFileTest_wxFile::MakeInStream(const wxString& name) const
 | |
| {
 | |
|     auto_ptr<wxFileInputStream> in(new wxFileInputStream(name));
 | |
|     CPPUNIT_ASSERT(in->Ok());
 | |
|     return in.release();
 | |
| }
 | |
| 
 | |
| wxOutputStream *LargeFileTest_wxFile::MakeOutStream(const wxString& name) const
 | |
| {
 | |
|     wxFile file(name, wxFile::write);
 | |
|     CPPUNIT_ASSERT(file.IsOpened());
 | |
|     int fd = file.fd();
 | |
|     file.Detach();
 | |
|     MakeSparse(name, fd);
 | |
|     return new wxFileOutputStream(fd);
 | |
| }
 | |
| 
 | |
| 
 | |
| ///////////////////////////////////////////////////////////////////////////////
 | |
| // wxFFile test case
 | |
| 
 | |
| class LargeFileTest_wxFFile : public LargeFileTest
 | |
| {
 | |
| public:
 | |
|     LargeFileTest_wxFFile() : LargeFileTest("wxFFile streams") { }
 | |
| 
 | |
| protected:
 | |
|     wxInputStream *MakeInStream(const wxString& name) const;
 | |
|     wxOutputStream *MakeOutStream(const wxString& name) const;
 | |
|     bool HasLFS() const;
 | |
| };
 | |
| 
 | |
| wxInputStream *LargeFileTest_wxFFile::MakeInStream(const wxString& name) const
 | |
| {
 | |
|     auto_ptr<wxFFileInputStream> in(new wxFFileInputStream(name));
 | |
|     CPPUNIT_ASSERT(in->Ok());
 | |
|     return in.release();
 | |
| }
 | |
| 
 | |
| wxOutputStream *LargeFileTest_wxFFile::MakeOutStream(const wxString& name) const
 | |
| {
 | |
|     wxFFile file(name, wxT("w"));
 | |
|     CPPUNIT_ASSERT(file.IsOpened());
 | |
|     FILE *fp = file.fp();
 | |
|     file.Detach();
 | |
|     MakeSparse(name, fileno(fp));
 | |
|     return new wxFFileOutputStream(fp);
 | |
| }
 | |
| 
 | |
| bool LargeFileTest_wxFFile::HasLFS() const
 | |
| {
 | |
| #ifdef wxHAS_LARGE_FFILES
 | |
|     return true;
 | |
| #else
 | |
|     return false;
 | |
| #endif
 | |
| }
 | |
| 
 | |
| 
 | |
| ///////////////////////////////////////////////////////////////////////////////
 | |
| // The suite
 | |
| 
 | |
| class largeFile : public CppUnit::TestSuite
 | |
| {
 | |
| public:
 | |
|     largeFile() : CppUnit::TestSuite("largeFile") { }
 | |
| 
 | |
|     static CppUnit::Test *suite();
 | |
| };
 | |
| 
 | |
| CppUnit::Test *largeFile::suite()
 | |
| {
 | |
|     largeFile *suite = new largeFile;
 | |
| 
 | |
|     suite->addTest(new LargeFileTest_wxFile);
 | |
|     suite->addTest(new LargeFileTest_wxFFile);
 | |
| 
 | |
|     return suite;
 | |
| }
 | |
| 
 | |
| 
 | |
| ///////////////////////////////////////////////////////////////////////////////
 | |
| // Implement the helpers
 | |
| //
 | |
| // Ideally these tests will be part of the default suite so that regressions
 | |
| // are picked up. However this is only possible when sparse files are
 | |
| // supported otherwise the tests require too much disk space.
 | |
| 
 | |
| #ifdef __WXMSW__
 | |
| 
 | |
| #ifndef FILE_SUPPORTS_SPARSE_FILES
 | |
| #define FILE_SUPPORTS_SPARSE_FILES 0x00000040
 | |
| #endif
 | |
| 
 | |
| #ifndef FSCTL_SET_SPARSE
 | |
| 
 | |
| #   ifndef FILE_SPECIAL_ACCESS
 | |
| #       define FILE_SPECIAL_ACCESS FILE_ANY_ACCESS
 | |
| #   endif
 | |
| #   define FSCTL_SET_SPARSE CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 49, \
 | |
|                                      METHOD_BUFFERED, FILE_SPECIAL_ACCESS)
 | |
| #endif
 | |
| 
 | |
| static DWORD  volumeFlags;
 | |
| static wxChar volumeType[64];
 | |
| static bool   volumeInfoInit;
 | |
| 
 | |
| void GetVolumeInfo(const wxString& path)
 | |
| {
 | |
|     // extract the volume 'C:\' or '\\tooter\share\' from the path
 | |
|     wxString vol;
 | |
| 
 | |
|     if (path.substr(1, 2) == wxT(":\\")) {
 | |
|         vol = path.substr(0, 3);
 | |
|     } else {
 | |
|         if (path.substr(0, 2) == wxT("\\\\")) {
 | |
|             size_t i = path.find(wxT('\\'), 2);
 | |
| 
 | |
|             if (i != wxString::npos && i > 2) {
 | |
|                 size_t j = path.find(wxT('\\'), ++i);
 | |
| 
 | |
|                 if (j != i)
 | |
|                     vol = path.substr(0, j) + wxT("\\");
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // NULL means the current volume
 | |
|     const wxChar *pVol = vol.empty() ? (const wxChar *)NULL
 | |
|                                      : vol.c_str();
 | |
| 
 | |
|     if (!::GetVolumeInformation(pVol, NULL, 0, NULL, NULL,
 | |
|                                 &volumeFlags,
 | |
|                                 volumeType,
 | |
|                                 WXSIZEOF(volumeType)))
 | |
|     {
 | |
|         wxLogSysError(wxT("GetVolumeInformation() failed"));
 | |
|     }
 | |
| 
 | |
|     volumeInfoInit = true;
 | |
| }
 | |
| 
 | |
| bool IsFAT(const wxString& path)
 | |
| {
 | |
|     if (!volumeInfoInit)
 | |
|         GetVolumeInfo(path);
 | |
|     return wxString(volumeType).Upper().find(wxT("FAT")) != wxString::npos;
 | |
| }
 | |
| 
 | |
| void MakeSparse(const wxString& path, int fd)
 | |
| {
 | |
|     DWORD cb;
 | |
| 
 | |
|     if (!volumeInfoInit)
 | |
|         GetVolumeInfo(path);
 | |
| 
 | |
|     if ((volumeFlags & FILE_SUPPORTS_SPARSE_FILES) != 0)
 | |
|         if (!::DeviceIoControl((HANDLE)_get_osfhandle(fd),
 | |
|                                FSCTL_SET_SPARSE,
 | |
|                                NULL, 0, NULL, 0, &cb, NULL))
 | |
|             volumeFlags &= ~FILE_SUPPORTS_SPARSE_FILES;
 | |
| }
 | |
| 
 | |
| // return the suite if sparse files are supported, otherwise return NULL
 | |
| //
 | |
| CppUnit::Test* GetlargeFileSuite()
 | |
| {
 | |
|     if (!volumeInfoInit) {
 | |
|         wxString path;
 | |
|         {
 | |
|             wxFile file;
 | |
|             path = wxFileName::CreateTempFileName(wxT("wxlfs-"), &file);
 | |
|             MakeSparse(path, file.fd());
 | |
|         }
 | |
|         wxRemoveFile(path);
 | |
|     }
 | |
| 
 | |
|     if ((volumeFlags & FILE_SUPPORTS_SPARSE_FILES) != 0)
 | |
|         return largeFile::suite();
 | |
|     else
 | |
|         return NULL;
 | |
| }
 | |
| 
 | |
| #else // __WXMSW__
 | |
| 
 | |
| bool IsFAT(const wxString& WXUNUSED(path)) { return false; }
 | |
| void MakeSparse(const wxString& WXUNUSED(path), int WXUNUSED(fd)) { }
 | |
| 
 | |
| // return the suite if sparse files are supported, otherwise return NULL
 | |
| //
 | |
| CppUnit::Test* GetlargeFileSuite()
 | |
| {
 | |
|     wxString path;
 | |
|     struct stat st1, st2;
 | |
|     memset(&st1, 0, sizeof(st1));
 | |
|     memset(&st2, 0, sizeof(st2));
 | |
| 
 | |
|     {
 | |
|         wxFile file;
 | |
|         path = wxFileName::CreateTempFileName(wxT("wxlfs-"), &file);
 | |
| 
 | |
|         fstat(file.fd(), &st1);
 | |
|         file.Seek(st1.st_blksize);
 | |
|         file.Write("x", 1);
 | |
|         fstat(file.fd(), &st1);
 | |
| 
 | |
|         file.Seek(0);
 | |
|         file.Write("x", 1);
 | |
|         fstat(file.fd(), &st2);
 | |
|     }
 | |
| 
 | |
|     wxRemoveFile(path);
 | |
| 
 | |
|     if (st1.st_blocks != st2.st_blocks)
 | |
|         return largeFile::suite();
 | |
|     else
 | |
|         return NULL;
 | |
| }
 | |
| 
 | |
| #endif // __WXMSW__
 | |
| 
 | |
| CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(largeFile, "largeFile");
 | |
| CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(largeFile, "Streams.largeFile");
 |