3f66f6a5b3
This keyword is not expanded by Git which means it's not replaced with the correct revision value in the releases made using git-based scripts and it's confusing to have lines with unexpanded "$Id$" in the released files. As expanding them with Git is not that simple (it could be done with git archive and export-subst attribute) and there are not many benefits in having them in the first place, just remove all these lines. If nothing else, this will make an eventual transition to Git simpler. Closes #14487. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@74602 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
442 lines
12 KiB
C++
442 lines
12 KiB
C++
///////////////////////////////////////////////////////////////////////////////
|
|
// Name: tests/streams/largefile.cpp
|
|
// Purpose: Tests for large file support
|
|
// Author: Mike Wetherell
|
|
// Copyright: (c) 2004 Mike Wetherell
|
|
// Licence: wxWindows 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 __WINDOWS__
|
|
#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->IsOk());
|
|
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->IsOk());
|
|
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 __WINDOWS__
|
|
|
|
#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 // __WINDOWS__
|
|
|
|
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 // __WINDOWS__
|
|
|
|
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(largeFile, "largeFile");
|
|
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(largeFile, "Streams.largeFile");
|