@@ -482,17 +482,16 @@ public: | |||||
template <class F1, class E1, class F2, class E2> | template <class F1, class E1, class F2, class E2> | ||||
struct Test5 | struct Test5 | ||||
{ | { | ||||
static void test (UnitTest& unitTest) | |||||
static void test (UnitTest& unitTest, Random& r) | |||||
{ | { | ||||
test (unitTest, false); | |||||
test (unitTest, true); | |||||
test (unitTest, false, r); | |||||
test (unitTest, true, r); | |||||
} | } | ||||
static void test (UnitTest& unitTest, bool inPlace) | |||||
static void test (UnitTest& unitTest, bool inPlace, Random& r) | |||||
{ | { | ||||
const int numSamples = 2048; | const int numSamples = 2048; | ||||
int32 original [numSamples], converted [numSamples], reversed [numSamples]; | int32 original [numSamples], converted [numSamples], reversed [numSamples]; | ||||
Random r; | |||||
{ | { | ||||
AudioData::Pointer<F1, E1, AudioData::NonInterleaved, AudioData::NonConst> d (original); | AudioData::Pointer<F1, E1, AudioData::NonInterleaved, AudioData::NonConst> d (original); | ||||
@@ -549,49 +548,50 @@ public: | |||||
template <class F1, class E1, class FormatType> | template <class F1, class E1, class FormatType> | ||||
struct Test3 | struct Test3 | ||||
{ | { | ||||
static void test (UnitTest& unitTest) | |||||
static void test (UnitTest& unitTest, Random& r) | |||||
{ | { | ||||
Test5 <F1, E1, FormatType, AudioData::BigEndian>::test (unitTest); | |||||
Test5 <F1, E1, FormatType, AudioData::LittleEndian>::test (unitTest); | |||||
Test5 <F1, E1, FormatType, AudioData::BigEndian>::test (unitTest, r); | |||||
Test5 <F1, E1, FormatType, AudioData::LittleEndian>::test (unitTest, r); | |||||
} | } | ||||
}; | }; | ||||
template <class FormatType, class Endianness> | template <class FormatType, class Endianness> | ||||
struct Test2 | struct Test2 | ||||
{ | { | ||||
static void test (UnitTest& unitTest) | |||||
static void test (UnitTest& unitTest, Random& r) | |||||
{ | { | ||||
Test3 <FormatType, Endianness, AudioData::Int8>::test (unitTest); | |||||
Test3 <FormatType, Endianness, AudioData::UInt8>::test (unitTest); | |||||
Test3 <FormatType, Endianness, AudioData::Int16>::test (unitTest); | |||||
Test3 <FormatType, Endianness, AudioData::Int24>::test (unitTest); | |||||
Test3 <FormatType, Endianness, AudioData::Int32>::test (unitTest); | |||||
Test3 <FormatType, Endianness, AudioData::Float32>::test (unitTest); | |||||
Test3 <FormatType, Endianness, AudioData::Int8>::test (unitTest, r); | |||||
Test3 <FormatType, Endianness, AudioData::UInt8>::test (unitTest, r); | |||||
Test3 <FormatType, Endianness, AudioData::Int16>::test (unitTest, r); | |||||
Test3 <FormatType, Endianness, AudioData::Int24>::test (unitTest, r); | |||||
Test3 <FormatType, Endianness, AudioData::Int32>::test (unitTest, r); | |||||
Test3 <FormatType, Endianness, AudioData::Float32>::test (unitTest, r); | |||||
} | } | ||||
}; | }; | ||||
template <class FormatType> | template <class FormatType> | ||||
struct Test1 | struct Test1 | ||||
{ | { | ||||
static void test (UnitTest& unitTest) | |||||
static void test (UnitTest& unitTest, Random& r) | |||||
{ | { | ||||
Test2 <FormatType, AudioData::BigEndian>::test (unitTest); | |||||
Test2 <FormatType, AudioData::LittleEndian>::test (unitTest); | |||||
Test2 <FormatType, AudioData::BigEndian>::test (unitTest, r); | |||||
Test2 <FormatType, AudioData::LittleEndian>::test (unitTest, r); | |||||
} | } | ||||
}; | }; | ||||
void runTest() | void runTest() | ||||
{ | { | ||||
Random r = getRandom(); | |||||
beginTest ("Round-trip conversion: Int8"); | beginTest ("Round-trip conversion: Int8"); | ||||
Test1 <AudioData::Int8>::test (*this); | |||||
Test1 <AudioData::Int8>::test (*this, r); | |||||
beginTest ("Round-trip conversion: Int16"); | beginTest ("Round-trip conversion: Int16"); | ||||
Test1 <AudioData::Int16>::test (*this); | |||||
Test1 <AudioData::Int16>::test (*this, r); | |||||
beginTest ("Round-trip conversion: Int24"); | beginTest ("Round-trip conversion: Int24"); | ||||
Test1 <AudioData::Int24>::test (*this); | |||||
Test1 <AudioData::Int24>::test (*this, r); | |||||
beginTest ("Round-trip conversion: Int32"); | beginTest ("Round-trip conversion: Int32"); | ||||
Test1 <AudioData::Int32>::test (*this); | |||||
Test1 <AudioData::Int32>::test (*this, r); | |||||
beginTest ("Round-trip conversion: Float32"); | beginTest ("Round-trip conversion: Float32"); | ||||
Test1 <AudioData::Float32>::test (*this); | |||||
Test1 <AudioData::Float32>::test (*this, r); | |||||
} | } | ||||
}; | }; | ||||
@@ -141,8 +141,8 @@ public: | |||||
class WriteThread : public Thread | class WriteThread : public Thread | ||||
{ | { | ||||
public: | public: | ||||
WriteThread (AbstractFifo& fifo_, int* buffer_) | |||||
: Thread ("fifo writer"), fifo (fifo_), buffer (buffer_) | |||||
WriteThread (AbstractFifo& f, int* b, Random rng) | |||||
: Thread ("fifo writer"), fifo (f), buffer (b), random (rng) | |||||
{ | { | ||||
startThread(); | startThread(); | ||||
} | } | ||||
@@ -155,11 +155,10 @@ public: | |||||
void run() | void run() | ||||
{ | { | ||||
int n = 0; | int n = 0; | ||||
Random r; | |||||
while (! threadShouldExit()) | while (! threadShouldExit()) | ||||
{ | { | ||||
int num = r.nextInt (2000) + 1; | |||||
int num = random.nextInt (2000) + 1; | |||||
int start1, size1, start2, size2; | int start1, size1, start2, size2; | ||||
fifo.prepareToWrite (num, start1, size1, start2, size2); | fifo.prepareToWrite (num, start1, size1, start2, size2); | ||||
@@ -181,6 +180,7 @@ public: | |||||
private: | private: | ||||
AbstractFifo& fifo; | AbstractFifo& fifo; | ||||
int* buffer; | int* buffer; | ||||
Random random; | |||||
}; | }; | ||||
void runTest() | void runTest() | ||||
@@ -190,10 +190,11 @@ public: | |||||
int buffer [5000]; | int buffer [5000]; | ||||
AbstractFifo fifo (numElementsInArray (buffer)); | AbstractFifo fifo (numElementsInArray (buffer)); | ||||
WriteThread writer (fifo, buffer); | |||||
WriteThread writer (fifo, buffer, getRandom()); | |||||
int n = 0; | int n = 0; | ||||
Random r; | |||||
Random r = getRandom(); | |||||
r.combineSeed (12345); | |||||
for (int count = 100000; --count >= 0;) | for (int count = 100000; --count >= 0;) | ||||
{ | { | ||||
@@ -616,8 +616,7 @@ public: | |||||
void runTest() | void runTest() | ||||
{ | { | ||||
beginTest ("JSON"); | beginTest ("JSON"); | ||||
Random r; | |||||
r.setSeedRandomly(); | |||||
Random r = getRandom(); | |||||
expect (JSON::parse (String::empty) == var::null); | expect (JSON::parse (String::empty) == var::null); | ||||
expect (JSON::parse ("{}").isObject()); | expect (JSON::parse ("{}").isObject()); | ||||
@@ -953,10 +953,10 @@ String BigInteger::toString (const int base, const int minimumNumCharacters) con | |||||
return isNegative() ? "-" + s : s; | return isNegative() ? "-" + s : s; | ||||
} | } | ||||
void BigInteger::parseString (const String& text, const int base) | |||||
void BigInteger::parseString (StringRef text, const int base) | |||||
{ | { | ||||
clear(); | clear(); | ||||
String::CharPointerType t (text.getCharPointer().findEndOfWhitespace()); | |||||
String::CharPointerType t (text.text.findEndOfWhitespace()); | |||||
setNegative (*t == (juce_wchar) '-'); | setNegative (*t == (juce_wchar) '-'); | ||||
@@ -282,7 +282,7 @@ public: | |||||
Specify a base such as 2 (binary), 8 (octal), 10 (decimal), 16 (hex). | Specify a base such as 2 (binary), 8 (octal), 10 (decimal), 16 (hex). | ||||
Any invalid characters will be ignored. | Any invalid characters will be ignored. | ||||
*/ | */ | ||||
void parseString (const String& text, int base); | |||||
void parseString (StringRef text, int base); | |||||
//============================================================================== | //============================================================================== | ||||
/** Turns the number into a block of binary data. | /** Turns the number into a block of binary data. | ||||
@@ -26,13 +26,11 @@ | |||||
============================================================================== | ============================================================================== | ||||
*/ | */ | ||||
Random::Random (const int64 seedValue) noexcept | |||||
: seed (seedValue) | |||||
Random::Random (const int64 seedValue) noexcept : seed (seedValue) | |||||
{ | { | ||||
} | } | ||||
Random::Random() | |||||
: seed (1) | |||||
Random::Random() : seed (1) | |||||
{ | { | ||||
setSeedRandomly(); | setSeedRandomly(); | ||||
} | } | ||||
@@ -163,24 +161,20 @@ public: | |||||
{ | { | ||||
beginTest ("Random"); | beginTest ("Random"); | ||||
for (int j = 10; --j >= 0;) | |||||
Random r = getRandom(); | |||||
for (int i = 2000; --i >= 0;) | |||||
{ | { | ||||
Random r; | |||||
r.setSeedRandomly(); | |||||
for (int i = 20; --i >= 0;) | |||||
{ | |||||
expect (r.nextDouble() >= 0.0 && r.nextDouble() < 1.0); | |||||
expect (r.nextFloat() >= 0.0f && r.nextFloat() < 1.0f); | |||||
expect (r.nextInt (5) >= 0 && r.nextInt (5) < 5); | |||||
expect (r.nextInt (1) == 0); | |||||
int n = r.nextInt (50) + 1; | |||||
expect (r.nextInt (n) >= 0 && r.nextInt (n) < n); | |||||
n = r.nextInt (0x7ffffffe) + 1; | |||||
expect (r.nextInt (n) >= 0 && r.nextInt (n) < n); | |||||
} | |||||
expect (r.nextDouble() >= 0.0 && r.nextDouble() < 1.0); | |||||
expect (r.nextFloat() >= 0.0f && r.nextFloat() < 1.0f); | |||||
expect (r.nextInt (5) >= 0 && r.nextInt (5) < 5); | |||||
expect (r.nextInt (1) == 0); | |||||
int n = r.nextInt (50) + 1; | |||||
expect (r.nextInt (n) >= 0 && r.nextInt (n) < n); | |||||
n = r.nextInt (0x7ffffffe) + 1; | |||||
expect (r.nextInt (n) >= 0 && r.nextInt (n) < n); | |||||
} | } | ||||
} | } | ||||
}; | }; | ||||
@@ -59,7 +59,6 @@ public: | |||||
~Random() noexcept; | ~Random() noexcept; | ||||
/** Returns the next random 32 bit integer. | /** Returns the next random 32 bit integer. | ||||
@returns a random integer from the full range 0x80000000 to 0x7fffffff | @returns a random integer from the full range 0x80000000 to 0x7fffffff | ||||
*/ | */ | ||||
int nextInt() noexcept; | int nextInt() noexcept; | ||||
@@ -71,29 +70,24 @@ public: | |||||
int nextInt (int maxValue) noexcept; | int nextInt (int maxValue) noexcept; | ||||
/** Returns the next 64-bit random number. | /** Returns the next 64-bit random number. | ||||
@returns a random integer from the full range 0x8000000000000000 to 0x7fffffffffffffff | @returns a random integer from the full range 0x8000000000000000 to 0x7fffffffffffffff | ||||
*/ | */ | ||||
int64 nextInt64() noexcept; | int64 nextInt64() noexcept; | ||||
/** Returns the next random floating-point number. | /** Returns the next random floating-point number. | ||||
@returns a random value in the range 0 to 1.0 | @returns a random value in the range 0 to 1.0 | ||||
*/ | */ | ||||
float nextFloat() noexcept; | float nextFloat() noexcept; | ||||
/** Returns the next random floating-point number. | /** Returns the next random floating-point number. | ||||
@returns a random value in the range 0 to 1.0 | @returns a random value in the range 0 to 1.0 | ||||
*/ | */ | ||||
double nextDouble() noexcept; | double nextDouble() noexcept; | ||||
/** Returns the next random boolean value. | |||||
*/ | |||||
/** Returns the next random boolean value. */ | |||||
bool nextBool() noexcept; | bool nextBool() noexcept; | ||||
/** Returns a BigInteger containing a random number. | /** Returns a BigInteger containing a random number. | ||||
@returns a random value in the range 0 to (maximumValue - 1). | @returns a random value in the range 0 to (maximumValue - 1). | ||||
*/ | */ | ||||
BigInteger nextLargeNumber (const BigInteger& maximumValue); | BigInteger nextLargeNumber (const BigInteger& maximumValue); | ||||
@@ -108,6 +102,9 @@ public: | |||||
/** Resets this Random object to a given seed value. */ | /** Resets this Random object to a given seed value. */ | ||||
void setSeed (int64 newSeed) noexcept; | void setSeed (int64 newSeed) noexcept; | ||||
/** Returns the RNG's current seed. */ | |||||
int64 getSeed() const noexcept { return seed; } | |||||
/** Merges this object's seed with another value. | /** Merges this object's seed with another value. | ||||
This sets the seed to be a value created by combining the current seed and this | This sets the seed to be a value created by combining the current seed and this | ||||
new value. | new value. | ||||
@@ -317,11 +317,11 @@ void MemoryBlock::setBitRange (const size_t bitRangeStart, size_t numBits, int b | |||||
} | } | ||||
//============================================================================== | //============================================================================== | ||||
void MemoryBlock::loadFromHexString (const String& hex) | |||||
void MemoryBlock::loadFromHexString (StringRef hex) | |||||
{ | { | ||||
ensureSize ((size_t) hex.length() >> 1); | ensureSize ((size_t) hex.length() >> 1); | ||||
char* dest = data; | char* dest = data; | ||||
String::CharPointerType t (hex.getCharPointer()); | |||||
String::CharPointerType t (hex.text); | |||||
for (;;) | for (;;) | ||||
{ | { | ||||
@@ -373,27 +373,27 @@ String MemoryBlock::toBase64Encoding() const | |||||
return destString; | return destString; | ||||
} | } | ||||
bool MemoryBlock::fromBase64Encoding (const String& s) | |||||
bool MemoryBlock::fromBase64Encoding (StringRef s) | |||||
{ | { | ||||
const int startPos = s.indexOfChar ('.') + 1; | |||||
String::CharPointerType dot (CharacterFunctions::find (s.text, CharPointer_ASCII ("."))); | |||||
if (startPos <= 0) | |||||
if (dot.isEmpty()) | |||||
return false; | return false; | ||||
const int numBytesNeeded = s.substring (0, startPos - 1).getIntValue(); | |||||
const int numBytesNeeded = String (s.text, dot).getIntValue(); | |||||
setSize ((size_t) numBytesNeeded, true); | setSize ((size_t) numBytesNeeded, true); | ||||
const int numChars = s.length() - startPos; | |||||
String::CharPointerType srcChars (s.getCharPointer()); | |||||
srcChars += startPos; | |||||
String::CharPointerType srcChars (dot + 1); | |||||
int pos = 0; | int pos = 0; | ||||
for (int i = 0; i < numChars; ++i) | |||||
for (;;) | |||||
{ | { | ||||
const char c = (char) srcChars.getAndAdvance(); | const char c = (char) srcChars.getAndAdvance(); | ||||
if (c == 0) | |||||
return true; | |||||
for (int j = 0; j < 64; ++j) | for (int j = 0; j < 64; ++j) | ||||
{ | { | ||||
if (base64EncodingTable[j] == c) | if (base64EncodingTable[j] == c) | ||||
@@ -404,6 +404,4 @@ bool MemoryBlock::fromBase64Encoding (const String& s) | |||||
} | } | ||||
} | } | ||||
} | } | ||||
return true; | |||||
} | } |
@@ -206,7 +206,7 @@ public: | |||||
@see String::toHexString() | @see String::toHexString() | ||||
*/ | */ | ||||
void loadFromHexString (const String& sourceHexString); | |||||
void loadFromHexString (StringRef sourceHexString); | |||||
//============================================================================== | //============================================================================== | ||||
/** Sets a number of bits in the memory block, treating it as a long binary sequence. */ | /** Sets a number of bits in the memory block, treating it as a long binary sequence. */ | ||||
@@ -235,7 +235,7 @@ public: | |||||
@see toBase64Encoding | @see toBase64Encoding | ||||
*/ | */ | ||||
bool fromBase64Encoding (const String& encodedString); | |||||
bool fromBase64Encoding (StringRef encodedString); | |||||
private: | private: | ||||
@@ -104,12 +104,12 @@ public: | |||||
void runTest() | void runTest() | ||||
{ | { | ||||
beginTest ("Basics"); | beginTest ("Basics"); | ||||
Random r; | |||||
Random r = getRandom(); | |||||
int randomInt = r.nextInt(); | int randomInt = r.nextInt(); | ||||
int64 randomInt64 = r.nextInt64(); | int64 randomInt64 = r.nextInt64(); | ||||
double randomDouble = r.nextDouble(); | double randomDouble = r.nextDouble(); | ||||
String randomString (createRandomWideCharString()); | |||||
String randomString (createRandomWideCharString (r)); | |||||
MemoryOutputStream mo; | MemoryOutputStream mo; | ||||
mo.writeInt (randomInt); | mo.writeInt (randomInt); | ||||
@@ -132,10 +132,9 @@ public: | |||||
expect (mi.readDoubleBigEndian() == randomDouble); | expect (mi.readDoubleBigEndian() == randomDouble); | ||||
} | } | ||||
static String createRandomWideCharString() | |||||
static String createRandomWideCharString (Random& r) | |||||
{ | { | ||||
juce_wchar buffer [50] = { 0 }; | juce_wchar buffer [50] = { 0 }; | ||||
Random r; | |||||
for (int i = 0; i < numElementsInArray (buffer) - 1; ++i) | for (int i = 0; i < numElementsInArray (buffer) - 1; ++i) | ||||
{ | { | ||||
@@ -2075,6 +2075,10 @@ String String::fromUTF8 (const char* const buffer, int bufferSizeBytes) | |||||
#endif | #endif | ||||
//============================================================================== | //============================================================================== | ||||
StringRef::StringRef() noexcept : text ("\0\0\0") | |||||
{ | |||||
} | |||||
StringRef::StringRef (const String::CharPointerType::CharType* stringLiteral) noexcept : text (stringLiteral) | StringRef::StringRef (const String::CharPointerType::CharType* stringLiteral) noexcept : text (stringLiteral) | ||||
{ | { | ||||
jassert (stringLiteral != nullptr); // This must be a valid string literal, not a null pointer!! | jassert (stringLiteral != nullptr); // This must be a valid string literal, not a null pointer!! | ||||
@@ -2086,8 +2090,8 @@ StringRef::StringRef (const String::CharPointerType::CharType* stringLiteral) no | |||||
create them. The source data could be UTF-8, ASCII or one of many local code-pages. | create them. The source data could be UTF-8, ASCII or one of many local code-pages. | ||||
To get around this problem, you must be more explicit when you pass an ambiguous 8-bit | To get around this problem, you must be more explicit when you pass an ambiguous 8-bit | ||||
string to the String class - so for example if your source data is actually UTF-8, | |||||
you'd call String (CharPointer_UTF8 ("my utf8 string..")), and it would be able to | |||||
string to the StringRef class - so for example if your source data is actually UTF-8, | |||||
you'd call StringRef (CharPointer_UTF8 ("my utf8 string..")), and it would be able to | |||||
correctly convert the multi-byte characters to unicode. It's *highly* recommended that | correctly convert the multi-byte characters to unicode. It's *highly* recommended that | ||||
you use UTF-8 with escape characters in your source code to represent extended characters, | you use UTF-8 with escape characters in your source code to represent extended characters, | ||||
because there's no other way to represent these strings in a way that isn't dependent on | because there's no other way to represent these strings in a way that isn't dependent on | ||||
@@ -2116,9 +2120,9 @@ public: | |||||
template <class CharPointerType> | template <class CharPointerType> | ||||
struct TestUTFConversion | struct TestUTFConversion | ||||
{ | { | ||||
static void test (UnitTest& test) | |||||
static void test (UnitTest& test, Random& r) | |||||
{ | { | ||||
String s (createRandomWideCharString()); | |||||
String s (createRandomWideCharString (r)); | |||||
typename CharPointerType::CharType buffer [300]; | typename CharPointerType::CharType buffer [300]; | ||||
@@ -2138,10 +2142,9 @@ public: | |||||
} | } | ||||
}; | }; | ||||
static String createRandomWideCharString() | |||||
static String createRandomWideCharString (Random& r) | |||||
{ | { | ||||
juce_wchar buffer[50] = { 0 }; | juce_wchar buffer[50] = { 0 }; | ||||
Random r; | |||||
for (int i = 0; i < numElementsInArray (buffer) - 1; ++i) | for (int i = 0; i < numElementsInArray (buffer) - 1; ++i) | ||||
{ | { | ||||
@@ -2162,6 +2165,8 @@ public: | |||||
void runTest() | void runTest() | ||||
{ | { | ||||
Random r = getRandom(); | |||||
{ | { | ||||
beginTest ("Basics"); | beginTest ("Basics"); | ||||
@@ -2402,9 +2407,9 @@ public: | |||||
{ | { | ||||
beginTest ("UTF conversions"); | beginTest ("UTF conversions"); | ||||
TestUTFConversion <CharPointer_UTF32>::test (*this); | |||||
TestUTFConversion <CharPointer_UTF8>::test (*this); | |||||
TestUTFConversion <CharPointer_UTF16>::test (*this); | |||||
TestUTFConversion <CharPointer_UTF32>::test (*this, r); | |||||
TestUTFConversion <CharPointer_UTF8>::test (*this, r); | |||||
TestUTFConversion <CharPointer_UTF16>::test (*this, r); | |||||
} | } | ||||
{ | { | ||||
@@ -86,6 +86,9 @@ public: | |||||
*/ | */ | ||||
StringRef (const String& string) noexcept; | StringRef (const String& string) noexcept; | ||||
/** Creates a StringRef pointer to an empty string. */ | |||||
StringRef() noexcept; | |||||
//============================================================================== | //============================================================================== | ||||
/** Returns a raw pointer to the underlying string data. */ | /** Returns a raw pointer to the underlying string data. */ | ||||
operator const String::CharPointerType::CharType*() const noexcept { return text.getAddress(); } | operator const String::CharPointerType::CharType*() const noexcept { return text.getAddress(); } | ||||
@@ -191,10 +191,9 @@ class DiffTests : public UnitTest | |||||
public: | public: | ||||
DiffTests() : UnitTest ("TextDiff class") {} | DiffTests() : UnitTest ("TextDiff class") {} | ||||
static String createString() | |||||
static String createString (Random& r) | |||||
{ | { | ||||
juce_wchar buffer[50] = { 0 }; | juce_wchar buffer[50] = { 0 }; | ||||
Random r; | |||||
for (int i = r.nextInt (49); --i >= 0;) | for (int i = r.nextInt (49); --i >= 0;) | ||||
{ | { | ||||
@@ -224,6 +223,8 @@ public: | |||||
{ | { | ||||
beginTest ("TextDiff"); | beginTest ("TextDiff"); | ||||
Random r = getRandom(); | |||||
testDiff (String::empty, String::empty); | testDiff (String::empty, String::empty); | ||||
testDiff ("x", String::empty); | testDiff ("x", String::empty); | ||||
testDiff (String::empty, "x"); | testDiff (String::empty, "x"); | ||||
@@ -234,9 +235,9 @@ public: | |||||
for (int i = 5000; --i >= 0;) | for (int i = 5000; --i >= 0;) | ||||
{ | { | ||||
String s (createString()); | |||||
testDiff (s, createString()); | |||||
testDiff (s + createString(), s + createString()); | |||||
String s (createString (r)); | |||||
testDiff (s, createString (r)); | |||||
testDiff (s + createString (r), s + createString (r)); | |||||
} | } | ||||
} | } | ||||
}; | }; | ||||
@@ -26,8 +26,8 @@ | |||||
============================================================================== | ============================================================================== | ||||
*/ | */ | ||||
UnitTest::UnitTest (const String& name_) | |||||
: name (name_), runner (nullptr) | |||||
UnitTest::UnitTest (const String& nm) | |||||
: name (nm), runner (nullptr) | |||||
{ | { | ||||
getAllTests().add (this); | getAllTests().add (this); | ||||
} | } | ||||
@@ -46,10 +46,10 @@ Array<UnitTest*>& UnitTest::getAllTests() | |||||
void UnitTest::initialise() {} | void UnitTest::initialise() {} | ||||
void UnitTest::shutdown() {} | void UnitTest::shutdown() {} | ||||
void UnitTest::performTest (UnitTestRunner* const runner_) | |||||
void UnitTest::performTest (UnitTestRunner* const newRunner) | |||||
{ | { | ||||
jassert (runner_ != nullptr); | |||||
runner = runner_; | |||||
jassert (newRunner != nullptr); | |||||
runner = newRunner; | |||||
initialise(); | initialise(); | ||||
runTest(); | runTest(); | ||||
@@ -58,22 +58,39 @@ void UnitTest::performTest (UnitTestRunner* const runner_) | |||||
void UnitTest::logMessage (const String& message) | void UnitTest::logMessage (const String& message) | ||||
{ | { | ||||
// This method's only valid while the test is being run! | |||||
jassert (runner != nullptr); | |||||
runner->logMessage (message); | runner->logMessage (message); | ||||
} | } | ||||
void UnitTest::beginTest (const String& testName) | void UnitTest::beginTest (const String& testName) | ||||
{ | { | ||||
// This method's only valid while the test is being run! | |||||
jassert (runner != nullptr); | |||||
runner->beginNewTest (this, testName); | runner->beginNewTest (this, testName); | ||||
} | } | ||||
void UnitTest::expect (const bool result, const String& failureMessage) | void UnitTest::expect (const bool result, const String& failureMessage) | ||||
{ | { | ||||
// This method's only valid while the test is being run! | |||||
jassert (runner != nullptr); | |||||
if (result) | if (result) | ||||
runner->addPass(); | runner->addPass(); | ||||
else | else | ||||
runner->addFail (failureMessage); | runner->addFail (failureMessage); | ||||
} | } | ||||
Random UnitTest::getRandom() const | |||||
{ | |||||
// This method's only valid while the test is being run! | |||||
jassert (runner != nullptr); | |||||
return runner->randomForTest; | |||||
} | |||||
//============================================================================== | //============================================================================== | ||||
UnitTestRunner::UnitTestRunner() | UnitTestRunner::UnitTestRunner() | ||||
: currentTest (nullptr), | : currentTest (nullptr), | ||||
@@ -110,11 +127,17 @@ void UnitTestRunner::resultsUpdated() | |||||
{ | { | ||||
} | } | ||||
void UnitTestRunner::runTests (const Array<UnitTest*>& tests) | |||||
void UnitTestRunner::runTests (const Array<UnitTest*>& tests, int64 randomSeed) | |||||
{ | { | ||||
results.clear(); | results.clear(); | ||||
resultsUpdated(); | resultsUpdated(); | ||||
if (randomSeed == 0) | |||||
randomSeed = Random().nextInt (0x7ffffff); | |||||
randomForTest = Random (randomSeed); | |||||
logMessage ("Random seed: 0x" + String::toHexString (randomSeed)); | |||||
for (int i = 0; i < tests.size(); ++i) | for (int i = 0; i < tests.size(); ++i) | ||||
{ | { | ||||
if (shouldAbortTests()) | if (shouldAbortTests()) | ||||
@@ -133,9 +156,9 @@ void UnitTestRunner::runTests (const Array<UnitTest*>& tests) | |||||
endTest(); | endTest(); | ||||
} | } | ||||
void UnitTestRunner::runAllTests() | |||||
void UnitTestRunner::runAllTests (int64 randomSeed) | |||||
{ | { | ||||
runTests (UnitTest::getAllTests()); | |||||
runTests (UnitTest::getAllTests(), randomSeed); | |||||
} | } | ||||
void UnitTestRunner::logMessage (const String& message) | void UnitTestRunner::logMessage (const String& message) | ||||
@@ -163,6 +163,22 @@ public: | |||||
*/ | */ | ||||
void logMessage (const String& message); | void logMessage (const String& message); | ||||
/** Returns a shared RNG that all unit tests should use. | |||||
If a test needs random numbers, it's important that when an error is found, the | |||||
exact circumstances can be re-created in order to re-test the problem, by | |||||
repeating the test with the same random seed value. | |||||
To make this possible, the UnitTestRunner class creates a master seed value | |||||
for the run, writes this number to the log, and then this method returns a | |||||
Random object based on that seed. All tests should only use this method to | |||||
create any Random objects that they need. | |||||
Note that this method will return an identical object each time it's called | |||||
for a given run, so if you need several different Random objects, the best | |||||
way to do that is to call Random::combineSeed() on the result to permute it | |||||
with a constant value. | |||||
*/ | |||||
Random getRandom() const; | |||||
private: | private: | ||||
//============================================================================== | //============================================================================== | ||||
const String name; | const String name; | ||||
@@ -198,13 +214,19 @@ public: | |||||
The tests are performed in order, and the results are logged. To run all the | The tests are performed in order, and the results are logged. To run all the | ||||
registered UnitTest objects that exist, use runAllTests(). | registered UnitTest objects that exist, use runAllTests(). | ||||
If you want to run the tests with a predetermined seed, you can pass that into | |||||
the randomSeed argument, or pass 0 to have a randomly-generated seed chosen. | |||||
*/ | */ | ||||
void runTests (const Array<UnitTest*>& tests); | |||||
void runTests (const Array<UnitTest*>& tests, int64 randomSeed = 0); | |||||
/** Runs all the UnitTest objects that currently exist. | /** Runs all the UnitTest objects that currently exist. | ||||
This calls runTests() for all the objects listed in UnitTest::getAllTests(). | This calls runTests() for all the objects listed in UnitTest::getAllTests(). | ||||
If you want to run the tests with a predetermined seed, you can pass that into | |||||
the randomSeed argument, or pass 0 to have a randomly-generated seed chosen. | |||||
*/ | */ | ||||
void runAllTests(); | |||||
void runAllTests (int64 randomSeed = 0); | |||||
/** Sets a flag to indicate whether an assertion should be triggered if a test fails. | /** Sets a flag to indicate whether an assertion should be triggered if a test fails. | ||||
This is true by default. | This is true by default. | ||||
@@ -274,6 +296,7 @@ private: | |||||
String currentSubCategory; | String currentSubCategory; | ||||
OwnedArray <TestResult, CriticalSection> results; | OwnedArray <TestResult, CriticalSection> results; | ||||
bool assertOnFailure, logPasses; | bool assertOnFailure, logPasses; | ||||
Random randomForTest; | |||||
void beginNewTest (UnitTest* test, const String& subCategory); | void beginNewTest (UnitTest* test, const String& subCategory); | ||||
void endTest(); | void endTest(); | ||||
@@ -169,7 +169,7 @@ public: | |||||
void runTest() | void runTest() | ||||
{ | { | ||||
beginTest ("GZIP"); | beginTest ("GZIP"); | ||||
Random rng; | |||||
Random rng = getRandom(); | |||||
for (int i = 100; --i >= 0;) | for (int i = 100; --i >= 0;) | ||||
{ | { | ||||
@@ -221,10 +221,10 @@ MD5::MD5 (CharPointer_UTF8 utf8) noexcept | |||||
processData (utf8.getAddress(), utf8.sizeInBytes() - 1); | processData (utf8.getAddress(), utf8.sizeInBytes() - 1); | ||||
} | } | ||||
MD5 MD5::fromUTF32 (const String& text) | |||||
MD5 MD5::fromUTF32 (StringRef text) | |||||
{ | { | ||||
MD5Generator generator; | MD5Generator generator; | ||||
String::CharPointerType t (text.getCharPointer()); | |||||
String::CharPointerType t (text.text); | |||||
while (! t.isEmpty()) | while (! t.isEmpty()) | ||||
{ | { | ||||
@@ -94,7 +94,7 @@ public: | |||||
this operation on it. In new code, you shouldn't use this, and are recommended to | this operation on it. In new code, you shouldn't use this, and are recommended to | ||||
use the constructor that takes a CharPointer_UTF8 instead. | use the constructor that takes a CharPointer_UTF8 instead. | ||||
*/ | */ | ||||
static MD5 fromUTF32 (const String&); | |||||
static MD5 fromUTF32 (StringRef); | |||||
//============================================================================== | //============================================================================== | ||||
bool operator== (const MD5&) const noexcept; | bool operator== (const MD5&) const noexcept; | ||||
@@ -1019,11 +1019,10 @@ class ValueTreeTests : public UnitTest | |||||
public: | public: | ||||
ValueTreeTests() : UnitTest ("ValueTrees") {} | ValueTreeTests() : UnitTest ("ValueTrees") {} | ||||
static String createRandomIdentifier() | |||||
static String createRandomIdentifier (Random& r) | |||||
{ | { | ||||
char buffer[50] = { 0 }; | char buffer[50] = { 0 }; | ||||
const char chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-:"; | const char chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-:"; | ||||
Random r; | |||||
for (int i = 1 + r.nextInt (numElementsInArray (buffer) - 2); --i >= 0;) | for (int i = 1 + r.nextInt (numElementsInArray (buffer) - 2); --i >= 0;) | ||||
buffer[i] = chars [r.nextInt (sizeof (chars) - 1)]; | buffer[i] = chars [r.nextInt (sizeof (chars) - 1)]; | ||||
@@ -1031,10 +1030,9 @@ public: | |||||
return CharPointer_ASCII (buffer); | return CharPointer_ASCII (buffer); | ||||
} | } | ||||
static String createRandomWideCharString() | |||||
static String createRandomWideCharString (Random& r) | |||||
{ | { | ||||
juce_wchar buffer[50] = { 0 }; | juce_wchar buffer[50] = { 0 }; | ||||
Random r; | |||||
for (int i = r.nextInt (numElementsInArray (buffer) - 1); --i >= 0;) | for (int i = r.nextInt (numElementsInArray (buffer) - 1); --i >= 0;) | ||||
{ | { | ||||
@@ -1053,20 +1051,19 @@ public: | |||||
return CharPointer_UTF32 (buffer); | return CharPointer_UTF32 (buffer); | ||||
} | } | ||||
static ValueTree createRandomTree (UndoManager* undoManager, int depth) | |||||
static ValueTree createRandomTree (UndoManager* undoManager, int depth, Random& r) | |||||
{ | { | ||||
Random r; | |||||
ValueTree v (createRandomIdentifier()); | |||||
ValueTree v (createRandomIdentifier (r)); | |||||
for (int i = r.nextInt (10); --i >= 0;) | for (int i = r.nextInt (10); --i >= 0;) | ||||
{ | { | ||||
switch (r.nextInt (5)) | switch (r.nextInt (5)) | ||||
{ | { | ||||
case 0: v.setProperty (createRandomIdentifier(), createRandomWideCharString(), undoManager); break; | |||||
case 1: v.setProperty (createRandomIdentifier(), r.nextInt(), undoManager); break; | |||||
case 2: if (depth < 5) v.addChild (createRandomTree (undoManager, depth + 1), r.nextInt (v.getNumChildren() + 1), undoManager); break; | |||||
case 3: v.setProperty (createRandomIdentifier(), r.nextBool(), undoManager); break; | |||||
case 4: v.setProperty (createRandomIdentifier(), r.nextDouble(), undoManager); break; | |||||
case 0: v.setProperty (createRandomIdentifier (r), createRandomWideCharString (r), undoManager); break; | |||||
case 1: v.setProperty (createRandomIdentifier (r), r.nextInt(), undoManager); break; | |||||
case 2: if (depth < 5) v.addChild (createRandomTree (undoManager, depth + 1, r), r.nextInt (v.getNumChildren() + 1), undoManager); break; | |||||
case 3: v.setProperty (createRandomIdentifier (r), r.nextBool(), undoManager); break; | |||||
case 4: v.setProperty (createRandomIdentifier (r), r.nextDouble(), undoManager); break; | |||||
default: break; | default: break; | ||||
} | } | ||||
} | } | ||||
@@ -1077,11 +1074,12 @@ public: | |||||
void runTest() | void runTest() | ||||
{ | { | ||||
beginTest ("ValueTree"); | beginTest ("ValueTree"); | ||||
Random r = getRandom(); | |||||
for (int i = 10; --i >= 0;) | for (int i = 10; --i >= 0;) | ||||
{ | { | ||||
MemoryOutputStream mo; | MemoryOutputStream mo; | ||||
ValueTree v1 (createRandomTree (nullptr, 0)); | |||||
ValueTree v1 (createRandomTree (nullptr, 0, r)); | |||||
v1.writeToStream (mo); | v1.writeToStream (mo); | ||||
MemoryInputStream mi (mo.getData(), mo.getDataSize(), false); | MemoryInputStream mi (mo.getData(), mo.getDataSize(), false); | ||||
@@ -1475,12 +1475,12 @@ String Path::toString() const | |||||
return s.toUTF8(); | return s.toUTF8(); | ||||
} | } | ||||
void Path::restoreFromString (const String& stringVersion) | |||||
void Path::restoreFromString (StringRef stringVersion) | |||||
{ | { | ||||
clear(); | clear(); | ||||
setUsingNonZeroWinding (true); | setUsingNonZeroWinding (true); | ||||
String::CharPointerType t (stringVersion.getCharPointer()); | |||||
String::CharPointerType t (stringVersion.text); | |||||
juce_wchar marker = 'm'; | juce_wchar marker = 'm'; | ||||
int numValues = 2; | int numValues = 2; | ||||
float values [6]; | float values [6]; | ||||
@@ -740,7 +740,7 @@ public: | |||||
/** Restores this path from a string that was created with the toString() method. | /** Restores this path from a string that was created with the toString() method. | ||||
@see toString() | @see toString() | ||||
*/ | */ | ||||
void restoreFromString (const String& stringVersion); | |||||
void restoreFromString (StringRef stringVersion); | |||||
private: | private: | ||||
@@ -25,20 +25,21 @@ | |||||
class CodeDocumentLine | class CodeDocumentLine | ||||
{ | { | ||||
public: | public: | ||||
CodeDocumentLine (const String::CharPointerType l, | |||||
CodeDocumentLine (const String::CharPointerType startOfLine, | |||||
const String::CharPointerType endOfLine, | |||||
const int lineLen, | const int lineLen, | ||||
const int numNewLineChars, | const int numNewLineChars, | ||||
const int startInFile) | const int startInFile) | ||||
: line (l, (size_t) lineLen), | |||||
: line (startOfLine, endOfLine), | |||||
lineStartInFile (startInFile), | lineStartInFile (startInFile), | ||||
lineLength (lineLen), | lineLength (lineLen), | ||||
lineLengthWithoutNewLines (lineLen - numNewLineChars) | lineLengthWithoutNewLines (lineLen - numNewLineChars) | ||||
{ | { | ||||
} | } | ||||
static void createLines (Array <CodeDocumentLine*>& newLines, const String& text) | |||||
static void createLines (Array<CodeDocumentLine*>& newLines, StringRef text) | |||||
{ | { | ||||
String::CharPointerType t (text.getCharPointer()); | |||||
String::CharPointerType t (text.text); | |||||
int charNumInFile = 0; | int charNumInFile = 0; | ||||
bool finished = false; | bool finished = false; | ||||
@@ -84,7 +85,7 @@ public: | |||||
} | } | ||||
} | } | ||||
newLines.add (new CodeDocumentLine (startOfLine, lineLength, | |||||
newLines.add (new CodeDocumentLine (startOfLine, t, lineLength, | |||||
numNewLineChars, startOfLineInFile)); | numNewLineChars, startOfLineInFile)); | ||||
} | } | ||||
@@ -791,7 +792,8 @@ void CodeDocument::checkLastLineStatus() | |||||
if (lastLine != nullptr && lastLine->endsWithLineBreak()) | if (lastLine != nullptr && lastLine->endsWithLineBreak()) | ||||
{ | { | ||||
// check that there's an empty line at the end if the preceding one ends in a newline.. | // check that there's an empty line at the end if the preceding one ends in a newline.. | ||||
lines.add (new CodeDocumentLine (String::empty.getCharPointer(), 0, 0, lastLine->lineStartInFile + lastLine->lineLength)); | |||||
lines.add (new CodeDocumentLine (StringRef(), StringRef(), 0, 0, | |||||
lastLine->lineStartInFile + lastLine->lineLength)); | |||||
} | } | ||||
} | } | ||||
@@ -227,9 +227,9 @@ private: | |||||
namespace CodeEditorHelpers | namespace CodeEditorHelpers | ||||
{ | { | ||||
static int findFirstNonWhitespaceChar (const String& line) noexcept | |||||
static int findFirstNonWhitespaceChar (StringRef line) noexcept | |||||
{ | { | ||||
String::CharPointerType t (line.getCharPointer()); | |||||
String::CharPointerType t (line.text); | |||||
int i = 0; | int i = 0; | ||||
while (! t.isEmpty()) | while (! t.isEmpty()) | ||||
@@ -1418,7 +1418,8 @@ String CodeEditorComponent::getTabString (const int numSpaces) const | |||||
int CodeEditorComponent::indexToColumn (int lineNum, int index) const noexcept | int CodeEditorComponent::indexToColumn (int lineNum, int index) const noexcept | ||||
{ | { | ||||
String::CharPointerType t (document.getLine (lineNum).getCharPointer()); | |||||
const String line (document.getLine (lineNum)); | |||||
String::CharPointerType t (line.getCharPointer()); | |||||
int col = 0; | int col = 0; | ||||
for (int i = 0; i < index; ++i) | for (int i = 0; i < index; ++i) | ||||
@@ -1440,7 +1441,8 @@ int CodeEditorComponent::indexToColumn (int lineNum, int index) const noexcept | |||||
int CodeEditorComponent::columnToIndex (int lineNum, int column) const noexcept | int CodeEditorComponent::columnToIndex (int lineNum, int column) const noexcept | ||||
{ | { | ||||
String::CharPointerType t (document.getLine (lineNum).getCharPointer()); | |||||
const String line (document.getLine (lineNum)); | |||||
String::CharPointerType t (line.getCharPointer()); | |||||
int i = 0, col = 0; | int i = 0, col = 0; | ||||