]>
git.saurik.com Git - apple/icu.git/blob - icuSources/test/intltest/unifiedcachetest.cpp
2 *******************************************************************************
3 * Copyright (C) 2015, International Business Machines Corporation and *
4 * others. All Rights Reserved. *
5 *******************************************************************************
7 * File UNIFIEDCACHETEST.CPP
9 ********************************************************************************
13 #include "unifiedcache.h"
14 #include "unicode/datefmt.h"
16 class UCTItem
: public SharedObject
{
19 UCTItem(const char *x
) : value(NULL
) {
20 value
= uprv_strdup(x
);
27 class UCTItem2
: public SharedObject
{
33 const UCTItem
*LocaleCacheKey
<UCTItem
>::createObject(
34 const void *context
, UErrorCode
&status
) const {
35 const UnifiedCache
*cacheContext
= (const UnifiedCache
*) context
;
36 if (uprv_strcmp(fLoc
.getName(), "zh") == 0) {
37 status
= U_MISSING_RESOURCE_ERROR
;
40 if (uprv_strcmp(fLoc
.getLanguage(), fLoc
.getName()) != 0) {
41 const UCTItem
*item
= NULL
;
42 if (cacheContext
== NULL
) {
43 UnifiedCache::getByLocale(fLoc
.getLanguage(), item
, status
);
45 cacheContext
->get(LocaleCacheKey
<UCTItem
>(fLoc
.getLanguage()), item
, status
);
47 if (U_FAILURE(status
)) {
52 UCTItem
*result
= new UCTItem(fLoc
.getName());
58 const UCTItem2
*LocaleCacheKey
<UCTItem2
>::createObject(
59 const void * /*unused*/, UErrorCode
& /*status*/) const {
66 class UnifiedCacheTest
: public IntlTest
{
70 void runIndexedTest(int32_t index
, UBool exec
, const char *&name
, char *par
=0);
72 void TestEvictionPolicy();
76 void TestHashEquals();
77 void TestEvictionUnderStress();
80 void UnifiedCacheTest::runIndexedTest(int32_t index
, UBool exec
, const char* &name
, char* /*par*/) {
82 TESTCASE_AUTO(TestEvictionPolicy
);
83 TESTCASE_AUTO(TestBounded
);
84 TESTCASE_AUTO(TestBasic
);
85 TESTCASE_AUTO(TestError
);
86 TESTCASE_AUTO(TestHashEquals
);
87 TESTCASE_AUTO(TestEvictionUnderStress
);
91 void UnifiedCacheTest::TestEvictionUnderStress() {
92 #if !UCONFIG_NO_FORMATTING
94 const Locale
*locales
= DateFormat::getAvailableLocales(localeCount
);
95 UErrorCode status
= U_ZERO_ERROR
;
96 const UnifiedCache
*cache
= UnifiedCache::getInstance(status
);
97 int64_t evictedCountBefore
= cache
->autoEvictedCount();
98 for (int32_t i
= 0; i
< localeCount
; ++i
) {
99 LocalPointer
<DateFormat
> ptr(DateFormat::createInstanceForSkeleton("yMd", locales
[i
], status
));
101 int64_t evictedCountAfter
= cache
->autoEvictedCount();
102 if (evictedCountBefore
== evictedCountAfter
) {
103 dataerrln("%s:%d Items should have been evicted from cache",
106 #endif /* #if !UCONFIG_NO_FORMATTING */
109 void UnifiedCacheTest::TestEvictionPolicy() {
110 UErrorCode status
= U_ZERO_ERROR
;
112 // We have to call this first or else calling the UnifiedCache
113 // ctor will fail. This is by design to deter clients from using the
114 // cache API incorrectly by creating their own cache instances.
115 UnifiedCache::getInstance(status
);
117 // We create our own local UnifiedCache instance to ensure we have
118 // complete control over it. Real clients should never ever create
120 UnifiedCache
cache(status
);
121 assertSuccess("", status
);
123 // Don't allow unused entries to exeed more than 100% of in use entries.
124 cache
.setEvictionPolicy(0, 100, status
);
126 static const char *locales
[] = {
127 "1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
128 "11", "12", "13", "14", "15", "16", "17", "18", "19", "20"};
130 const UCTItem
*usedReferences
[] = {NULL
, NULL
, NULL
, NULL
, NULL
};
131 const UCTItem
*unusedReference
= NULL
;
133 // Add 5 in-use entries
134 for (int32_t i
= 0; i
< UPRV_LENGTHOF(usedReferences
); i
++) {
136 LocaleCacheKey
<UCTItem
>(locales
[i
]),
142 // Add 10 not in use entries.
143 for (int32_t i
= 0; i
< 10; ++i
) {
145 LocaleCacheKey
<UCTItem
>(
146 locales
[i
+ UPRV_LENGTHOF(usedReferences
)]),
151 unusedReference
->removeRef();
153 // unused count not to exeed in use count
154 assertEquals("", UPRV_LENGTHOF(usedReferences
), cache
.unusedCount());
155 assertEquals("", 2*UPRV_LENGTHOF(usedReferences
), cache
.keyCount());
157 // Free up those used entries.
158 for (int32_t i
= 0; i
< UPRV_LENGTHOF(usedReferences
); i
++) {
159 usedReferences
[i
]->removeRef();
162 // This should free up all cache items
163 assertEquals("", 0, cache
.keyCount());
165 assertSuccess("", status
);
170 void UnifiedCacheTest::TestBounded() {
171 UErrorCode status
= U_ZERO_ERROR
;
173 // We have to call this first or else calling the UnifiedCache
174 // ctor will fail. This is by design to deter clients from using the
175 // cache API incorrectly by creating their own cache instances.
176 UnifiedCache::getInstance(status
);
178 // We create our own local UnifiedCache instance to ensure we have
179 // complete control over it. Real clients should never ever create
181 UnifiedCache
cache(status
);
182 assertSuccess("", status
);
184 // Maximum unused count is 3.
185 cache
.setEvictionPolicy(3, 0, status
);
187 // Our cache will hold up to 3 unused key-value pairs
188 // We test the following invariants:
189 // 1. unusedCount <= 3
190 // 2. cache->get(X) always returns the same reference as long as caller
191 // already holds references to that same object.
193 // We first add 5 key-value pairs with two distinct values, "en" and "fr"
194 // keeping all those references.
196 const UCTItem
*en
= NULL
;
197 const UCTItem
*enGb
= NULL
;
198 const UCTItem
*enUs
= NULL
;
199 const UCTItem
*fr
= NULL
;
200 const UCTItem
*frFr
= NULL
;
201 cache
.get(LocaleCacheKey
<UCTItem
>("en_US"), &cache
, enUs
, status
);
202 cache
.get(LocaleCacheKey
<UCTItem
>("en"), &cache
, en
, status
);
203 assertEquals("", 1, cache
.unusedCount());
204 cache
.get(LocaleCacheKey
<UCTItem
>("en_GB"), &cache
, enGb
, status
);
205 cache
.get(LocaleCacheKey
<UCTItem
>("fr_FR"), &cache
, frFr
, status
);
206 cache
.get(LocaleCacheKey
<UCTItem
>("fr"), &cache
, fr
, status
);
208 // Client holds two unique references, "en" and "fr" the other three
209 // entries are eligible for eviction.
210 assertEquals("", 3, cache
.unusedCount());
211 assertEquals("", 5, cache
.keyCount());
213 // Exercise cache more but don't hold the references except for
214 // the last one. At the end of this, we will hold references to one
215 // additional distinct value, so we will have references to 3 distinct
217 const UCTItem
*throwAway
= NULL
;
218 cache
.get(LocaleCacheKey
<UCTItem
>("zn_AA"), &cache
, throwAway
, status
);
219 cache
.get(LocaleCacheKey
<UCTItem
>("sr_AA"), &cache
, throwAway
, status
);
220 cache
.get(LocaleCacheKey
<UCTItem
>("de_AU"), &cache
, throwAway
, status
);
222 const UCTItem
*deAu(throwAway
);
225 // Client holds three unique references, "en", "fr", "de" although we
226 // could have a total of 8 entries in the cache maxUnusedCount == 3
227 // so we have only 6 entries.
228 assertEquals("", 3, cache
.unusedCount());
229 assertEquals("", 6, cache
.keyCount());
231 // For all the references we have, cache must continue to return
232 // those same references (#2)
234 cache
.get(LocaleCacheKey
<UCTItem
>("en"), &cache
, throwAway
, status
);
235 if (throwAway
!= en
) {
236 errln("Expected en to resolve to the same object.");
238 cache
.get(LocaleCacheKey
<UCTItem
>("en_US"), &cache
, throwAway
, status
);
239 if (throwAway
!= enUs
) {
240 errln("Expected enUs to resolve to the same object.");
242 cache
.get(LocaleCacheKey
<UCTItem
>("en_GB"), &cache
, throwAway
, status
);
243 if (throwAway
!= enGb
) {
244 errln("Expected enGb to resolve to the same object.");
246 cache
.get(LocaleCacheKey
<UCTItem
>("fr_FR"), &cache
, throwAway
, status
);
247 if (throwAway
!= frFr
) {
248 errln("Expected frFr to resolve to the same object.");
250 cache
.get(LocaleCacheKey
<UCTItem
>("fr_FR"), &cache
, throwAway
, status
);
251 cache
.get(LocaleCacheKey
<UCTItem
>("fr"), &cache
, throwAway
, status
);
252 if (throwAway
!= fr
) {
253 errln("Expected fr to resolve to the same object.");
255 cache
.get(LocaleCacheKey
<UCTItem
>("de_AU"), &cache
, throwAway
, status
);
256 if (throwAway
!= deAu
) {
257 errln("Expected deAu to resolve to the same object.");
260 assertEquals("", 3, cache
.unusedCount());
261 assertEquals("", 6, cache
.keyCount());
263 // Now we hold a references to two more distinct values. Cache size
265 const UCTItem
*es
= NULL
;
266 const UCTItem
*ru
= NULL
;
267 cache
.get(LocaleCacheKey
<UCTItem
>("es"), &cache
, es
, status
);
268 cache
.get(LocaleCacheKey
<UCTItem
>("ru"), &cache
, ru
, status
);
269 assertEquals("", 3, cache
.unusedCount());
270 assertEquals("", 8, cache
.keyCount());
272 // Now release all the references we hold except for
274 SharedObject::clearPtr(enGb
);
275 SharedObject::clearPtr(enUs
);
276 SharedObject::clearPtr(fr
);
277 SharedObject::clearPtr(frFr
);
278 SharedObject::clearPtr(deAu
);
279 SharedObject::clearPtr(es
);
280 SharedObject::clearPtr(ru
);
281 SharedObject::clearPtr(en
);
282 SharedObject::clearPtr(throwAway
);
284 // Size of cache should magically drop to 3.
285 assertEquals("", 3, cache
.unusedCount());
286 assertEquals("", 3, cache
.keyCount());
288 // Be sure nothing happens setting the eviction policy in the middle of
290 cache
.setEvictionPolicy(3, 0, status
);
291 assertSuccess("", status
);
295 void UnifiedCacheTest::TestBasic() {
296 UErrorCode status
= U_ZERO_ERROR
;
297 const UnifiedCache
*cache
= UnifiedCache::getInstance(status
);
298 assertSuccess("", status
);
300 int32_t baseCount
= cache
->keyCount();
301 const UCTItem
*en
= NULL
;
302 const UCTItem
*enGb
= NULL
;
303 const UCTItem
*enGb2
= NULL
;
304 const UCTItem
*enUs
= NULL
;
305 const UCTItem
*fr
= NULL
;
306 const UCTItem
*frFr
= NULL
;
307 cache
->get(LocaleCacheKey
<UCTItem
>("en"), en
, status
);
308 cache
->get(LocaleCacheKey
<UCTItem
>("en_US"), enUs
, status
);
309 cache
->get(LocaleCacheKey
<UCTItem
>("en_GB"), enGb
, status
);
310 cache
->get(LocaleCacheKey
<UCTItem
>("fr_FR"), frFr
, status
);
311 cache
->get(LocaleCacheKey
<UCTItem
>("fr"), fr
, status
);
312 cache
->get(LocaleCacheKey
<UCTItem
>("en_GB"), enGb2
, status
);
313 SharedObject::clearPtr(enGb2
);
315 errln("Expected en_GB and en_US to resolve to same object.");
318 errln("Expected fr and fr_FR to resolve to same object.");
321 errln("Expected en_GB and fr to return different objects.");
323 assertSuccess("", status
);
324 // en_US, en_GB, en share one object; fr_FR and fr don't share.
326 assertEquals("", baseCount
+ 5, cache
->keyCount());
327 SharedObject::clearPtr(enGb
);
330 // Only 2 unique values in the cache. flushing trims cache down
331 // to this minimum size.
332 assertEquals("", baseCount
+ 2, cache
->keyCount());
333 SharedObject::clearPtr(enUs
);
334 SharedObject::clearPtr(en
);
336 // With en_GB and en_US and en cleared there are no more hard references to
337 // the "en" object, so it gets flushed and the keys that refer to it
338 // get removed from the cache. Now we have just one unique value, fr, in
340 assertEquals("", baseCount
+ 1, cache
->keyCount());
341 SharedObject::clearPtr(fr
);
343 assertEquals("", baseCount
+ 1, cache
->keyCount());
344 SharedObject::clearPtr(frFr
);
346 assertEquals("", baseCount
+ 0, cache
->keyCount());
347 assertSuccess("", status
);
350 void UnifiedCacheTest::TestError() {
351 UErrorCode status
= U_ZERO_ERROR
;
352 const UnifiedCache
*cache
= UnifiedCache::getInstance(status
);
353 assertSuccess("", status
);
355 int32_t baseCount
= cache
->keyCount();
356 const UCTItem
*zh
= NULL
;
357 const UCTItem
*zhTw
= NULL
;
358 const UCTItem
*zhHk
= NULL
;
360 status
= U_ZERO_ERROR
;
361 cache
->get(LocaleCacheKey
<UCTItem
>("zh"), zh
, status
);
362 if (status
!= U_MISSING_RESOURCE_ERROR
) {
363 errln("Expected U_MISSING_RESOURCE_ERROR");
365 status
= U_ZERO_ERROR
;
366 cache
->get(LocaleCacheKey
<UCTItem
>("zh_TW"), zhTw
, status
);
367 if (status
!= U_MISSING_RESOURCE_ERROR
) {
368 errln("Expected U_MISSING_RESOURCE_ERROR");
370 status
= U_ZERO_ERROR
;
371 cache
->get(LocaleCacheKey
<UCTItem
>("zh_HK"), zhHk
, status
);
372 if (status
!= U_MISSING_RESOURCE_ERROR
) {
373 errln("Expected U_MISSING_RESOURCE_ERROR");
375 // 3 keys in cache zh, zhTW, zhHk all pointing to error placeholders
376 assertEquals("", baseCount
+ 3, cache
->keyCount());
378 // error placeholders have no hard references so they always get flushed.
379 assertEquals("", baseCount
+ 0, cache
->keyCount());
382 void UnifiedCacheTest::TestHashEquals() {
383 LocaleCacheKey
<UCTItem
> key1("en_US");
384 LocaleCacheKey
<UCTItem
> key2("en_US");
385 LocaleCacheKey
<UCTItem
> diffKey1("en_UT");
386 LocaleCacheKey
<UCTItem2
> diffKey2("en_US");
387 assertTrue("", key1
.hashCode() == key2
.hashCode());
388 assertTrue("", key1
.hashCode() != diffKey1
.hashCode());
389 assertTrue("", key1
.hashCode() != diffKey2
.hashCode());
390 assertTrue("", diffKey1
.hashCode() != diffKey2
.hashCode());
391 assertTrue("", key1
== key2
);
392 assertTrue("", key1
!= diffKey1
);
393 assertTrue("", key1
!= diffKey2
);
394 assertTrue("", diffKey1
!= diffKey2
);
397 extern IntlTest
*createUnifiedCacheTest() {
398 return new UnifiedCacheTest();