diff --git a/Builds/VisualStudio2012/beast.vcxproj b/Builds/VisualStudio2012/beast.vcxproj
index 6cc039c10..18f2243de 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 c70a09c15..2e5383b85 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 cb3662d5d..f2387873f 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 8abaf273e..cbc27191a 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 000000000..5d94a0bba
--- /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 000000000..b2fa7792c
--- /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 ee6df2b5a..200b40a13 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;