| /* |
| ******************************************************************************* |
| * 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(); |
| } |