1/*
2*******************************************************************************
3* Copyright (C) 2014, International Business Machines Corporation and         *
4* others. All Rights Reserved.                                                *
5*******************************************************************************
6*
7* File LRUCACHETEST.CPP
8*
9********************************************************************************
10*/
11#include "cstring.h"
12#include "intltest.h"
13#include "lrucache.h"
14#include "sharedptr.h"
15
16class CopyOnWriteForTesting : public SharedObject {
17public:
18    CopyOnWriteForTesting() : SharedObject(), localeNamePtr(), formatStrPtr(), length(0) {
19    }
20
21    CopyOnWriteForTesting(const CopyOnWriteForTesting &other) :
22        SharedObject(other),
23        localeNamePtr(other.localeNamePtr),
24        formatStrPtr(other.formatStrPtr),
25        length(other.length) {
26    }
27
28    virtual ~CopyOnWriteForTesting() {
29    }
30
31    SharedPtr<UnicodeString> localeNamePtr;
32    SharedPtr<UnicodeString> formatStrPtr;
33    int32_t length;
34private:
35    CopyOnWriteForTesting &operator=(const CopyOnWriteForTesting &rhs);
36};
37
38class LRUCacheForTesting : public LRUCache {
39public:
40    LRUCacheForTesting(
41        int32_t maxSize,
42        const UnicodeString &dfs, UErrorCode &status);
43    virtual ~LRUCacheForTesting() {
44    }
45protected:
46    virtual SharedObject *create(const char *localeId, UErrorCode &status);
47private:
48    SharedPtr<UnicodeString> defaultFormatStr;
49};
50
51LRUCacheForTesting::LRUCacheForTesting(
52        int32_t maxSize,
53        const UnicodeString &dfs, UErrorCode &status) :
54    LRUCache(maxSize, status), defaultFormatStr() {
55    if (U_FAILURE(status)) {
56        return;
57    }
58    defaultFormatStr.reset(new UnicodeString(dfs));
59}
60
61SharedObject *LRUCacheForTesting::create(const char *localeId, UErrorCode &status) {
62    if (uprv_strcmp(localeId, "error") == 0) {
63        status = U_ILLEGAL_ARGUMENT_ERROR;
64        return NULL;
65    }
66    CopyOnWriteForTesting *result = new CopyOnWriteForTesting;
67    result->localeNamePtr.reset(new UnicodeString(localeId));
68    result->formatStrPtr = defaultFormatStr;
69    result->length = 5;
70    return result;
71}
72
73class LRUCacheTest : public IntlTest {
74public:
75    LRUCacheTest() {
76    }
77    void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par=0);
78private:
79    void TestSharedPointer();
80    void TestErrorCallingConstructor();
81    void TestLRUCache();
82    void TestLRUCacheError();
83    void verifySharedPointer(
84            const CopyOnWriteForTesting* ptr,
85            const UnicodeString& name,
86            const UnicodeString& format);
87    void verifyString(
88            const UnicodeString &expected, const UnicodeString &actual);
89    void verifyReferences(
90            const CopyOnWriteForTesting* ptr,
91            int32_t count, int32_t nameCount, int32_t formatCount);
92};
93
94void LRUCacheTest::runIndexedTest(int32_t index, UBool exec, const char* &name, char* /*par*/) {
95  TESTCASE_AUTO_BEGIN;
96  TESTCASE_AUTO(TestSharedPointer);
97  TESTCASE_AUTO(TestErrorCallingConstructor);
98  TESTCASE_AUTO(TestLRUCache);
99  TESTCASE_AUTO(TestLRUCacheError);
100  TESTCASE_AUTO_END;
101}
102
103void LRUCacheTest::TestSharedPointer() {
104    UErrorCode status = U_ZERO_ERROR;
105    LRUCacheForTesting cache(3, "little", status);
106    const CopyOnWriteForTesting* ptr = NULL;
107    cache.get("boo", ptr, status);
108    verifySharedPointer(ptr, "boo", "little");
109    const CopyOnWriteForTesting* ptrCopy = ptr;
110    ptrCopy->addRef();
111    {
112        const CopyOnWriteForTesting* ptrCopy2(ptrCopy);
113        ptrCopy2->addRef();
114        verifyReferences(ptr, 4, 1, 2);
115        ptrCopy2->removeRef();
116    }
117
118    verifyReferences(ptr, 3, 1, 2);
119    CopyOnWriteForTesting *wPtrCopy = SharedObject::copyOnWrite(ptrCopy);
120    *wPtrCopy->localeNamePtr.readWrite() = UnicodeString("hi there");
121    *wPtrCopy->formatStrPtr.readWrite() = UnicodeString("see you");
122    verifyReferences(ptr, 2, 1, 2);
123    verifyReferences(ptrCopy, 1, 1, 1);
124    verifySharedPointer(ptr, "boo", "little");
125    verifySharedPointer(ptrCopy, "hi there", "see you");
126    ptrCopy->removeRef();
127    ptr->removeRef();
128}
129
130void LRUCacheTest::TestErrorCallingConstructor() {
131    UErrorCode status = U_MEMORY_ALLOCATION_ERROR;
132    LRUCacheForTesting cache(3, "little", status);
133}
134
135void LRUCacheTest::TestLRUCache() {
136    UErrorCode status = U_ZERO_ERROR;
137    LRUCacheForTesting cache(3, "little", status);
138    const CopyOnWriteForTesting* ptr1 = NULL;
139    const CopyOnWriteForTesting* ptr2 = NULL;
140    const CopyOnWriteForTesting* ptr3 = NULL;
141    const CopyOnWriteForTesting* ptr4 = NULL;
142    const CopyOnWriteForTesting* ptr5 = NULL;
143    cache.get("foo", ptr1, status);
144    cache.get("bar", ptr2, status);
145    cache.get("baz", ptr3, status);
146    verifySharedPointer(ptr1, "foo", "little");
147    verifySharedPointer(ptr2, "bar", "little");
148    verifySharedPointer(ptr3, "baz", "little");
149
150    // Cache holds a reference to returned data which explains the 2s
151    // Note the '4'. each cached data has a reference to "little" and the
152    // cache itself also has a reference to "little"
153    verifyReferences(ptr1, 2, 1, 4);
154    verifyReferences(ptr2, 2, 1, 4);
155    verifyReferences(ptr3, 2, 1, 4);
156
157    // (Most recent) "baz", "bar", "foo" (Least Recent)
158    // Cache is now full but thanks to shared pointers we can still evict.
159    cache.get("full", ptr4, status);
160    verifySharedPointer(ptr4, "full", "little");
161
162    verifyReferences(ptr4, 2, 1, 5);
163
164    // (Most Recent) "full" "baz", "bar" (Least Recent)
165    cache.get("baz", ptr5, status);
166    verifySharedPointer(ptr5, "baz", "little");
167    // ptr5, ptr3, and cache have baz data
168    verifyReferences(ptr5, 3, 1, 5);
169
170    // This should delete foo data since it got evicted from cache.
171    ptr1->removeRef();
172    ptr1 = NULL;
173    // Reference count for little drops to 4 because foo data was deleted.
174    verifyReferences(ptr5, 3, 1, 4);
175
176    // (Most Recent) "baz" "full" "bar" (Least Recent)
177    cache.get("baz", ptr5, status);
178    verifySharedPointer(ptr5, "baz", "little");
179    verifyReferences(ptr5, 3, 1, 4);
180
181    // (Most Recent) "baz", "full", "bar" (Least Recent)
182    // ptr3, ptr5 -> "baz" ptr4 -> "full" ptr2 -> "bar"
183    if (!cache.contains("baz") || !cache.contains("full") || !cache.contains("bar") || cache.contains("foo")) {
184        errln("Unexpected keys in cache.");
185    }
186    cache.get("new1", ptr5, status);
187    verifySharedPointer(ptr5, "new1", "little");
188    verifyReferences(ptr5, 2, 1, 5);
189
190    // Since bar was evicted, clearing its pointer should delete its data.
191    // Notice that the reference count to 'little' dropped from 5 to 4.
192    ptr2->removeRef();
193    ptr2 = NULL;
194    verifyReferences(ptr5, 2, 1, 4);
195    if (cache.contains("bar") || !cache.contains("full")) {
196        errln("Unexpected 'bar' in cache.");
197    }
198
199    // (Most Recent) "new1", "baz", "full" (Least Recent)
200    // ptr3 -> "baz" ptr4 -> "full" ptr5 -> "new1"
201    cache.get("new2", ptr5, status);
202    verifySharedPointer(ptr5, "new2", "little");
203    verifyReferences(ptr5, 2, 1, 5);
204
205    // since "full" was evicted, clearing its pointer should delete its data.
206    ptr4->removeRef();
207    ptr4 = NULL;
208    verifyReferences(ptr5, 2, 1, 4);
209    if (cache.contains("full") || !cache.contains("baz")) {
210        errln("Unexpected 'full' in cache.");
211    }
212
213    // (Most Recent) "new2", "new1", "baz" (Least Recent)
214    // ptr3 -> "baz" ptr5 -> "new2"
215    cache.get("new3", ptr5, status);
216    verifySharedPointer(ptr5, "new3", "little");
217    verifyReferences(ptr5, 2, 1, 5);
218
219    // since "baz" was evicted, clearing its pointer should delete its data.
220    ptr3->removeRef();
221    ptr3 = NULL;
222    verifyReferences(ptr5, 2, 1, 4);
223    if (cache.contains("baz") || !cache.contains("new3")) {
224        errln("Unexpected 'baz' in cache.");
225    }
226    SharedObject::clearPtr(ptr1);
227    SharedObject::clearPtr(ptr2);
228    SharedObject::clearPtr(ptr3);
229    SharedObject::clearPtr(ptr4);
230    SharedObject::clearPtr(ptr5);
231}
232
233void LRUCacheTest::TestLRUCacheError() {
234    UErrorCode status = U_ZERO_ERROR;
235    LRUCacheForTesting cache(3, "little", status);
236    const CopyOnWriteForTesting *ptr1;
237    cache.get("error", ptr1, status);
238    if (status != U_ILLEGAL_ARGUMENT_ERROR) {
239        errln("Expected an error.");
240    }
241}
242
243void LRUCacheTest::verifySharedPointer(
244        const CopyOnWriteForTesting* ptr,
245        const UnicodeString& name,
246        const UnicodeString& format) {
247    const UnicodeString *strPtr = ptr->localeNamePtr.readOnly();
248    verifyString(name, *strPtr);
249    strPtr = ptr->formatStrPtr.readOnly();
250    verifyString(format, *strPtr);
251}
252
253void LRUCacheTest::verifyString(const UnicodeString &expected, const UnicodeString &actual) {
254    if (expected != actual) {
255        errln(UnicodeString("Expected '") + expected + "', got '"+ actual+"'");
256    }
257}
258
259void LRUCacheTest::verifyReferences(const CopyOnWriteForTesting* ptr, int32_t count, int32_t nameCount, int32_t formatCount) {
260    int32_t actual = ptr->getRefCount();
261    if (count != actual) {
262        errln("Main reference count wrong: Expected %d, got %d", count, actual);
263    }
264    actual = ptr->localeNamePtr.count();
265    if (nameCount != actual) {
266        errln("name reference count wrong: Expected %d, got %d", nameCount, actual);
267    }
268    actual = ptr->formatStrPtr.count();
269    if (formatCount != actual) {
270        errln("format reference count wrong: Expected %d, got %d", formatCount, actual);
271    }
272}
273
274extern IntlTest *createLRUCacheTest() {
275    return new LRUCacheTest();
276}
277