This is a combination of running clang-tidy with modernize-use-nullptr check for some ports (GTK, X11, OSX) and manual changes to the ports for which it couldn't be used easily (MSW, DFB) and also manually updating the docs. Also replace NULL with null or nullptr in the comments as this is more consistent with the use of nullptr in the code and makes it simpler to grep for the remaining occurrences of NULL itself. And also use null in the assert messages. Only a few occurrences of "NULL" are still left in non-C files, mostly corresponding to unclear comments or string output which it might not be safe to change.
436 lines
12 KiB
C++
436 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"
|
|
|
|
|
|
// for all others, include the necessary headers
|
|
#ifndef WX_PRECOMP
|
|
#include "wx/wx.h"
|
|
#endif
|
|
|
|
#include "wx/filename.h"
|
|
#include "wx/scopedptr.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
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// 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() override;
|
|
|
|
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
|
|
{
|
|
wxScopedPtr<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
|
|
{
|
|
wxScopedPtr<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 override;
|
|
wxOutputStream *MakeOutStream(const wxString& name) const override;
|
|
bool HasLFS() const override { return (wxFileOffset)0xffffffff > 0; }
|
|
};
|
|
|
|
wxInputStream *LargeFileTest_wxFile::MakeInStream(const wxString& name) const
|
|
{
|
|
wxScopedPtr<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 override;
|
|
wxOutputStream *MakeOutStream(const wxString& name) const override;
|
|
bool HasLFS() const override;
|
|
};
|
|
|
|
wxInputStream *LargeFileTest_wxFFile::MakeInStream(const wxString& name) const
|
|
{
|
|
wxScopedPtr<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("\\");
|
|
}
|
|
}
|
|
}
|
|
|
|
// nullptr means the current volume
|
|
const wxChar *pVol = vol.empty() ? (const wxChar *)nullptr
|
|
: vol.c_str();
|
|
|
|
if (!::GetVolumeInformation(pVol, nullptr, 0, nullptr, nullptr,
|
|
&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,
|
|
nullptr, 0, nullptr, 0, &cb, nullptr))
|
|
volumeFlags &= ~FILE_SUPPORTS_SPARSE_FILES;
|
|
}
|
|
|
|
// return the suite if sparse files are supported, otherwise return nullptr
|
|
//
|
|
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 nullptr;
|
|
}
|
|
|
|
#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 nullptr
|
|
//
|
|
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 nullptr;
|
|
}
|
|
|
|
#endif // __WINDOWS__
|
|
|
|
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(largeFile, "Streams.largeFile");
|