]>
git.saurik.com Git - apple/icu.git/blob - icuSources/test/intltest/unifiedcachetest.cpp
1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 *******************************************************************************
5 * Copyright (C) 2015, International Business Machines Corporation and *
6 * others. All Rights Reserved. *
7 *******************************************************************************
9 * File UNIFIEDCACHETEST.CPP
11 ********************************************************************************
15 #include "unifiedcache.h"
16 #include "unicode/datefmt.h"
18 class UCTItem
: public SharedObject
{
21 UCTItem(const char *x
) : value(NULL
) {
22 value
= uprv_strdup(x
);
29 class UCTItem2
: public SharedObject
{
35 const UCTItem
*LocaleCacheKey
<UCTItem
>::createObject(
36 const void *context
, UErrorCode
&status
) const {
37 const UnifiedCache
*cacheContext
= (const UnifiedCache
*) context
;
38 if (uprv_strcmp(fLoc
.getName(), "zh") == 0) {
39 status
= U_MISSING_RESOURCE_ERROR
;
42 if (uprv_strcmp(fLoc
.getLanguage(), fLoc
.getName()) != 0) {
43 const UCTItem
*item
= NULL
;
44 if (cacheContext
== NULL
) {
45 UnifiedCache::getByLocale(fLoc
.getLanguage(), item
, status
);
47 cacheContext
->get(LocaleCacheKey
<UCTItem
>(fLoc
.getLanguage()), item
, status
);
49 if (U_FAILURE(status
)) {
54 UCTItem
*result
= new UCTItem(fLoc
.getName());
60 const UCTItem2
*LocaleCacheKey
<UCTItem2
>::createObject(
61 const void * /*unused*/, UErrorCode
& /*status*/) const {
68 class UnifiedCacheTest
: public IntlTest
{
72 void runIndexedTest(int32_t index
, UBool exec
, const char *&name
, char *par
=0);
74 void TestEvictionPolicy();
78 void TestHashEquals();
79 void TestEvictionUnderStress();
82 void UnifiedCacheTest::runIndexedTest(int32_t index
, UBool exec
, const char* &name
, char* /*par*/) {
84 TESTCASE_AUTO(TestEvictionPolicy
);
85 TESTCASE_AUTO(TestBounded
);
86 TESTCASE_AUTO(TestBasic
);
87 TESTCASE_AUTO(TestError
);
88 TESTCASE_AUTO(TestHashEquals
);
89 TESTCASE_AUTO(TestEvictionUnderStress
);
93 void UnifiedCacheTest::TestEvictionUnderStress() {
94 #if !UCONFIG_NO_FORMATTING
96 const Locale
*locales
= DateFormat::getAvailableLocales(localeCount
);
97 UErrorCode status
= U_ZERO_ERROR
;
98 const UnifiedCache
*cache
= UnifiedCache::getInstance(status
);
99 int64_t evictedCountBefore
= cache
->autoEvictedCount();
100 for (int32_t i
= 0; i
< localeCount
; ++i
) {
101 LocalPointer
<DateFormat
> ptr(DateFormat::createInstanceForSkeleton("yMd", locales
[i
], status
));
103 int64_t evictedCountAfter
= cache
->autoEvictedCount();
104 if (evictedCountBefore
== evictedCountAfter
) {
105 dataerrln("%s:%d Items should have been evicted from cache",
108 #endif /* #if !UCONFIG_NO_FORMATTING */
111 void UnifiedCacheTest::TestEvictionPolicy() {
112 UErrorCode status
= U_ZERO_ERROR
;
114 // We have to call this first or else calling the UnifiedCache
115 // ctor will fail. This is by design to deter clients from using the
116 // cache API incorrectly by creating their own cache instances.
117 UnifiedCache::getInstance(status
);
119 // We create our own local UnifiedCache instance to ensure we have
120 // complete control over it. Real clients should never ever create
122 UnifiedCache
cache(status
);
123 assertSuccess("", status
);
125 // Don't allow unused entries to exeed more than 100% of in use entries.
126 cache
.setEvictionPolicy(0, 100, status
);
128 static const char *locales
[] = {
129 "1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
130 "11", "12", "13", "14", "15", "16", "17", "18", "19", "20"};
132 const UCTItem
*usedReferences
[] = {NULL
, NULL
, NULL
, NULL
, NULL
};
133 const UCTItem
*unusedReference
= NULL
;
135 // Add 5 in-use entries
136 for (int32_t i
= 0; i
< UPRV_LENGTHOF(usedReferences
); i
++) {
138 LocaleCacheKey
<UCTItem
>(locales
[i
]),
144 // Add 10 not in use entries.
145 for (int32_t i
= 0; i
< 10; ++i
) {
147 LocaleCacheKey
<UCTItem
>(
148 locales
[i
+ UPRV_LENGTHOF(usedReferences
)]),
153 unusedReference
->removeRef();
155 // unused count not to exeed in use count
156 assertEquals("", UPRV_LENGTHOF(usedReferences
), cache
.unusedCount());
157 assertEquals("", 2*UPRV_LENGTHOF(usedReferences
), cache
.keyCount());
159 // Free up those used entries.
160 for (int32_t i
= 0; i
< UPRV_LENGTHOF(usedReferences
); i
++) {
161 usedReferences
[i
]->removeRef();
164 // This should free up all cache items
165 assertEquals("", 0, cache
.keyCount());
167 assertSuccess("", status
);
172 void UnifiedCacheTest::TestBounded() {
173 UErrorCode status
= U_ZERO_ERROR
;
175 // We have to call this first or else calling the UnifiedCache
176 // ctor will fail. This is by design to deter clients from using the
177 // cache API incorrectly by creating their own cache instances.
178 UnifiedCache::getInstance(status
);
180 // We create our own local UnifiedCache instance to ensure we have
181 // complete control over it. Real clients should never ever create
183 UnifiedCache
cache(status
);
184 assertSuccess("", status
);
186 // Maximum unused count is 3.
187 cache
.setEvictionPolicy(3, 0, status
);
189 // Our cache will hold up to 3 unused key-value pairs
190 // We test the following invariants:
191 // 1. unusedCount <= 3
192 // 2. cache->get(X) always returns the same reference as long as caller
193 // already holds references to that same object.
195 // We first add 5 key-value pairs with two distinct values, "en" and "fr"
196 // keeping all those references.
198 const UCTItem
*en
= NULL
;
199 const UCTItem
*enGb
= NULL
;
200 const UCTItem
*enUs
= NULL
;
201 const UCTItem
*fr
= NULL
;
202 const UCTItem
*frFr
= NULL
;
203 cache
.get(LocaleCacheKey
<UCTItem
>("en_US"), &cache
, enUs
, status
);
204 cache
.get(LocaleCacheKey
<UCTItem
>("en"), &cache
, en
, status
);
205 assertEquals("", 1, cache
.unusedCount());
206 cache
.get(LocaleCacheKey
<UCTItem
>("en_GB"), &cache
, enGb
, status
);
207 cache
.get(LocaleCacheKey
<UCTItem
>("fr_FR"), &cache
, frFr
, status
);
208 cache
.get(LocaleCacheKey
<UCTItem
>("fr"), &cache
, fr
, status
);
210 // Client holds two unique references, "en" and "fr" the other three
211 // entries are eligible for eviction.
212 assertEquals("", 3, cache
.unusedCount());
213 assertEquals("", 5, cache
.keyCount());
215 // Exercise cache more but don't hold the references except for
216 // the last one. At the end of this, we will hold references to one
217 // additional distinct value, so we will have references to 3 distinct
219 const UCTItem
*throwAway
= NULL
;
220 cache
.get(LocaleCacheKey
<UCTItem
>("zn_AA"), &cache
, throwAway
, status
);
221 cache
.get(LocaleCacheKey
<UCTItem
>("sr_AA"), &cache
, throwAway
, status
);
222 cache
.get(LocaleCacheKey
<UCTItem
>("de_AU"), &cache
, throwAway
, status
);
224 const UCTItem
*deAu(throwAway
);
227 // Client holds three unique references, "en", "fr", "de" although we
228 // could have a total of 8 entries in the cache maxUnusedCount == 3
229 // so we have only 6 entries.
230 assertEquals("", 3, cache
.unusedCount());
231 assertEquals("", 6, cache
.keyCount());
233 // For all the references we have, cache must continue to return
234 // those same references (#2)
236 cache
.get(LocaleCacheKey
<UCTItem
>("en"), &cache
, throwAway
, status
);
237 if (throwAway
!= en
) {
238 errln("Expected en to resolve to the same object.");
240 cache
.get(LocaleCacheKey
<UCTItem
>("en_US"), &cache
, throwAway
, status
);
241 if (throwAway
!= enUs
) {
242 errln("Expected enUs to resolve to the same object.");
244 cache
.get(LocaleCacheKey
<UCTItem
>("en_GB"), &cache
, throwAway
, status
);
245 if (throwAway
!= enGb
) {
246 errln("Expected enGb to resolve to the same object.");
248 cache
.get(LocaleCacheKey
<UCTItem
>("fr_FR"), &cache
, throwAway
, status
);
249 if (throwAway
!= frFr
) {
250 errln("Expected frFr to resolve to the same object.");
252 cache
.get(LocaleCacheKey
<UCTItem
>("fr_FR"), &cache
, throwAway
, status
);
253 cache
.get(LocaleCacheKey
<UCTItem
>("fr"), &cache
, throwAway
, status
);
254 if (throwAway
!= fr
) {
255 errln("Expected fr to resolve to the same object.");
257 cache
.get(LocaleCacheKey
<UCTItem
>("de_AU"), &cache
, throwAway
, status
);
258 if (throwAway
!= deAu
) {
259 errln("Expected deAu to resolve to the same object.");
262 assertEquals("", 3, cache
.unusedCount());
263 assertEquals("", 6, cache
.keyCount());
265 // Now we hold a references to two more distinct values. Cache size
267 const UCTItem
*es
= NULL
;
268 const UCTItem
*ru
= NULL
;
269 cache
.get(LocaleCacheKey
<UCTItem
>("es"), &cache
, es
, status
);
270 cache
.get(LocaleCacheKey
<UCTItem
>("ru"), &cache
, ru
, status
);
271 assertEquals("", 3, cache
.unusedCount());
272 assertEquals("", 8, cache
.keyCount());
274 // Now release all the references we hold except for
276 SharedObject::clearPtr(enGb
);
277 SharedObject::clearPtr(enUs
);
278 SharedObject::clearPtr(fr
);
279 SharedObject::clearPtr(frFr
);
280 SharedObject::clearPtr(deAu
);
281 SharedObject::clearPtr(es
);
282 SharedObject::clearPtr(ru
);
283 SharedObject::clearPtr(en
);
284 SharedObject::clearPtr(throwAway
);
286 // Size of cache should magically drop to 3.
287 assertEquals("", 3, cache
.unusedCount());
288 assertEquals("", 3, cache
.keyCount());
290 // Be sure nothing happens setting the eviction policy in the middle of
292 cache
.setEvictionPolicy(3, 0, status
);
293 assertSuccess("", status
);
297 void UnifiedCacheTest::TestBasic() {
298 UErrorCode status
= U_ZERO_ERROR
;
299 const UnifiedCache
*cache
= UnifiedCache::getInstance(status
);
300 assertSuccess("", status
);
302 int32_t baseCount
= cache
->keyCount();
303 const UCTItem
*en
= NULL
;
304 const UCTItem
*enGb
= NULL
;
305 const UCTItem
*enGb2
= NULL
;
306 const UCTItem
*enUs
= NULL
;
307 const UCTItem
*fr
= NULL
;
308 const UCTItem
*frFr
= NULL
;
309 cache
->get(LocaleCacheKey
<UCTItem
>("en"), en
, status
);
310 cache
->get(LocaleCacheKey
<UCTItem
>("en_US"), enUs
, status
);
311 cache
->get(LocaleCacheKey
<UCTItem
>("en_GB"), enGb
, status
);
312 cache
->get(LocaleCacheKey
<UCTItem
>("fr_FR"), frFr
, status
);
313 cache
->get(LocaleCacheKey
<UCTItem
>("fr"), fr
, status
);
314 cache
->get(LocaleCacheKey
<UCTItem
>("en_GB"), enGb2
, status
);
315 SharedObject::clearPtr(enGb2
);
317 errln("Expected en_GB and en_US to resolve to same object.");
320 errln("Expected fr and fr_FR to resolve to same object.");
323 errln("Expected en_GB and fr to return different objects.");
325 assertSuccess("", status
);
326 // en_US, en_GB, en share one object; fr_FR and fr don't share.
328 assertEquals("", baseCount
+ 5, cache
->keyCount());
329 SharedObject::clearPtr(enGb
);
332 // Only 2 unique values in the cache. flushing trims cache down
333 // to this minimum size.
334 assertEquals("", baseCount
+ 2, cache
->keyCount());
335 SharedObject::clearPtr(enUs
);
336 SharedObject::clearPtr(en
);
338 // With en_GB and en_US and en cleared there are no more hard references to
339 // the "en" object, so it gets flushed and the keys that refer to it
340 // get removed from the cache. Now we have just one unique value, fr, in
342 assertEquals("", baseCount
+ 1, cache
->keyCount());
343 SharedObject::clearPtr(fr
);
345 assertEquals("", baseCount
+ 1, cache
->keyCount());
346 SharedObject::clearPtr(frFr
);
348 assertEquals("", baseCount
+ 0, cache
->keyCount());
349 assertSuccess("", status
);
352 void UnifiedCacheTest::TestError() {
353 UErrorCode status
= U_ZERO_ERROR
;
354 const UnifiedCache
*cache
= UnifiedCache::getInstance(status
);
355 assertSuccess("", status
);
357 int32_t baseCount
= cache
->keyCount();
358 const UCTItem
*zh
= NULL
;
359 const UCTItem
*zhTw
= NULL
;
360 const UCTItem
*zhHk
= NULL
;
362 status
= U_ZERO_ERROR
;
363 cache
->get(LocaleCacheKey
<UCTItem
>("zh"), zh
, status
);
364 if (status
!= U_MISSING_RESOURCE_ERROR
) {
365 errln("Expected U_MISSING_RESOURCE_ERROR");
367 status
= U_ZERO_ERROR
;
368 cache
->get(LocaleCacheKey
<UCTItem
>("zh_TW"), zhTw
, status
);
369 if (status
!= U_MISSING_RESOURCE_ERROR
) {
370 errln("Expected U_MISSING_RESOURCE_ERROR");
372 status
= U_ZERO_ERROR
;
373 cache
->get(LocaleCacheKey
<UCTItem
>("zh_HK"), zhHk
, status
);
374 if (status
!= U_MISSING_RESOURCE_ERROR
) {
375 errln("Expected U_MISSING_RESOURCE_ERROR");
377 // 3 keys in cache zh, zhTW, zhHk all pointing to error placeholders
378 assertEquals("", baseCount
+ 3, cache
->keyCount());
380 // error placeholders have no hard references so they always get flushed.
381 assertEquals("", baseCount
+ 0, cache
->keyCount());
384 void UnifiedCacheTest::TestHashEquals() {
385 LocaleCacheKey
<UCTItem
> key1("en_US");
386 LocaleCacheKey
<UCTItem
> key2("en_US");
387 LocaleCacheKey
<UCTItem
> diffKey1("en_UT");
388 LocaleCacheKey
<UCTItem2
> diffKey2("en_US");
389 assertTrue("", key1
.hashCode() == key2
.hashCode());
390 assertTrue("", key1
.hashCode() != diffKey1
.hashCode());
391 assertTrue("", key1
.hashCode() != diffKey2
.hashCode());
392 assertTrue("", diffKey1
.hashCode() != diffKey2
.hashCode());
393 assertTrue("", key1
== key2
);
394 assertTrue("", key1
!= diffKey1
);
395 assertTrue("", key1
!= diffKey2
);
396 assertTrue("", diffKey1
!= diffKey2
);
399 extern IntlTest
*createUnifiedCacheTest() {
400 return new UnifiedCacheTest();