| OLD | NEW |
| 1 // Copyright 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #import "ios/chrome/browser/snapshots/snapshot_cache.h" | 5 #import "ios/chrome/browser/snapshots/snapshot_cache.h" |
| 6 | 6 |
| 7 #import <Foundation/Foundation.h> | 7 #import <Foundation/Foundation.h> |
| 8 | 8 |
| 9 #include "base/files/file_path.h" | 9 #include "base/files/file_path.h" |
| 10 #include "base/files/file_util.h" | 10 #include "base/files/file_util.h" |
| 11 #include "base/format_macros.h" | 11 #include "base/format_macros.h" |
| 12 #include "base/location.h" | 12 #include "base/location.h" |
| 13 #include "base/mac/bind_objc_block.h" | 13 #include "base/mac/bind_objc_block.h" |
| 14 #include "base/mac/scoped_cftyperef.h" | 14 #include "base/mac/scoped_cftyperef.h" |
| 15 #include "base/mac/scoped_nsautorelease_pool.h" | |
| 16 #include "base/mac/scoped_nsobject.h" | |
| 17 #include "base/run_loop.h" | 15 #include "base/run_loop.h" |
| 18 #include "base/strings/sys_string_conversions.h" | 16 #include "base/strings/sys_string_conversions.h" |
| 19 #include "base/task_scheduler/task_scheduler.h" | 17 #include "base/task_scheduler/task_scheduler.h" |
| 20 #include "base/threading/sequenced_worker_pool.h" | 18 #include "base/threading/sequenced_worker_pool.h" |
| 21 #include "base/time/time.h" | 19 #include "base/time/time.h" |
| 22 #import "ios/chrome/browser/snapshots/snapshot_cache_internal.h" | 20 #import "ios/chrome/browser/snapshots/snapshot_cache_internal.h" |
| 23 #include "ios/web/public/test/test_web_thread_bundle.h" | 21 #include "ios/web/public/test/test_web_thread_bundle.h" |
| 24 #include "ios/web/public/web_thread.h" | 22 #include "ios/web/public/web_thread.h" |
| 25 #include "testing/gtest/include/gtest/gtest.h" | 23 #include "testing/gtest/include/gtest/gtest.h" |
| 26 #include "testing/gtest_mac.h" | 24 #include "testing/gtest_mac.h" |
| 27 #include "testing/platform_test.h" | 25 #include "testing/platform_test.h" |
| 28 | 26 |
| 27 #if !defined(__has_feature) || !__has_feature(objc_arc) |
| 28 #error "This file requires ARC support." |
| 29 #endif |
| 30 |
| 29 static const NSUInteger kSessionCount = 10; | 31 static const NSUInteger kSessionCount = 10; |
| 30 static const NSUInteger kSnapshotPixelSize = 8; | 32 static const NSUInteger kSnapshotPixelSize = 8; |
| 31 | 33 |
| 32 namespace { | 34 namespace { |
| 33 | 35 |
| 34 class SnapshotCacheTest : public PlatformTest { | 36 class SnapshotCacheTest : public PlatformTest { |
| 35 protected: | 37 protected: |
| 36 // Build an array of session names and an array of UIImages filled with | 38 // Build an array of session names and an array of UIImages filled with |
| 37 // random colors. | 39 // random colors. |
| 38 void SetUp() override { | 40 void SetUp() override { |
| 39 PlatformTest::SetUp(); | 41 PlatformTest::SetUp(); |
| 40 snapshotCache_.reset([[SnapshotCache alloc] init]); | 42 snapshotCache_ = [[SnapshotCache alloc] init]; |
| 41 testImages_.reset([[NSMutableArray alloc] initWithCapacity:kSessionCount]); | 43 testImages_ = [[NSMutableArray alloc] initWithCapacity:kSessionCount]; |
| 42 testSessions_.reset( | 44 testSessions_ = [[NSMutableArray alloc] initWithCapacity:kSessionCount]; |
| 43 [[NSMutableArray alloc] initWithCapacity:kSessionCount]); | |
| 44 | 45 |
| 45 CGFloat scale = [snapshotCache_ snapshotScaleForDevice]; | 46 CGFloat scale = [snapshotCache_ snapshotScaleForDevice]; |
| 46 UIGraphicsBeginImageContextWithOptions( | 47 UIGraphicsBeginImageContextWithOptions( |
| 47 CGSizeMake(kSnapshotPixelSize, kSnapshotPixelSize), NO, scale); | 48 CGSizeMake(kSnapshotPixelSize, kSnapshotPixelSize), NO, scale); |
| 48 CGContextRef context = UIGraphicsGetCurrentContext(); | 49 CGContextRef context = UIGraphicsGetCurrentContext(); |
| 49 srand(1); | 50 srand(1); |
| 50 | 51 |
| 51 for (NSUInteger i = 0; i < kSessionCount; ++i) { | 52 for (NSUInteger i = 0; i < kSessionCount; ++i) { |
| 52 UIImage* image = GenerateRandomImage(context); | 53 UIImage* image = GenerateRandomImage(context); |
| 53 [testImages_ addObject:image]; | 54 [testImages_ addObject:image]; |
| 54 [testSessions_ | 55 [testSessions_ |
| 55 addObject:[NSString stringWithFormat:@"SessionId-%" PRIuNS, i]]; | 56 addObject:[NSString stringWithFormat:@"SessionId-%" PRIuNS, i]]; |
| 56 } | 57 } |
| 57 | 58 |
| 58 UIGraphicsEndImageContext(); | 59 UIGraphicsEndImageContext(); |
| 59 | 60 |
| 60 ClearDumpedImages(); | 61 ClearDumpedImages(); |
| 61 } | 62 } |
| 62 | 63 |
| 63 void TearDown() override { | 64 void TearDown() override { |
| 64 ClearDumpedImages(); | 65 ClearDumpedImages(); |
| 65 [snapshotCache_ shutdown]; | 66 [snapshotCache_ shutdown]; |
| 66 snapshotCache_.reset(); | 67 snapshotCache_ = nil; |
| 67 PlatformTest::TearDown(); | 68 PlatformTest::TearDown(); |
| 68 } | 69 } |
| 69 | 70 |
| 70 SnapshotCache* GetSnapshotCache() { | 71 SnapshotCache* GetSnapshotCache() { return snapshotCache_; } |
| 71 return snapshotCache_.get(); | |
| 72 } | |
| 73 | 72 |
| 74 // Generates an image filled with a random color. | 73 // Generates an image filled with a random color. |
| 75 UIImage* GenerateRandomImage(CGContextRef context) { | 74 UIImage* GenerateRandomImage(CGContextRef context) { |
| 76 CGFloat r = rand() / CGFloat(RAND_MAX); | 75 CGFloat r = rand() / CGFloat(RAND_MAX); |
| 77 CGFloat g = rand() / CGFloat(RAND_MAX); | 76 CGFloat g = rand() / CGFloat(RAND_MAX); |
| 78 CGFloat b = rand() / CGFloat(RAND_MAX); | 77 CGFloat b = rand() / CGFloat(RAND_MAX); |
| 79 CGContextSetRGBStrokeColor(context, r, g, b, 1.0); | 78 CGContextSetRGBStrokeColor(context, r, g, b, 1.0); |
| 80 CGContextSetRGBFillColor(context, r, g, b, 1.0); | 79 CGContextSetRGBFillColor(context, r, g, b, 1.0); |
| 81 CGContextFillRect( | 80 CGContextFillRect( |
| 82 context, CGRectMake(0.0, 0.0, kSnapshotPixelSize, kSnapshotPixelSize)); | 81 context, CGRectMake(0.0, 0.0, kSnapshotPixelSize, kSnapshotPixelSize)); |
| 83 return UIGraphicsGetImageFromCurrentImageContext(); | 82 return UIGraphicsGetImageFromCurrentImageContext(); |
| 84 } | 83 } |
| 85 | 84 |
| 86 // Flushes all the runloops internally used by the snapshot cache. | 85 // Flushes all the runloops internally used by the snapshot cache. |
| 87 void FlushRunLoops() { | 86 void FlushRunLoops() { |
| 88 base::TaskScheduler::GetInstance()->FlushForTesting(); | 87 base::TaskScheduler::GetInstance()->FlushForTesting(); |
| 89 base::RunLoop().RunUntilIdle(); | 88 base::RunLoop().RunUntilIdle(); |
| 90 } | 89 } |
| 91 | 90 |
| 92 // This function removes the snapshots both from dictionary and from disk. | 91 // This function removes the snapshots both from dictionary and from disk. |
| 93 void ClearDumpedImages() { | 92 void ClearDumpedImages() { |
| 94 SnapshotCache* cache = GetSnapshotCache(); | 93 SnapshotCache* cache = GetSnapshotCache(); |
| 95 | 94 |
| 96 NSString* sessionID; | 95 NSString* sessionID; |
| 97 for (sessionID in testSessions_.get()) | 96 for (sessionID in testSessions_) |
| 98 [cache removeImageWithSessionID:sessionID]; | 97 [cache removeImageWithSessionID:sessionID]; |
| 99 | 98 |
| 100 FlushRunLoops(); | 99 FlushRunLoops(); |
| 101 // The above calls to -removeImageWithSessionID remove both the color | 100 // The above calls to -removeImageWithSessionID remove both the color |
| 102 // and grey snapshots for each sessionID, if they are on disk. However, | 101 // and grey snapshots for each sessionID, if they are on disk. However, |
| 103 // ensure we also get rid of the grey snapshots in memory. | 102 // ensure we also get rid of the grey snapshots in memory. |
| 104 [cache removeGreyCache]; | 103 [cache removeGreyCache]; |
| 105 | 104 |
| 106 __block BOOL foundImage = NO; | 105 __block BOOL foundImage = NO; |
| 107 __block NSUInteger numCallbacks = 0; | 106 __block NSUInteger numCallbacks = 0; |
| 108 for (sessionID in testSessions_.get()) { | 107 for (sessionID in testSessions_) { |
| 109 base::FilePath path([cache imagePathForSessionID:sessionID]); | 108 base::FilePath path([cache imagePathForSessionID:sessionID]); |
| 110 | 109 |
| 111 // Checks that the snapshot is not on disk. | 110 // Checks that the snapshot is not on disk. |
| 112 EXPECT_FALSE(base::PathExists(path)); | 111 EXPECT_FALSE(base::PathExists(path)); |
| 113 | 112 |
| 114 // Check that the snapshot is not in the dictionary. | 113 // Check that the snapshot is not in the dictionary. |
| 115 [cache retrieveImageForSessionID:sessionID | 114 [cache retrieveImageForSessionID:sessionID |
| 116 callback:^(UIImage* image) { | 115 callback:^(UIImage* image) { |
| 117 ++numCallbacks; | 116 ++numCallbacks; |
| 118 if (image) | 117 if (image) |
| (...skipping 12 matching lines...) Expand all Loading... |
| 131 void LoadAllColorImagesIntoCache(bool waitForFilesOnDisk) { | 130 void LoadAllColorImagesIntoCache(bool waitForFilesOnDisk) { |
| 132 LoadColorImagesIntoCache(kSessionCount, waitForFilesOnDisk); | 131 LoadColorImagesIntoCache(kSessionCount, waitForFilesOnDisk); |
| 133 } | 132 } |
| 134 | 133 |
| 135 // Loads |count| color images into the cache. If |waitForFilesOnDisk| | 134 // Loads |count| color images into the cache. If |waitForFilesOnDisk| |
| 136 // is YES, will not return until the images have been written to disk. | 135 // is YES, will not return until the images have been written to disk. |
| 137 void LoadColorImagesIntoCache(NSUInteger count, bool waitForFilesOnDisk) { | 136 void LoadColorImagesIntoCache(NSUInteger count, bool waitForFilesOnDisk) { |
| 138 SnapshotCache* cache = GetSnapshotCache(); | 137 SnapshotCache* cache = GetSnapshotCache(); |
| 139 // Put color images in the cache. | 138 // Put color images in the cache. |
| 140 for (NSUInteger i = 0; i < count; ++i) { | 139 for (NSUInteger i = 0; i < count; ++i) { |
| 141 base::mac::ScopedNSAutoreleasePool pool; | 140 @autoreleasepool { |
| 142 UIImage* image = [testImages_ objectAtIndex:i]; | 141 UIImage* image = [testImages_ objectAtIndex:i]; |
| 143 NSString* sessionID = [testSessions_ objectAtIndex:i]; | 142 NSString* sessionID = [testSessions_ objectAtIndex:i]; |
| 144 [cache setImage:image withSessionID:sessionID]; | 143 [cache setImage:image withSessionID:sessionID]; |
| 144 } |
| 145 } | 145 } |
| 146 if (waitForFilesOnDisk) { | 146 if (waitForFilesOnDisk) { |
| 147 FlushRunLoops(); | 147 FlushRunLoops(); |
| 148 for (NSUInteger i = 0; i < count; ++i) { | 148 for (NSUInteger i = 0; i < count; ++i) { |
| 149 // Check that images are on the disk. | 149 // Check that images are on the disk. |
| 150 NSString* sessionID = [testSessions_ objectAtIndex:i]; | 150 NSString* sessionID = [testSessions_ objectAtIndex:i]; |
| 151 base::FilePath path([cache imagePathForSessionID:sessionID]); | 151 base::FilePath path([cache imagePathForSessionID:sessionID]); |
| 152 EXPECT_TRUE(base::PathExists(path)); | 152 EXPECT_TRUE(base::PathExists(path)); |
| 153 } | 153 } |
| 154 } | 154 } |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 198 // _performMemoryWarning is a private API and must not be compiled into | 198 // _performMemoryWarning is a private API and must not be compiled into |
| 199 // official builds. | 199 // official builds. |
| 200 #pragma clang diagnostic push | 200 #pragma clang diagnostic push |
| 201 #pragma clang diagnostic ignored "-Wundeclared-selector" | 201 #pragma clang diagnostic ignored "-Wundeclared-selector" |
| 202 [[UIApplication sharedApplication] | 202 [[UIApplication sharedApplication] |
| 203 performSelector:@selector(_performMemoryWarning)]; | 203 performSelector:@selector(_performMemoryWarning)]; |
| 204 #pragma clang diagnostic pop | 204 #pragma clang diagnostic pop |
| 205 } | 205 } |
| 206 | 206 |
| 207 web::TestWebThreadBundle thread_bundle_; | 207 web::TestWebThreadBundle thread_bundle_; |
| 208 base::scoped_nsobject<SnapshotCache> snapshotCache_; | 208 SnapshotCache* snapshotCache_; |
| 209 base::scoped_nsobject<NSMutableArray> testSessions_; | 209 NSMutableArray* testSessions_; |
| 210 base::scoped_nsobject<NSMutableArray> testImages_; | 210 NSMutableArray* testImages_; |
| 211 }; | 211 }; |
| 212 | 212 |
| 213 // This test simply put all the snapshots in the cache and then gets them back | 213 // This test simply put all the snapshots in the cache and then gets them back |
| 214 // As the snapshots are kept in memory, the same pointer can be retrieved. | 214 // As the snapshots are kept in memory, the same pointer can be retrieved. |
| 215 // This test also checks that images are correctly removed from the disk. | 215 // This test also checks that images are correctly removed from the disk. |
| 216 TEST_F(SnapshotCacheTest, Cache) { | 216 TEST_F(SnapshotCacheTest, Cache) { |
| 217 SnapshotCache* cache = GetSnapshotCache(); | 217 SnapshotCache* cache = GetSnapshotCache(); |
| 218 | 218 |
| 219 NSUInteger expectedCacheSize = MIN(kSessionCount, [cache lruCacheMaxSize]); | 219 NSUInteger expectedCacheSize = MIN(kSessionCount, [cache lruCacheMaxSize]); |
| 220 | 220 |
| (...skipping 211 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 432 } | 432 } |
| 433 #endif // !TARGET_IPHONE_SIMULATOR | 433 #endif // !TARGET_IPHONE_SIMULATOR |
| 434 | 434 |
| 435 // Tests mostRecentGreyBlock, which is a block to be called when the most | 435 // Tests mostRecentGreyBlock, which is a block to be called when the most |
| 436 // recently requested grey image is finally loaded. | 436 // recently requested grey image is finally loaded. |
| 437 // The test requests three images be cached as grey images. Only the final | 437 // The test requests three images be cached as grey images. Only the final |
| 438 // callback of the three requests should be called. | 438 // callback of the three requests should be called. |
| 439 // Disabled due to the greyImage crash. b/8048597 | 439 // Disabled due to the greyImage crash. b/8048597 |
| 440 TEST_F(SnapshotCacheTest, MostRecentGreyBlock) { | 440 TEST_F(SnapshotCacheTest, MostRecentGreyBlock) { |
| 441 const NSUInteger kNumImages = 3; | 441 const NSUInteger kNumImages = 3; |
| 442 base::scoped_nsobject<NSMutableArray> sessionIDs( | 442 NSMutableArray* sessionIDs = |
| 443 [[NSMutableArray alloc] initWithCapacity:kNumImages]); | 443 [[NSMutableArray alloc] initWithCapacity:kNumImages]; |
| 444 [sessionIDs addObject:[testSessions_ objectAtIndex:0]]; | 444 [sessionIDs addObject:[testSessions_ objectAtIndex:0]]; |
| 445 [sessionIDs addObject:[testSessions_ objectAtIndex:1]]; | 445 [sessionIDs addObject:[testSessions_ objectAtIndex:1]]; |
| 446 [sessionIDs addObject:[testSessions_ objectAtIndex:2]]; | 446 [sessionIDs addObject:[testSessions_ objectAtIndex:2]]; |
| 447 | 447 |
| 448 SnapshotCache* cache = GetSnapshotCache(); | 448 SnapshotCache* cache = GetSnapshotCache(); |
| 449 | 449 |
| 450 // Put 3 images in the cache. | 450 // Put 3 images in the cache. |
| 451 LoadColorImagesIntoCache(kNumImages, true); | 451 LoadColorImagesIntoCache(kNumImages, true); |
| 452 // Make sure the color images are only on disk, to ensure the background | 452 // Make sure the color images are only on disk, to ensure the background |
| 453 // thread is slow enough to queue up the requests. | 453 // thread is slow enough to queue up the requests. |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 490 | 490 |
| 491 // Now convert every image into a grey image, on disk, in the background. | 491 // Now convert every image into a grey image, on disk, in the background. |
| 492 for (NSUInteger i = 0; i < kSessionCount; ++i) { | 492 for (NSUInteger i = 0; i < kSessionCount; ++i) { |
| 493 [cache saveGreyInBackgroundForSessionID:[testSessions_ objectAtIndex:i]]; | 493 [cache saveGreyInBackgroundForSessionID:[testSessions_ objectAtIndex:i]]; |
| 494 } | 494 } |
| 495 | 495 |
| 496 // Waits for the grey images for the sessions in |testSessions_| to be written | 496 // Waits for the grey images for the sessions in |testSessions_| to be written |
| 497 // to disk, which happens in a background thread. | 497 // to disk, which happens in a background thread. |
| 498 FlushRunLoops(); | 498 FlushRunLoops(); |
| 499 | 499 |
| 500 for (NSString* sessionID in testSessions_.get()) { | 500 for (NSString* sessionID in testSessions_) { |
| 501 base::FilePath path([cache greyImagePathForSessionID:sessionID]); | 501 base::FilePath path([cache greyImagePathForSessionID:sessionID]); |
| 502 EXPECT_TRUE(base::PathExists(path)); | 502 EXPECT_TRUE(base::PathExists(path)); |
| 503 base::DeleteFile(path, false); | 503 base::DeleteFile(path, false); |
| 504 } | 504 } |
| 505 } | 505 } |
| 506 | 506 |
| 507 // Verifies that image size and scale are preserved when writing and reading | 507 // Verifies that image size and scale are preserved when writing and reading |
| 508 // from disk. | 508 // from disk. |
| 509 TEST_F(SnapshotCacheTest, SizeAndScalePreservation) { | 509 TEST_F(SnapshotCacheTest, SizeAndScalePreservation) { |
| 510 SnapshotCache* cache = GetSnapshotCache(); | 510 SnapshotCache* cache = GetSnapshotCache(); |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 565 EXPECT_TRUE(base::PathExists(retinaFile)); | 565 EXPECT_TRUE(base::PathExists(retinaFile)); |
| 566 | 566 |
| 567 // Delete the image. | 567 // Delete the image. |
| 568 [cache removeImageWithSessionID:kSession]; | 568 [cache removeImageWithSessionID:kSession]; |
| 569 FlushRunLoops(); // ensure the file is removed. | 569 FlushRunLoops(); // ensure the file is removed. |
| 570 | 570 |
| 571 EXPECT_FALSE(base::PathExists(retinaFile)); | 571 EXPECT_FALSE(base::PathExists(retinaFile)); |
| 572 } | 572 } |
| 573 | 573 |
| 574 } // namespace | 574 } // namespace |
| OLD | NEW |