diff --git a/Foundation/CPCache.j b/Foundation/CPCache.j index f24c56f3a..c8efe2e2d 100644 --- a/Foundation/CPCache.j +++ b/Foundation/CPCache.j @@ -272,6 +272,22 @@ var CPCacheDelegate_cache_willEvictObject_ = 1 << 1; [[_items objectForKey:sortedKeys[i]] setPosition:_currentPosition++]; } +/* + * Check if the totalCostLimit is exceeded + */ +- (BOOL)_isTotalCostLimitExceeded +{ + return ([self _totalCost] > _totalCostLimit && _totalCostLimit > 0); +} + +/* + * Check if the countLimit is exceeded + */ +- (BOOL)_isCountLimitExceeded +{ + return ([self _count] > _countLimit && _countLimit > 0); +} + /* * This method clean the cache if the totalCost or the count exceeds the totalCostLimit or the countLimit * until to satisfy condition (totalCost < totalCostLimit and count < countLimit) @@ -279,7 +295,7 @@ var CPCacheDelegate_cache_willEvictObject_ = 1 << 1; - (void)_cleanCache { // Check if the condition is satisfied - if (!(([self _totalCost] > _totalCostLimit && _totalCostLimit > 0) || ([self _count] > _countLimit && _countLimit > 0))) + if (![self _isTotalCostLimitExceeded] && ![self _isCountLimitExceeded]) return; // Sort keys by position @@ -290,7 +306,7 @@ var CPCacheDelegate_cache_willEvictObject_ = 1 << 1; // Remove oldest objects until to satisfy the break condition for (var i = 0; i < sortedKeys.length; ++i) { - if (!(([self _totalCost] > _totalCostLimit && _totalCostLimit > 0) || ([self _count] > _countLimit && _countLimit > 0))) + if (![self _isTotalCostLimitExceeded] && ![self _isCountLimitExceeded]) break; // Call delegate method to warn that the object is going to be removed diff --git a/Tests/Foundation/CPCacheTest.j b/Tests/Foundation/CPCacheTest.j index c16af881f..ac50a0fc6 100644 --- a/Tests/Foundation/CPCacheTest.j +++ b/Tests/Foundation/CPCacheTest.j @@ -303,11 +303,49 @@ [self assert:3 equals:[[cache._items objectForKey:@"key4"] position]]; } +- (void)testIsTotalCostLimitExceeded +{ + // Setup cache + var cache = [[CPCache alloc] init]; + [cache setObject:@"Object1" forKey:@"key1" cost:50]; + + // Not exceed (because of not limit) + [self assert:NO equals:[cache _isTotalCostLimitExceeded]]; + + // Not exceed (with limit) + [cache setTotalCostLimit:100]; + [self assert:NO equals:[cache _isTotalCostLimitExceeded]]; + + // Exceed (by hacking the cost of object) + [[cache._items objectForKey:@"key1"] setCost: 150]; + cache._totalCostCache = -1; + [self assert:YES equals:[cache _isTotalCostLimitExceeded]]; +} + +- (void)testIsCountLimitExceeded +{ + // Setup cache + var cache = [[CPCache alloc] init]; + [cache setObject:@"Object1" forKey:@"key1"]; + + // Not exceed (because of not limit) + [self assert:NO equals:[cache _isCountLimitExceeded]]; + + // Not exceed (with limit) + [cache setCountLimit:1]; + [self assert:NO equals:[cache _isCountLimitExceeded]]; + + // Exceed (by hacking the items) + [cache._items setObject:[_CPCacheItem cacheItemWithObject:@"Object2" cost:0 position:3] forKey:@"key2"]; + [self assert:YES equals:[cache _isCountLimitExceeded]]; +} + - (void)testCleanCacheWithoutDiscarding { // Setup cache var cache = [[CPCache alloc] init]; - [cache setTotalCostLimit: 100] + [cache setTotalCostLimit: 100]; + [cache setCountLimit: 2]; [cache setDelegate:self]; [cache setObject:@"Object1" forKey:@"key1" cost:50]; [cache setObject:@"Object2" forKey:@"key2" cost:40]; @@ -323,12 +361,50 @@ - (void)testCleanCacheWithCostDiscarding { - // Not testable, but tested in testSetObjectForKeyDiscardByCost + // Setup cache + var cache = [[CPCache alloc] init]; + [cache setTotalCostLimit: 100]; + [cache setDelegate:self]; + [cache setObject:@"Object1" forKey:@"key1" cost:50]; + [cache setObject:@"Object2" forKey:@"key2" cost:40]; + + // Hack cost of object + [[cache._items objectForKey:@"key2"] setCost:80]; + cache._totalCostCache = -1; + + // Set total cost limit + [cache _cleanCache]; + [self assert:nil equals:[cache objectForKey:@"key1"]]; + [self assert:@"Object2" equals:[cache objectForKey:@"key2"]]; + + // Check delegate + [self assert:1 equals:_countDelegateExecuted]; + [self assert:cache equals:[_caches objectAtIndex:0]]; + [self assert:@"Object1" equals:[_objects objectAtIndex:0]]; } - (void)testCleanCacheWithCountDiscarding { - // Not testable, but tested in testSetObjectForKeyDiscardByCount + // Setup cache + var cache = [[CPCache alloc] init]; + [cache setCountLimit:2]; + [cache setDelegate:self]; + [cache setObject:@"Object1" forKey:@"key1"]; + [cache setObject:@"Object2" forKey:@"key2"]; + + // Hack items + [cache._items setObject:[_CPCacheItem cacheItemWithObject:@"Object3" cost:0 position:3] forKey:@"key3"]; + + // Set total cost limit + [cache _cleanCache]; + [self assert:nil equals:[cache objectForKey:@"key1"]]; + [self assert:@"Object2" equals:[cache objectForKey:@"key2"]]; + [self assert:@"Object3" equals:[cache objectForKey:@"key3"]]; + + // Check delegate + [self assert:1 equals:_countDelegateExecuted]; + [self assert:cache equals:[_caches objectAtIndex:0]]; + [self assert:@"Object1" equals:[_objects objectAtIndex:0]]; }