From a3af6404b04ccf347ad4d08d74fbe7d255932951 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Wed, 17 Jul 2013 11:56:45 -0700 Subject: [PATCH] RandomAccessFile unit tests --- Builds/VisualStudio2012/beast.vcxproj | 7 + Builds/VisualStudio2012/beast.vcxproj.filters | 6 + modules/beast_core/beast_core.cpp | 1 + modules/beast_core/beast_core.h | 1 + .../diagnostic/beast_UnitTestUtilities.cpp | 56 ++++++ .../diagnostic/beast_UnitTestUtilities.h | 100 +++++++++++ .../files/beast_RandomAccessFile.cpp | 166 ++++++++++++++---- 7 files changed, 298 insertions(+), 39 deletions(-) create mode 100644 modules/beast_core/diagnostic/beast_UnitTestUtilities.cpp create mode 100644 modules/beast_core/diagnostic/beast_UnitTestUtilities.h diff --git a/Builds/VisualStudio2012/beast.vcxproj b/Builds/VisualStudio2012/beast.vcxproj index 6cc039c10c..18f2243de8 100644 --- a/Builds/VisualStudio2012/beast.vcxproj +++ b/Builds/VisualStudio2012/beast.vcxproj @@ -134,6 +134,7 @@ + @@ -408,6 +409,12 @@ true true + + true + true + true + true + true true diff --git a/Builds/VisualStudio2012/beast.vcxproj.filters b/Builds/VisualStudio2012/beast.vcxproj.filters index c70a09c151..2e5383b85e 100644 --- a/Builds/VisualStudio2012/beast.vcxproj.filters +++ b/Builds/VisualStudio2012/beast.vcxproj.filters @@ -626,6 +626,9 @@ beast_core\files + + beast_core\diagnostic + @@ -973,6 +976,9 @@ beast_core\files + + beast_core\diagnostic + diff --git a/modules/beast_core/beast_core.cpp b/modules/beast_core/beast_core.cpp index cb3662d5d8..f2387873fe 100644 --- a/modules/beast_core/beast_core.cpp +++ b/modules/beast_core/beast_core.cpp @@ -149,6 +149,7 @@ namespace beast #include "diagnostic/beast_FPUFlags.cpp" #include "diagnostic/beast_LeakChecked.cpp" #include "diagnostic/beast_UnitTest.cpp" +#include "diagnostic/beast_UnitTestUtilities.cpp" #include "files/beast_DirectoryIterator.cpp" #include "files/beast_File.cpp" diff --git a/modules/beast_core/beast_core.h b/modules/beast_core/beast_core.h index 8abaf273ea..cbc27191a0 100644 --- a/modules/beast_core/beast_core.h +++ b/modules/beast_core/beast_core.h @@ -226,6 +226,7 @@ namespace beast #include "diagnostic/beast_Error.h" #include "diagnostic/beast_FPUFlags.h" #include "diagnostic/beast_UnitTest.h" +#include "diagnostic/beast_UnitTestUtilities.h" #include "diagnostic/beast_Throw.h" #include "containers/beast_AbstractFifo.h" #include "containers/beast_Array.h" diff --git a/modules/beast_core/diagnostic/beast_UnitTestUtilities.cpp b/modules/beast_core/diagnostic/beast_UnitTestUtilities.cpp new file mode 100644 index 0000000000..5d94a0bba0 --- /dev/null +++ b/modules/beast_core/diagnostic/beast_UnitTestUtilities.cpp @@ -0,0 +1,56 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2013, Vinnie Falco + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +class UnitTestUtilitiesTests : public UnitTest +{ +public: + UnitTestUtilitiesTests () : UnitTest ("UnitTestUtilities") + { + } + + void testPayload () + { + using namespace UnitTestUtilities; + + int const maxBufferSize = 4000; + int const minimumBytes = 1; + int const numberOfItems = 100; + int64 const seedValue = 50; + + beginTest ("Payload"); + + Payload p1 (maxBufferSize); + Payload p2 (maxBufferSize); + + for (int i = 0; i < numberOfItems; ++i) + { + p1.repeatableRandomFill (minimumBytes, maxBufferSize, seedValue); + p2.repeatableRandomFill (minimumBytes, maxBufferSize, seedValue); + + expect (p1 == p2, "Should be equal"); + } + } + + void runTest () + { + testPayload (); + } +}; + +static UnitTestUtilitiesTests unitTestUtilitiesTests; diff --git a/modules/beast_core/diagnostic/beast_UnitTestUtilities.h b/modules/beast_core/diagnostic/beast_UnitTestUtilities.h new file mode 100644 index 0000000000..b2fa7792c0 --- /dev/null +++ b/modules/beast_core/diagnostic/beast_UnitTestUtilities.h @@ -0,0 +1,100 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2013, Vinnie Falco + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef BEAST_UNITTESTUTILITIES_H_INCLUDED +#define BEAST_UNITTESTUTILITIES_H_INCLUDED + +#include "../maths/beast_Random.h" + +namespace UnitTestUtilities +{ + +/** Fairly shuffle an array pseudo-randomly. +*/ +template +void repeatableShuffle (int const numberOfItems, T& arrayOfItems, int64 seedValue) +{ + Random r (seedValue); + + for (int i = numberOfItems - 1; i > 0; --i) + { + int const choice = r.nextInt (i + 1); + + std::swap (arrayOfItems [i], arrayOfItems [choice]); + } +} + +/** A block of memory used for test data. +*/ +struct Payload +{ + /** Construct a payload with a buffer of the specified maximum size. + + @param maximumBytes The size of the buffer, in bytes. + */ + explicit Payload (int maxBufferSize) + : bufferSize (maxBufferSize) + , data (maxBufferSize) + { + } + + /** Generate a random block of data within a certain size range. + + @param minimumBytes The smallest number of bytes in the resulting payload. + @param maximumBytes The largest number of bytes in the resulting payload. + @param seedValue The value to seed the random number generator with. + */ + void repeatableRandomFill (int minimumBytes, int maximumBytes, int64 seedValue) noexcept + { + bassert (minimumBytes >=0 && maximumBytes <= bufferSize); + + Random r (seedValue); + + bytes = minimumBytes + r.nextInt (1 + maximumBytes - minimumBytes); + + bassert (bytes >= minimumBytes && bytes <= bufferSize); + + for (int i = 0; i < bytes; ++i) + data [i] = static_cast (r.nextInt ()); + } + + /** Compare two payloads for equality. + */ + bool operator== (Payload const& other) const noexcept + { + if (bytes == other.bytes) + { + return memcmp (data.getData (), other.data.getData (), bytes) == 0; + } + else + { + return false; + } + } + +public: + int const bufferSize; + + int bytes; + HeapBlock data; +}; + +} + +#endif diff --git a/modules/beast_core/files/beast_RandomAccessFile.cpp b/modules/beast_core/files/beast_RandomAccessFile.cpp index ee6df2b5a1..200b40a13f 100644 --- a/modules/beast_core/files/beast_RandomAccessFile.cpp +++ b/modules/beast_core/files/beast_RandomAccessFile.cpp @@ -157,60 +157,148 @@ Result RandomAccessFile::flushBuffer () class RandomAccessFileTests : public UnitTest { public: - RandomAccessFileTests () : UnitTest ("RandomAccessFile") + RandomAccessFileTests () + : UnitTest ("RandomAccessFile") + , numRecords (1000) + , seedValue (50) { } - struct Payload + /* For this test we will create a file which consists of a fixed + number of variable length records. Each record is numbered sequentially + start at 1. To calculate the position of each record we first build + a table of size/offset pairs using a pseudorandom number generator. + */ + struct Record { - Payload (int maxBytes) - : bufferSize (maxBytes) - , data (maxBytes) - { - } - - // Create a pseudo-random payload - void generate (int64 seedValue) noexcept - { - Random r (seedValue); - - bytes = 1 + r.nextInt (bufferSize); - - bassert (bytes >= 1 && bytes <= bufferSize); - - for (int i = 0; i < bytes; ++i) - data [i] = static_cast (r.nextInt ()); - } - - bool operator== (Payload const& other) const noexcept - { - if (bytes == other.bytes) - { - return memcmp (data.getData (), other.data.getData (), bytes) == 0; - } - else - { - return false; - } - } - - int const bufferSize; + int index; int bytes; - HeapBlock data; + int offset; }; - void runTest () - { - RandomAccessFile file; + typedef HeapBlock Records; - beginTest ("open"); + // Produce the pseudo-random set of records. + static void createRecords (HeapBlock & records, + int numRecords, + int maxBytes, + int64 seedValue) + { + using namespace UnitTestUtilities; + + Random r (seedValue); + + records.malloc (numRecords); + + int offset = 0; + + for (int i = 0; i < numRecords; ++i) + { + int const bytes = r.nextInt (maxBytes) + 1; + + records [i].index = i; + records [i].bytes = bytes; + records [i].offset = offset; + + offset += bytes; + } + + repeatableShuffle (numRecords, records, seedValue); + } + + void writeRecords (RandomAccessFile& file, + int numRecords, + HeapBlock const& records, + int64 seedValue) + { + using namespace UnitTestUtilities; + + for (int i = 0; i < numRecords; ++i) + { + Payload p (records [i].bytes); + + p.repeatableRandomFill (records [i].bytes, + records [i].bytes, + records [i].index + seedValue); + + file.setPosition (records [i].offset); + + Result result = file.write (p.data.getData (), p.bytes); + + expect (result.wasOk (), "Should be ok"); + } + } + + void readRecords (RandomAccessFile& file, + int numRecords, + HeapBlock const & records, + int64 seedValue) + { + using namespace UnitTestUtilities; + + for (int i = 0; i < numRecords; ++i) + { + int const bytes = records [i].bytes; + + Payload p1 (bytes); + Payload p2 (bytes); + + p1.repeatableRandomFill (bytes, bytes, records [i].index + seedValue); + + file.setPosition (records [i].offset); + + Result result = file.read (p2.data.getData (), bytes); + + expect (result.wasOk (), "Should be ok"); + + if (result.wasOk ()) + { + p2.bytes = bytes; + + expect (p1 == p2, "Should be equal"); + } + } + } + + void testFile (int const bufferSize) + { + using namespace UnitTestUtilities; + + String s; + s << "bufferSize = " << String (bufferSize); + beginTest (s); + + int const maxPayload = bmax (1000, bufferSize * 2); + + RandomAccessFile file (bufferSize); Result result = file.open (File::createTempFile ("tests"), RandomAccessFile::readWrite); expect (result.wasOk (), "Should be ok"); + + HeapBlock records (numRecords); + + createRecords (records, numRecords, maxPayload, seedValue); + + writeRecords (file, numRecords, records, seedValue); + + readRecords (file, numRecords, records, seedValue); + + repeatableShuffle (numRecords, records, seedValue); + + readRecords (file, numRecords, records, seedValue); + } + + void runTest () + { + testFile (0); + testFile (1000); + testFile (10000); } private: + int const numRecords; + int64 const seedValue; }; static RandomAccessFileTests randomAccessFileTests;