blob: a304eb823af861a0f46c0bb4d7991248ba247842 [file] [log] [blame]
/*
*******************************************************************************
* Copyright (C) 2014, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*
* File LRUCACHETEST.CPP
*
********************************************************************************
*/
#include "cstring.h"
#include "intltest.h"
#include "lrucache.h"
#include "sharedptr.h"
class CopyOnWriteForTesting : public SharedObject {
public:
CopyOnWriteForTesting() : SharedObject(), localeNamePtr(), formatStrPtr(), length(0) {
}
CopyOnWriteForTesting(const CopyOnWriteForTesting &other) :
SharedObject(other),
localeNamePtr(other.localeNamePtr),
formatStrPtr(other.formatStrPtr),
length(other.length) {
}
virtual ~CopyOnWriteForTesting() {
}
SharedPtr<UnicodeString> localeNamePtr;
SharedPtr<UnicodeString> formatStrPtr;
int32_t length;
private:
CopyOnWriteForTesting &operator=(const CopyOnWriteForTesting &rhs);
};
class LRUCacheForTesting : public LRUCache {
public:
LRUCacheForTesting(
int32_t maxSize,
const UnicodeString &dfs, UErrorCode &status);
virtual ~LRUCacheForTesting() {
}
protected:
virtual SharedObject *create(const char *localeId, UErrorCode &status);
private:
SharedPtr<UnicodeString> defaultFormatStr;
};
LRUCacheForTesting::LRUCacheForTesting(
int32_t maxSize,
const UnicodeString &dfs, UErrorCode &status) :
LRUCache(maxSize, status), defaultFormatStr() {
if (U_FAILURE(status)) {
return;
}
defaultFormatStr.reset(new UnicodeString(dfs));
}
SharedObject *LRUCacheForTesting::create(const char *localeId, UErrorCode &status) {
if (uprv_strcmp(localeId, "error") == 0) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return NULL;
}
CopyOnWriteForTesting *result = new CopyOnWriteForTesting;
result->localeNamePtr.reset(new UnicodeString(localeId));
result->formatStrPtr = defaultFormatStr;
result->length = 5;
return result;
}
class LRUCacheTest : public IntlTest {
public:
LRUCacheTest() {
}
void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par=0);
private:
void TestSharedPointer();
void TestErrorCallingConstructor();
void TestLRUCache();
void TestLRUCacheError();
void verifySharedPointer(
const CopyOnWriteForTesting* ptr,
const UnicodeString& name,
const UnicodeString& format);
void verifyString(
const UnicodeString &expected, const UnicodeString &actual);
void verifyReferences(
const CopyOnWriteForTesting* ptr,
int32_t count, int32_t nameCount, int32_t formatCount);
};
void LRUCacheTest::runIndexedTest(int32_t index, UBool exec, const char* &name, char* /*par*/) {
TESTCASE_AUTO_BEGIN;
TESTCASE_AUTO(TestSharedPointer);
TESTCASE_AUTO(TestErrorCallingConstructor);
TESTCASE_AUTO(TestLRUCache);
TESTCASE_AUTO(TestLRUCacheError);
TESTCASE_AUTO_END;
}
void LRUCacheTest::TestSharedPointer() {
UErrorCode status = U_ZERO_ERROR;
LRUCacheForTesting cache(3, "little", status);
const CopyOnWriteForTesting* ptr = NULL;
cache.get("boo", ptr, status);
verifySharedPointer(ptr, "boo", "little");
const CopyOnWriteForTesting* ptrCopy = ptr;
ptrCopy->addRef();
{
const CopyOnWriteForTesting* ptrCopy2(ptrCopy);
ptrCopy2->addRef();
verifyReferences(ptr, 4, 1, 2);
ptrCopy2->removeRef();
}
verifyReferences(ptr, 3, 1, 2);
CopyOnWriteForTesting *wPtrCopy = SharedObject::copyOnWrite(ptrCopy);
*wPtrCopy->localeNamePtr.readWrite() = UnicodeString("hi there");
*wPtrCopy->formatStrPtr.readWrite() = UnicodeString("see you");
verifyReferences(ptr, 2, 1, 2);
verifyReferences(ptrCopy, 1, 1, 1);
verifySharedPointer(ptr, "boo", "little");
verifySharedPointer(ptrCopy, "hi there", "see you");
ptrCopy->removeRef();
ptr->removeRef();
}
void LRUCacheTest::TestErrorCallingConstructor() {
UErrorCode status = U_MEMORY_ALLOCATION_ERROR;
LRUCacheForTesting cache(3, "little", status);
}
void LRUCacheTest::TestLRUCache() {
UErrorCode status = U_ZERO_ERROR;
LRUCacheForTesting cache(3, "little", status);
const CopyOnWriteForTesting* ptr1 = NULL;
const CopyOnWriteForTesting* ptr2 = NULL;
const CopyOnWriteForTesting* ptr3 = NULL;
const CopyOnWriteForTesting* ptr4 = NULL;
const CopyOnWriteForTesting* ptr5 = NULL;
cache.get("foo", ptr1, status);
cache.get("bar", ptr2, status);
cache.get("baz", ptr3, status);
verifySharedPointer(ptr1, "foo", "little");
verifySharedPointer(ptr2, "bar", "little");
verifySharedPointer(ptr3, "baz", "little");
// Cache holds a reference to returned data which explains the 2s
// Note the '4'. each cached data has a reference to "little" and the
// cache itself also has a reference to "little"
verifyReferences(ptr1, 2, 1, 4);
verifyReferences(ptr2, 2, 1, 4);
verifyReferences(ptr3, 2, 1, 4);
// (Most recent) "baz", "bar", "foo" (Least Recent)
// Cache is now full but thanks to shared pointers we can still evict.
cache.get("full", ptr4, status);
verifySharedPointer(ptr4, "full", "little");
verifyReferences(ptr4, 2, 1, 5);
// (Most Recent) "full" "baz", "bar" (Least Recent)
cache.get("baz", ptr5, status);
verifySharedPointer(ptr5, "baz", "little");
// ptr5, ptr3, and cache have baz data
verifyReferences(ptr5, 3, 1, 5);
// This should delete foo data since it got evicted from cache.
ptr1->removeRef();
ptr1 = NULL;
// Reference count for little drops to 4 because foo data was deleted.
verifyReferences(ptr5, 3, 1, 4);
// (Most Recent) "baz" "full" "bar" (Least Recent)
cache.get("baz", ptr5, status);
verifySharedPointer(ptr5, "baz", "little");
verifyReferences(ptr5, 3, 1, 4);
// (Most Recent) "baz", "full", "bar" (Least Recent)
// ptr3, ptr5 -> "baz" ptr4 -> "full" ptr2 -> "bar"
if (!cache.contains("baz") || !cache.contains("full") || !cache.contains("bar") || cache.contains("foo")) {
errln("Unexpected keys in cache.");
}
cache.get("new1", ptr5, status);
verifySharedPointer(ptr5, "new1", "little");
verifyReferences(ptr5, 2, 1, 5);
// Since bar was evicted, clearing its pointer should delete its data.
// Notice that the reference count to 'little' dropped from 5 to 4.
ptr2->removeRef();
ptr2 = NULL;
verifyReferences(ptr5, 2, 1, 4);
if (cache.contains("bar") || !cache.contains("full")) {
errln("Unexpected 'bar' in cache.");
}
// (Most Recent) "new1", "baz", "full" (Least Recent)
// ptr3 -> "baz" ptr4 -> "full" ptr5 -> "new1"
cache.get("new2", ptr5, status);
verifySharedPointer(ptr5, "new2", "little");
verifyReferences(ptr5, 2, 1, 5);
// since "full" was evicted, clearing its pointer should delete its data.
ptr4->removeRef();
ptr4 = NULL;
verifyReferences(ptr5, 2, 1, 4);
if (cache.contains("full") || !cache.contains("baz")) {
errln("Unexpected 'full' in cache.");
}
// (Most Recent) "new2", "new1", "baz" (Least Recent)
// ptr3 -> "baz" ptr5 -> "new2"
cache.get("new3", ptr5, status);
verifySharedPointer(ptr5, "new3", "little");
verifyReferences(ptr5, 2, 1, 5);
// since "baz" was evicted, clearing its pointer should delete its data.
ptr3->removeRef();
ptr3 = NULL;
verifyReferences(ptr5, 2, 1, 4);
if (cache.contains("baz") || !cache.contains("new3")) {
errln("Unexpected 'baz' in cache.");
}
SharedObject::clearPtr(ptr1);
SharedObject::clearPtr(ptr2);
SharedObject::clearPtr(ptr3);
SharedObject::clearPtr(ptr4);
SharedObject::clearPtr(ptr5);
}
void LRUCacheTest::TestLRUCacheError() {
UErrorCode status = U_ZERO_ERROR;
LRUCacheForTesting cache(3, "little", status);
const CopyOnWriteForTesting *ptr1;
cache.get("error", ptr1, status);
if (status != U_ILLEGAL_ARGUMENT_ERROR) {
errln("Expected an error.");
}
}
void LRUCacheTest::verifySharedPointer(
const CopyOnWriteForTesting* ptr,
const UnicodeString& name,
const UnicodeString& format) {
const UnicodeString *strPtr = ptr->localeNamePtr.readOnly();
verifyString(name, *strPtr);
strPtr = ptr->formatStrPtr.readOnly();
verifyString(format, *strPtr);
}
void LRUCacheTest::verifyString(const UnicodeString &expected, const UnicodeString &actual) {
if (expected != actual) {
errln(UnicodeString("Expected '") + expected + "', got '"+ actual+"'");
}
}
void LRUCacheTest::verifyReferences(const CopyOnWriteForTesting* ptr, int32_t count, int32_t nameCount, int32_t formatCount) {
int32_t actual = ptr->getRefCount();
if (count != actual) {
errln("Main reference count wrong: Expected %d, got %d", count, actual);
}
actual = ptr->localeNamePtr.count();
if (nameCount != actual) {
errln("name reference count wrong: Expected %d, got %d", nameCount, actual);
}
actual = ptr->formatStrPtr.count();
if (formatCount != actual) {
errln("format reference count wrong: Expected %d, got %d", formatCount, actual);
}
}
extern IntlTest *createLRUCacheTest() {
return new LRUCacheTest();
}