1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "components/enhanced_bookmarks/image_store.h" 6 7 #import <UIKit/UIKit.h> 8 9 #include "base/files/scoped_temp_dir.h" 10 #include "base/mac/scoped_cftyperef.h" 11 #include "components/enhanced_bookmarks/image_store_util.h" 12 #include "components/enhanced_bookmarks/persistent_image_store.h" 13 #include "components/enhanced_bookmarks/test_image_store.h" 14 #include "testing/platform_test.h" 15 #include "ui/gfx/geometry/size.h" 16 #include "ui/gfx/image/image.h" 17 #include "url/gurl.h" 18 19 namespace { 20 21 // Generates a gfx::Image with a random UIImage representation. Uses off-center 22 // circle gradient to make all pixels slightly different in order to detect 23 // small image alterations. 24 gfx::Image GenerateRandomUIImage(gfx::Size& size, float scale) { 25 UIGraphicsBeginImageContextWithOptions(CGSizeMake(size.width(), 26 size.height()), 27 YES, // opaque. 28 scale); 29 // Create the gradient's colors. 30 CGFloat locations[] = { 0.0, 1.0 }; 31 CGFloat components[] = { rand()/CGFloat(RAND_MAX), // Start color r 32 rand()/CGFloat(RAND_MAX), // g 33 rand()/CGFloat(RAND_MAX), // b 34 1.0, // Alpha 35 rand()/CGFloat(RAND_MAX), // End color r 36 rand()/CGFloat(RAND_MAX), // g 37 rand()/CGFloat(RAND_MAX), // b 38 1.0 }; // Alpha 39 CGPoint center = CGPointMake(size.width() / 3, size.height() / 3); 40 CGFloat radius = MAX(size.width(), size.height()); 41 42 base::ScopedCFTypeRef<CGColorSpaceRef> 43 colorspace(CGColorSpaceCreateDeviceRGB()); 44 base::ScopedCFTypeRef<CGGradientRef> 45 gradient(CGGradientCreateWithColorComponents(colorspace, 46 components, 47 locations, 48 arraysize(locations))); 49 CGContextDrawRadialGradient(UIGraphicsGetCurrentContext(), 50 gradient, 51 center, 52 0, 53 center, 54 radius, 55 kCGGradientDrawsAfterEndLocation); 56 UIImage* image = UIGraphicsGetImageFromCurrentImageContext(); 57 UIGraphicsEndImageContext(); 58 return gfx::Image([image retain]); 59 } 60 61 // Returns true if the two images are identical. 62 bool CompareImages(const gfx::Image& image_1, const gfx::Image& image_2) { 63 if (image_1.IsEmpty() && image_2.IsEmpty()) 64 return true; 65 if (image_1.IsEmpty() || image_2.IsEmpty()) 66 return false; 67 68 scoped_refptr<base::RefCountedMemory> image_1_bytes = 69 enhanced_bookmarks::BytesForImage(image_1); 70 scoped_refptr<base::RefCountedMemory> image_2_bytes = 71 enhanced_bookmarks::BytesForImage(image_2); 72 73 if (image_1_bytes->size() != image_2_bytes->size()) 74 return false; 75 76 return !memcmp(image_1_bytes->front(), 77 image_2_bytes->front(), 78 image_1_bytes->size()); 79 } 80 81 // Factory functions for creating instances of the implementations. 82 template <class T> 83 ImageStore* CreateStore(base::ScopedTempDir& folder); 84 85 template <> 86 ImageStore* CreateStore<TestImageStore>( 87 base::ScopedTempDir& folder) { 88 return new TestImageStore(); 89 } 90 91 template <> 92 ImageStore* CreateStore<PersistentImageStore>( 93 base::ScopedTempDir& folder) { 94 return new PersistentImageStore(folder.path()); 95 } 96 97 // Methods to check if persistence is on or not. 98 template <class T> bool ShouldPersist(); 99 template <> bool ShouldPersist<TestImageStore>() { return false; } 100 template <> bool ShouldPersist<PersistentImageStore>() { return true; } 101 102 // Test fixture class template for the abstract API. 103 template <class T> 104 class ImageStoreUnitTestIOS : public PlatformTest { 105 protected: 106 ImageStoreUnitTestIOS() {} 107 virtual ~ImageStoreUnitTestIOS() {} 108 109 virtual void SetUp() OVERRIDE { 110 bool success = temp_dir_.CreateUniqueTempDir(); 111 ASSERT_TRUE(success); 112 store_.reset(CreateStore<T>(temp_dir_)); 113 } 114 115 virtual void TearDown() OVERRIDE { 116 if (store_ && use_persistent_store()) 117 store_->ClearAll(); 118 } 119 120 bool use_persistent_store() const { return ShouldPersist<T>(); } 121 void ResetStore() { store_.reset(CreateStore<T>(temp_dir_)); } 122 123 // The directory the database is saved into. 124 base::ScopedTempDir temp_dir_; 125 // The object the fixture is testing, via its base interface. 126 scoped_ptr<ImageStore> store_; 127 128 private: 129 DISALLOW_COPY_AND_ASSIGN(ImageStoreUnitTestIOS); 130 }; 131 132 // The list of implementations of the abstract API that are going to be tested. 133 typedef testing::Types<TestImageStore, 134 PersistentImageStore> Implementations; 135 136 TYPED_TEST_CASE(ImageStoreUnitTestIOS, Implementations); 137 138 TYPED_TEST(ImageStoreUnitTestIOS, StoringImagesPreservesScale) { 139 CGFloat scales[] = { 0.0, 1.0, 2.0 }; 140 gfx::Size image_size(42, 24); 141 for (unsigned long i = 0; i < arraysize(scales); i++) { 142 gfx::Image src_image(GenerateRandomUIImage(image_size, scales[i])); 143 const GURL url("foo://bar"); 144 const GURL image_url("a.jpg"); 145 this->store_->Insert(url, image_url, src_image); 146 std::pair<gfx::Image, GURL> image_info = this->store_->Get(url); 147 148 EXPECT_EQ(image_url, image_info.second); 149 EXPECT_TRUE(CompareImages(src_image, image_info.first)); 150 } 151 } 152 153 } // namespace 154