Chromium Code Reviews| Index: ios/chrome/browser/snapshots/snapshot_cache_unittest.mm |
| diff --git a/ios/chrome/browser/snapshots/snapshot_cache_unittest.mm b/ios/chrome/browser/snapshots/snapshot_cache_unittest.mm |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..ac44cd673c02d5eaf59a7e29c99f9810ea6cc6de |
| --- /dev/null |
| +++ b/ios/chrome/browser/snapshots/snapshot_cache_unittest.mm |
| @@ -0,0 +1,629 @@ |
| +// Copyright 2012 The Chromium Authors. All rights reserved. |
|
Paweł Hajdan Jr.
2015/01/30 12:24:29
nit: 2015
|
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#import "ios/chrome/browser/snapshots/snapshot_cache.h" |
| + |
| +#import <Foundation/Foundation.h> |
| + |
| +#include "base/files/file_path.h" |
| +#include "base/files/file_util.h" |
| +#include "base/format_macros.h" |
| +#include "base/location.h" |
| +#include "base/mac/bind_objc_block.h" |
| +#include "base/mac/scoped_nsautorelease_pool.h" |
| +#include "base/strings/sys_string_conversions.h" |
| +#import "base/test/ios/wait_util.h" |
| +#include "base/time/time.h" |
| +#include "ios/chrome/browser/ui/ui_util.h" |
| +#include "ios/web/public/test/test_web_thread.h" |
| +#include "ios/web/public/web_thread.h" |
| +#include "testing/gtest/include/gtest/gtest.h" |
| +#include "testing/gtest_mac.h" |
| +#include "testing/platform_test.h" |
| + |
| +static const NSUInteger kSessionCount = 10; |
| +static const NSUInteger kSnapshotPixelSize = 8; |
| + |
| +// Promote some implementation methods to public. |
| +@interface SnapshotCache (Testing) |
| +- (base::FilePath)imagePathForSessionID:(NSString*)sessionID; |
| +- (base::FilePath)greyImagePathForSessionID:(NSString*)sessionID; |
| +- (void)handleLowMemory; |
| +@end |
| + |
| +@interface SnapshotCache (TestingAdditions) |
| +- (void)flushOperationQueue; |
| +- (BOOL)hasImageInMemory:(NSString*)sessionID; |
| +- (BOOL)hasGreyImageInMemory:(NSString*)sessionID; |
| +@end |
| + |
| +@implementation SnapshotCache (TestingAdditions) |
| +- (void)flushOperationQueue { |
| + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); |
| + web::WebThread::PostBlockingPoolSequencedTask( |
| + "SnapshotCacheSequenceToken", FROM_HERE, |
| + base::BindBlock(^{ |
| + dispatch_semaphore_signal(semaphore); |
| + })); |
| + dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); |
| + dispatch_release(semaphore); |
| +} |
| +- (BOOL)hasImageInMemory:(NSString*)sessionID { |
| + return [imageDictionary_ objectForKey:sessionID] != nil; |
| +} |
| +- (BOOL)hasGreyImageInMemory:(NSString*)sessionID { |
| + return [greyImageDictionary_ objectForKey:sessionID] != nil; |
| +} |
| +@end |
| + |
| +namespace { |
| + |
| +class SnapshotCacheTest : public PlatformTest { |
| + protected: |
| + // Build an array of session names and an array of UIImages filled with |
| + // random colors. |
| + void SetUp() override { |
| + file_thread_.reset(new web::TestWebThread(web::WebThread::FILE)); |
| + file_thread_->Start(); |
| + testImages_.reset([[NSMutableArray alloc] initWithCapacity:kSessionCount]); |
| + testSessions_.reset( |
| + [[NSMutableArray alloc] initWithCapacity:kSessionCount]); |
| + |
| + CGFloat scale = [SnapshotCache snapshotScaleForDevice]; |
| + UIGraphicsBeginImageContextWithOptions( |
| + CGSizeMake(kSnapshotPixelSize, kSnapshotPixelSize), NO, scale); |
| + CGContextRef context = UIGraphicsGetCurrentContext(); |
| + srand(1); |
| + |
| + for (NSUInteger i = 0; i < kSessionCount; ++i) { |
| + UIImage* image = GenerateRandomImage(context); |
| + [testImages_ addObject:image]; |
| + [testSessions_ |
| + addObject:[NSString stringWithFormat:@"SessionId-%" PRIuNS, i]]; |
| + } |
| + |
| + UIGraphicsEndImageContext(); |
| + } |
| + |
| + // Generates an image filled with a random color. |
| + UIImage* GenerateRandomImage(CGContextRef context) { |
| + CGFloat r = rand() / CGFloat(RAND_MAX); |
| + CGFloat g = rand() / CGFloat(RAND_MAX); |
| + CGFloat b = rand() / CGFloat(RAND_MAX); |
| + CGContextSetRGBStrokeColor(context, r, g, b, 1.0); |
| + CGContextSetRGBFillColor(context, r, g, b, 1.0); |
| + CGContextFillRect( |
| + context, CGRectMake(0.0, 0.0, kSnapshotPixelSize, kSnapshotPixelSize)); |
| + return UIGraphicsGetImageFromCurrentImageContext(); |
| + } |
| + |
| + // This function removes the snapshots both from dictionary and from disk. |
| + void ClearDumpedImages() { |
| + SnapshotCache* cache = [SnapshotCache sharedInstance]; |
| + |
| + NSString* sessionID; |
| + for (sessionID in testSessions_.get()) |
| + [cache removeImageWithSessionID:sessionID]; |
| + |
| + [cache flushOperationQueue]; |
| + // The above calls to -removeImageWithSessionID remove both the color |
| + // and grey snapshots for each sessionID, if they are on disk. However, |
| + // ensure we also get rid of the grey snapshots in memory. |
| + [cache removeGreyCache]; |
| + |
| + __block BOOL foundImage = NO; |
| + __block NSUInteger numCallbacks = 0; |
| + for (sessionID in testSessions_.get()) { |
| + base::FilePath path([cache imagePathForSessionID:sessionID]); |
| + |
| + // Checks that the snapshot is not on disk. |
| + EXPECT_FALSE(base::PathExists(path)); |
| + |
| + // Check that the snapshot is not in the dictionary. |
| + [cache retrieveImageForSessionID:sessionID |
| + callback:^(UIImage* image) { |
| + numCallbacks++; |
| + if (image) |
| + foundImage = YES; |
| + }]; |
| + } |
| + |
| + // Spin the main message loop until all of the callbacks run. Time out |
| + // after a while to avoid waiting forever. |
| + NSUInteger expectedNumCallbacks = [testSessions_ count]; |
| + NSTimeInterval kTimeoutSec = 3; |
|
Paweł Hajdan Jr.
2015/01/30 12:24:29
I strongly second Nico's comment about using base/
|
| + base::WaitUntilCondition(^bool() { |
| + return numCallbacks >= expectedNumCallbacks; |
| + }, nullptr, base::TimeDelta::FromSecondsD(kTimeoutSec)); |
| + |
| + // Expect that all the callbacks ran and that none retrieved an image. |
| + EXPECT_EQ(expectedNumCallbacks, numCallbacks); |
| + EXPECT_FALSE(foundImage); |
| + } |
| + |
| + // Loads kSessionCount color images into the cache. If |waitForFilesOnDisk| |
| + // is YES, will not return until the images have been written to disk. |
| + void LoadAllColorImagesIntoCache(bool waitForFilesOnDisk) { |
| + LoadColorImagesIntoCache(kSessionCount, waitForFilesOnDisk); |
| + } |
| + |
| + // Loads |count| color images into the cache. If |waitForFilesOnDisk| |
| + // is YES, will not return until the images have been written to disk. |
| + void LoadColorImagesIntoCache(NSUInteger count, bool waitForFilesOnDisk) { |
| + SnapshotCache* cache = [SnapshotCache sharedInstance]; |
| + // Put color images in the cache. |
| + for (NSUInteger i = 0; i < count; ++i) { |
| + base::mac::ScopedNSAutoreleasePool pool; |
| + UIImage* image = [testImages_ objectAtIndex:i]; |
| + NSString* sessionID = [testSessions_ objectAtIndex:i]; |
| + [cache setImage:image withSessionID:sessionID]; |
| + } |
| + if (waitForFilesOnDisk) { |
| + [cache flushOperationQueue]; |
| + for (NSUInteger i = 0; i < count; ++i) { |
| + // Check that images are on the disk. |
| + NSString* sessionID = [testSessions_ objectAtIndex:i]; |
| + base::FilePath path([cache imagePathForSessionID:sessionID]); |
| + EXPECT_TRUE(base::PathExists(path)); |
| + } |
| + } |
| + } |
| + |
| + // Waits for the grey images for the sessions in |testSessions_| to be written |
| + // to disk, which happens in a background thread. |
| + void WaitForAllGreyImagesOnDisk() { |
| + SnapshotCache* cache = [SnapshotCache sharedInstance]; |
| + NSString* sessionID; |
| + NSTimeInterval kTimeoutSec = 3; |
| + for (sessionID in testSessions_.get()) { |
| + base::FilePath path([cache greyImagePathForSessionID:sessionID]); |
| + base::WaitUntilCondition(^bool() { |
| + return base::PathExists(path); |
| + }, nullptr, base::TimeDelta::FromSecondsD(kTimeoutSec)); |
| + } |
| + } |
| + |
| + // Waits for the first |count| grey images for sessions in |testSessions_| |
| + // to be placed in the cache. |
| + void WaitForGreyImagesInCache(NSUInteger count) { |
| + SnapshotCache* cache = [SnapshotCache sharedInstance]; |
| + NSTimeInterval kTimeoutSec = 3; |
| + for (NSUInteger i = 0; i < count; i++) { |
| + base::WaitUntilCondition(^bool() { |
| + return [cache hasGreyImageInMemory:testSessions_[i]]; |
| + }, nullptr, base::TimeDelta::FromSecondsD(kTimeoutSec)); |
| + } |
| + } |
| + |
| + // Guesses the order of the color channels in the image. |
| + // Supports RGB, BGR, RGBA, BGRA, ARGB, ABGR. |
| + // Returns the position of each channel between 0 and 3. |
| + void ComputeColorComponents(CGImageRef cgImage, |
| + int* red, |
| + int* green, |
| + int* blue) { |
| + CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(cgImage); |
| + CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(cgImage); |
| + int byteOrder = bitmapInfo & kCGBitmapByteOrderMask; |
| + |
| + *red = 0; |
| + *green = 1; |
| + *blue = 2; |
| + |
| + if (alphaInfo == kCGImageAlphaLast || |
| + alphaInfo == kCGImageAlphaPremultipliedLast || |
| + alphaInfo == kCGImageAlphaNoneSkipLast) { |
| + *red = 1; |
| + *green = 2; |
| + *blue = 3; |
| + } |
| + |
| + if (byteOrder != kCGBitmapByteOrder32Host) { |
| + int lastChannel = (CGImageGetBitsPerPixel(cgImage) == 24) ? 2 : 3; |
| + *red = lastChannel - *red; |
| + *green = lastChannel - *green; |
| + *blue = lastChannel - *blue; |
| + } |
| + } |
| + |
| + const char* GetPixelData(CGImageRef cgImage) { |
| + CFDataRef data = CGDataProviderCopyData(CGImageGetDataProvider(cgImage)); |
| + return reinterpret_cast<const char*>(CFDataGetBytePtr(data)); |
| + } |
| + |
| + base::scoped_nsobject<NSMutableArray> testSessions_; |
| + base::scoped_nsobject<NSMutableArray> testImages_; |
| + scoped_ptr<web::TestWebThread> file_thread_; |
| +}; |
| + |
| +// This test simply put all the snapshots in the cache and then gets them back |
| +// As the snapshots are kept in memory, the same pointer can be retrieved. |
| +// This test also checks that images are correctly removed from the disk. |
| +TEST_F(SnapshotCacheTest, Cache) { |
| + // Don't run on tablets because color snapshots are not cached so this test |
| + // can't compare the UIImage pointers directly. |
| + if (IsIPadIdiom()) { |
| + return; |
| + } |
| + |
| + ClearDumpedImages(); |
| + |
| + SnapshotCache* cache = [SnapshotCache sharedInstance]; |
| + |
| + // Put all images in the cache. |
| + for (NSUInteger i = 0; i < kSessionCount; ++i) { |
| + UIImage* image = [testImages_ objectAtIndex:i]; |
| + NSString* sessionID = [testSessions_ objectAtIndex:i]; |
| + [cache setImage:image withSessionID:sessionID]; |
| + } |
| + |
| + // Get images back. |
| + __block NSUInteger numberOfCallbacks = 0; |
| + for (NSUInteger i = 0; i < kSessionCount; ++i) { |
| + NSString* sessionID = [testSessions_ objectAtIndex:i]; |
| + UIImage* expectedImage = [testImages_ objectAtIndex:i]; |
| + EXPECT_TRUE(expectedImage != nil); |
| + [cache retrieveImageForSessionID:sessionID |
| + callback:^(UIImage* image) { |
| + // Images have not been removed from the |
| + // dictionnary. We expect the same pointer. |
| + EXPECT_EQ(expectedImage, image); |
| + ++numberOfCallbacks; |
| + }]; |
| + } |
| + EXPECT_EQ(kSessionCount, numberOfCallbacks); |
| + |
| + ClearDumpedImages(); |
| +} |
| + |
| +// This test puts all the snapshots in the cache and flushes them to disk. |
| +// The snapshots are then reloaded from the disk, and the colors are compared. |
| +TEST_F(SnapshotCacheTest, SaveToDisk) { |
| + ClearDumpedImages(); |
| + |
| + SnapshotCache* cache = [SnapshotCache sharedInstance]; |
| + |
| + // Put all images in the cache. |
| + for (NSUInteger i = 0; i < kSessionCount; ++i) { |
| + UIImage* image = [testImages_ objectAtIndex:i]; |
| + NSString* sessionID = [testSessions_ objectAtIndex:i]; |
| + [cache setImage:image withSessionID:sessionID]; |
| + } |
| + [cache flushOperationQueue]; |
| + |
| + for (NSUInteger i = 0; i < kSessionCount; ++i) { |
| + // Check that images are on the disk. |
| + NSString* sessionID = [testSessions_ objectAtIndex:i]; |
| + |
| + base::FilePath path([cache imagePathForSessionID:sessionID]); |
| + EXPECT_TRUE(base::PathExists(path)); |
| + |
| + // Check image colors by comparing the first pixel against the reference |
| + // image. |
| + UIImage* image = |
| + [UIImage imageWithContentsOfFile:base::SysUTF8ToNSString(path.value())]; |
| + CGImageRef cgImage = [image CGImage]; |
| + const char* pixels = GetPixelData(cgImage); |
| + EXPECT_TRUE(pixels); |
| + |
| + UIImage* referenceImage = [testImages_ objectAtIndex:i]; |
| + CGImageRef referenceCgImage = [referenceImage CGImage]; |
| + const char* referencePixels = GetPixelData(referenceCgImage); |
| + EXPECT_TRUE(referencePixels); |
| + |
| + if (pixels != nil && referencePixels != nil) { |
| + // Color components may not be in the same order, |
| + // because of writing to disk and reloading. |
| + int red, green, blue; |
| + ComputeColorComponents(cgImage, &red, &green, &blue); |
| + |
| + int referenceRed, referenceGreen, referenceBlue; |
| + ComputeColorComponents(referenceCgImage, &referenceRed, &referenceGreen, |
| + &referenceBlue); |
| + |
| + // Colors may not be exactly the same (compression or rounding errors) |
| + // thus a small difference is allowed. |
| + EXPECT_NEAR(referencePixels[referenceRed], pixels[red], 1); |
| + EXPECT_NEAR(referencePixels[referenceGreen], pixels[green], 1); |
| + EXPECT_NEAR(referencePixels[referenceBlue], pixels[blue], 1); |
| + } |
| + } |
| + |
| + ClearDumpedImages(); |
| +} |
| + |
| +TEST_F(SnapshotCacheTest, PurgeTest) { |
| + ClearDumpedImages(); |
| + |
| + SnapshotCache* cache = [SnapshotCache sharedInstance]; |
| + |
| + // Put all images in the cache. |
| + for (NSUInteger i = 0; i < kSessionCount; ++i) { |
| + UIImage* image = [testImages_ objectAtIndex:i]; |
| + NSString* sessionID = [testSessions_ objectAtIndex:i]; |
| + [cache setImage:image withSessionID:sessionID]; |
| + } |
| + |
| + NSMutableSet* liveSessions = [NSMutableSet setWithCapacity:1]; |
| + [liveSessions addObject:[testSessions_ objectAtIndex:0]]; |
| + |
| + // Purge the cache. |
| + [cache purgeCacheOlderThan:(base::Time::Now() - base::TimeDelta::FromHours(1)) |
| + keeping:liveSessions]; |
| + [cache flushOperationQueue]; |
| + |
| + // Check that nothing has been deleted. |
| + for (NSUInteger i = 0; i < kSessionCount; ++i) { |
| + // Check that images are on the disk. |
| + NSString* sessionID = [testSessions_ objectAtIndex:i]; |
| + |
| + base::FilePath path([cache imagePathForSessionID:sessionID]); |
| + EXPECT_TRUE(base::PathExists(path)); |
| + } |
| + |
| + // Purge the cache. |
| + [cache purgeCacheOlderThan:base::Time::Now() keeping:liveSessions]; |
| + [cache flushOperationQueue]; |
| + |
| + // Check that the file have been deleted. |
| + for (NSUInteger i = 0; i < kSessionCount; ++i) { |
| + // Check that images are on the disk. |
| + NSString* sessionID = [testSessions_ objectAtIndex:i]; |
| + |
| + base::FilePath path([cache imagePathForSessionID:sessionID]); |
| + if (i == 0) |
| + EXPECT_TRUE(base::PathExists(path)); |
| + else |
| + EXPECT_FALSE(base::PathExists(path)); |
| + } |
| + ClearDumpedImages(); |
| +} |
| + |
| +// Loads the color images into the cache, and pins two of them. Ensures that |
| +// only the two pinned IDs remain in memory after a call to -handleLowMemory. |
| +TEST_F(SnapshotCacheTest, testHandleLowMemory) { |
| + ClearDumpedImages(); |
| + LoadAllColorImagesIntoCache(true); |
| + |
| + SnapshotCache* cache = [SnapshotCache sharedInstance]; |
| + |
| + NSString* firstPinnedID = [testSessions_ objectAtIndex:4]; |
| + NSString* secondPinnedID = [testSessions_ objectAtIndex:6]; |
| + NSMutableSet* set = [NSMutableSet set]; |
| + [set addObject:firstPinnedID]; |
| + [set addObject:secondPinnedID]; |
| + cache.pinnedIDs = set; |
| + |
| + [cache handleLowMemory]; |
| + |
| + BOOL expectedValue = YES; |
| + if (IsIPadIdiom()) { |
| + expectedValue = NO; |
| + } |
| + EXPECT_EQ(expectedValue, [cache hasImageInMemory:firstPinnedID]); |
| + EXPECT_EQ(expectedValue, [cache hasImageInMemory:secondPinnedID]); |
| + |
| + NSString* notPinnedID = [testSessions_ objectAtIndex:2]; |
| + EXPECT_FALSE([cache hasImageInMemory:notPinnedID]); |
| + |
| + // Wait for the final image to be pulled off disk. |
| + [cache flushOperationQueue]; |
| + ClearDumpedImages(); |
| +} |
| + |
| +// Tests that createGreyCache creates the grey snapshots in the background, |
| +// from color images in the in-memory cache. When the grey images are all |
| +// loaded into memory, tests that the request to retrieve the grey snapshot |
| +// calls the callback immediately. |
| +// Disabled on simulators because it sometimes crashes. crbug/421425 |
| +#if !TARGET_IPHONE_SIMULATOR |
| +TEST_F(SnapshotCacheTest, testCreateGreyCache) { |
| + ClearDumpedImages(); |
| + LoadAllColorImagesIntoCache(true); |
| + |
| + // Request the creation of a grey image cache for all images. |
| + SnapshotCache* cache = [SnapshotCache sharedInstance]; |
| + [cache createGreyCache:testSessions_]; |
| + |
| + // Wait for them to be put into the grey image cache. |
| + WaitForGreyImagesInCache(kSessionCount); |
| + |
| + __block NSUInteger numberOfCallbacks = 0; |
| + for (NSUInteger i = 0; i < kSessionCount; ++i) { |
| + NSString* sessionID = [testSessions_ objectAtIndex:i]; |
| + [cache retrieveGreyImageForSessionID:sessionID |
| + callback:^(UIImage* image) { |
| + EXPECT_TRUE(image); |
| + ++numberOfCallbacks; |
| + }]; |
| + } |
| + |
| + EXPECT_EQ(numberOfCallbacks, kSessionCount); |
| + ClearDumpedImages(); |
| +} |
| + |
| +// Same as previous test, except that all the color images are on disk, |
| +// rather than in memory. |
| +// Disabled due to the greyImage crash. b/8048597 |
| +TEST_F(SnapshotCacheTest, testCreateGreyCacheFromDisk) { |
| + ClearDumpedImages(); |
| + LoadAllColorImagesIntoCache(true); |
| + |
| + // Remove color images from in-memory cache. |
| + SnapshotCache* cache = [SnapshotCache sharedInstance]; |
| + [cache handleLowMemory]; |
| + |
| + // Request the creation of a grey image cache for all images. |
| + [cache createGreyCache:testSessions_]; |
| + |
| + // Wait for them to be put into the grey image cache. |
| + WaitForGreyImagesInCache(kSessionCount); |
| + |
| + __block NSUInteger numberOfCallbacks = 0; |
| + for (NSUInteger i = 0; i < kSessionCount; ++i) { |
| + NSString* sessionID = [testSessions_ objectAtIndex:i]; |
| + [cache retrieveGreyImageForSessionID:sessionID |
| + callback:^(UIImage* image) { |
| + EXPECT_TRUE(image); |
| + ++numberOfCallbacks; |
| + }]; |
| + } |
| + |
| + EXPECT_EQ(numberOfCallbacks, kSessionCount); |
| + ClearDumpedImages(); |
| +} |
| +#endif // !TARGET_IPHONE_SIMULATOR |
| + |
| +// Tests mostRecentGreyBlock, which is a block to be called when the most |
| +// recently requested grey image is finally loaded. |
| +// The test requests three images be cached as grey images. Only the final |
| +// callback of the three requests should be called. |
| +// Disabled due to the greyImage crash. b/8048597 |
| +TEST_F(SnapshotCacheTest, testMostRecentGreyBlock) { |
| + ClearDumpedImages(); |
| + |
| + const NSUInteger kNumImages = 3; |
| + base::scoped_nsobject<NSMutableArray> sessionIDs( |
| + [[NSMutableArray alloc] initWithCapacity:kNumImages]); |
| + [sessionIDs addObject:[testSessions_ objectAtIndex:0]]; |
| + [sessionIDs addObject:[testSessions_ objectAtIndex:1]]; |
| + [sessionIDs addObject:[testSessions_ objectAtIndex:2]]; |
| + |
| + SnapshotCache* cache = [SnapshotCache sharedInstance]; |
| + |
| + // Put 3 images in the cache. |
| + LoadColorImagesIntoCache(kNumImages, true); |
| + // Make sure the color images are only on disk, to ensure the background |
| + // thread is slow enough to queue up the requests. |
| + [cache handleLowMemory]; |
| + |
| + // Enable the grey image cache. |
| + [cache createGreyCache:sessionIDs]; |
| + |
| + // Request the grey versions |
| + __block BOOL firstCallbackCalled = NO; |
| + __block BOOL secondCallbackCalled = NO; |
| + __block BOOL thirdCallbackCalled = NO; |
| + [cache greyImageForSessionID:[testSessions_ objectAtIndex:0] |
| + callback:^(UIImage*) { |
| + firstCallbackCalled = YES; |
| + }]; |
| + [cache greyImageForSessionID:[testSessions_ objectAtIndex:1] |
| + callback:^(UIImage*) { |
| + secondCallbackCalled = YES; |
| + }]; |
| + [cache greyImageForSessionID:[testSessions_ objectAtIndex:2] |
| + callback:^(UIImage*) { |
| + thirdCallbackCalled = YES; |
| + }]; |
| + |
| + // Wait for them to be loaded. |
| + WaitForGreyImagesInCache(kNumImages); |
| + |
| + EXPECT_FALSE(firstCallbackCalled); |
| + EXPECT_FALSE(secondCallbackCalled); |
| + EXPECT_TRUE(thirdCallbackCalled); |
| + |
| + ClearDumpedImages(); |
| +} |
| + |
| +// Test the function used to save a grey copy of a color snapshot fully on a |
| +// background thread when the application is backgrounded. |
| +// Disabled due to the greyImage crash. b/8048597 |
| +TEST_F(SnapshotCacheTest, testGreyImageAllInBackground) { |
| + ClearDumpedImages(); |
| + LoadAllColorImagesIntoCache(true); |
| + |
| + SnapshotCache* cache = [SnapshotCache sharedInstance]; |
| + |
| + // Now convert every image into a grey image, on disk, in the background. |
| + for (NSUInteger i = 0; i < kSessionCount; ++i) { |
| + [cache saveGreyInBackgroundForSessionID:[testSessions_ objectAtIndex:i]]; |
| + } |
| + |
| + WaitForAllGreyImagesOnDisk(); |
| + |
| + for (NSUInteger i = 0; i < kSessionCount; ++i) { |
| + base::FilePath greyImagePath( |
| + [cache greyImagePathForSessionID:[testSessions_ objectAtIndex:i]]); |
| + EXPECT_TRUE(base::PathExists(greyImagePath)); |
| + base::DeleteFile(greyImagePath, false); |
| + } |
| + |
| + ClearDumpedImages(); |
| +} |
| + |
| +// Verifies that image size and scale are preserved when writing and reading |
| +// from disk. |
| +TEST_F(SnapshotCacheTest, TestSizeAndScalePreservation) { |
| + // Create an image with the expected snapshot scale. |
| + CGFloat scale = [SnapshotCache snapshotScaleForDevice]; |
| + UIGraphicsBeginImageContextWithOptions( |
| + CGSizeMake(kSnapshotPixelSize, kSnapshotPixelSize), NO, scale); |
| + CGContextRef context = UIGraphicsGetCurrentContext(); |
| + UIImage* image = GenerateRandomImage(context); |
| + UIGraphicsEndImageContext(); |
| + |
| + // Add the image to the cache then call handle low memory to ensure the image |
| + // is read from disk instead of the in-memory cache. |
| + SnapshotCache* cache = [SnapshotCache sharedInstance]; |
| + NSString* const kSession = @"foo"; |
| + [cache setImage:image withSessionID:kSession]; |
| + [cache flushOperationQueue]; // ensure the file is written to disk. |
| + [cache handleLowMemory]; |
| + |
| + // Retrive the image and have the callback verify the size and scale. |
| + __block BOOL callbackComplete = NO; |
| + [cache retrieveImageForSessionID:kSession |
| + callback:^(UIImage* imageFromDisk) { |
| + EXPECT_EQ(image.size.width, |
| + imageFromDisk.size.width); |
| + EXPECT_EQ(image.size.height, |
| + imageFromDisk.size.height); |
| + EXPECT_EQ(image.scale, imageFromDisk.scale); |
| + callbackComplete = YES; |
| + }]; |
| + |
| + // Spin the main message loop until the callback is run. Time out after a |
| + // a while to avoid waiting forever. |
| + const NSTimeInterval kTimeoutSec = 3; |
| + base::WaitUntilCondition(^bool() { |
| + return callbackComplete; |
| + }, nullptr, base::TimeDelta::FromSecondsD(kTimeoutSec)); |
| + EXPECT_TRUE(callbackComplete); |
| +} |
| + |
| +// Verifies that retina-scale images are deleted properly. |
| +TEST_F(SnapshotCacheTest, TestDeleteRetinaImages) { |
| + if ([SnapshotCache snapshotScaleForDevice] != 2.0) { |
| + return; |
| + } |
| + |
| + // Create an image with retina scale. |
| + UIGraphicsBeginImageContextWithOptions( |
| + CGSizeMake(kSnapshotPixelSize, kSnapshotPixelSize), NO, 2.0); |
| + CGContextRef context = UIGraphicsGetCurrentContext(); |
| + UIImage* image = GenerateRandomImage(context); |
| + UIGraphicsEndImageContext(); |
| + |
| + // Add the image to the cache then call handle low memory to ensure the image |
| + // is read from disk instead of the in-memory cache. |
| + SnapshotCache* cache = [SnapshotCache sharedInstance]; |
| + NSString* const kSession = @"foo"; |
| + [cache setImage:image withSessionID:kSession]; |
| + [cache flushOperationQueue]; // ensure the file is written to disk. |
| + [cache handleLowMemory]; |
| + |
| + // Verify the file was writted with @2x in the file name. |
| + base::FilePath retinaFile = [cache imagePathForSessionID:kSession]; |
| + EXPECT_TRUE(base::PathExists(retinaFile)); |
| + |
| + // Delete the image. |
| + [cache removeImageWithSessionID:kSession]; |
| + [cache flushOperationQueue]; // ensure the file is removed. |
| + |
| + EXPECT_FALSE(base::PathExists(retinaFile)); |
| +} |
| + |
| +} // namespace |