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 19namespace { 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. 24gfx::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. 62bool 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. 82template <class T> 83ImageStore* CreateStore(base::ScopedTempDir& folder); 84 85template <> 86ImageStore* CreateStore<TestImageStore>( 87 base::ScopedTempDir& folder) { 88 return new TestImageStore(); 89} 90 91template <> 92ImageStore* CreateStore<PersistentImageStore>( 93 base::ScopedTempDir& folder) { 94 return new PersistentImageStore(folder.path()); 95} 96 97// Methods to check if persistence is on or not. 98template <class T> bool ShouldPersist(); 99template <> bool ShouldPersist<TestImageStore>() { return false; } 100template <> bool ShouldPersist<PersistentImageStore>() { return true; } 101 102// Test fixture class template for the abstract API. 103template <class T> 104class 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. 133typedef testing::Types<TestImageStore, 134 PersistentImageStore> Implementations; 135 136TYPED_TEST_CASE(ImageStoreUnitTestIOS, Implementations); 137 138TYPED_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