1 // Copyright (c) 2012 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 "content/test/image_decoder_test.h" 6 7 #include "base/file_util.h" 8 #include "base/files/file_enumerator.h" 9 #include "base/files/file_path.h" 10 #include "base/md5.h" 11 #include "base/memory/scoped_ptr.h" 12 #include "base/path_service.h" 13 #include "base/strings/string_util.h" 14 #include "base/strings/utf_string_conversions.h" 15 #include "third_party/WebKit/public/platform/WebData.h" 16 #include "third_party/WebKit/public/platform/WebImage.h" 17 #include "third_party/WebKit/public/platform/WebSize.h" 18 #include "third_party/WebKit/public/web/WebImageDecoder.h" 19 20 // Uncomment this to recalculate the MD5 sums; see header comments. 21 // #define CALCULATE_MD5_SUMS 22 23 namespace { 24 25 const int kFirstFrameIndex = 0; 26 27 // Determine if we should test with file specified by |path| based 28 // on |file_selection| and the |threshold| for the file size. 29 bool ShouldSkipFile(const base::FilePath& path, 30 ImageDecoderTestFileSelection file_selection, 31 const int64 threshold) { 32 if (file_selection == TEST_ALL) 33 return false; 34 35 int64 image_size = 0; 36 base::GetFileSize(path, &image_size); 37 return (file_selection == TEST_SMALLER) == (image_size > threshold); 38 } 39 40 } // namespace 41 42 void ReadFileToVector(const base::FilePath& path, std::vector<char>* contents) { 43 std::string raw_image_data; 44 base::ReadFileToString(path, &raw_image_data); 45 contents->resize(raw_image_data.size()); 46 memcpy(&contents->front(), raw_image_data.data(), raw_image_data.size()); 47 } 48 49 base::FilePath GetMD5SumPath(const base::FilePath& path) { 50 static const base::FilePath::StringType kDecodedDataExtension( 51 FILE_PATH_LITERAL(".md5sum")); 52 return base::FilePath(path.value() + kDecodedDataExtension); 53 } 54 55 #if defined(CALCULATE_MD5_SUMS) 56 void SaveMD5Sum(const base::FilePath& path, const blink::WebImage& web_image) { 57 // Calculate MD5 sum. 58 base::MD5Digest digest; 59 web_image.getSkBitmap().lockPixels(); 60 base::MD5Sum(web_image.getSkBitmap().getPixels(), 61 web_image.getSkBitmap().width() * 62 web_image.getSkBitmap().height() * sizeof(uint32_t), 63 &digest); 64 65 // Write sum to disk. 66 int bytes_written = base::WriteFile( 67 path, reinterpret_cast<const char*>(&digest), sizeof digest); 68 ASSERT_EQ(sizeof digest, bytes_written); 69 web_image.getSkBitmap().unlockPixels(); 70 } 71 #endif 72 73 #if !defined(CALCULATE_MD5_SUMS) 74 void VerifyImage(const blink::WebImageDecoder& decoder, 75 const base::FilePath& path, 76 const base::FilePath& md5_sum_path, 77 size_t frame_index) { 78 // Make sure decoding can complete successfully. 79 EXPECT_TRUE(decoder.isSizeAvailable()) << path.value(); 80 EXPECT_GE(decoder.frameCount(), frame_index) << path.value(); 81 EXPECT_TRUE(decoder.isFrameCompleteAtIndex(frame_index)) << path.value(); 82 EXPECT_FALSE(decoder.isFailed()); 83 84 // Calculate MD5 sum. 85 base::MD5Digest actual_digest; 86 blink::WebImage web_image = decoder.getFrameAtIndex(frame_index); 87 web_image.getSkBitmap().lockPixels(); 88 base::MD5Sum(web_image.getSkBitmap().getPixels(), 89 web_image.getSkBitmap().width() * 90 web_image.getSkBitmap().height() * sizeof(uint32_t), 91 &actual_digest); 92 93 // Read the MD5 sum off disk. 94 std::string file_bytes; 95 base::ReadFileToString(md5_sum_path, &file_bytes); 96 base::MD5Digest expected_digest; 97 ASSERT_EQ(sizeof expected_digest, file_bytes.size()) << path.value(); 98 memcpy(&expected_digest, file_bytes.data(), sizeof expected_digest); 99 100 // Verify that the sums are the same. 101 EXPECT_EQ(0, memcmp(&expected_digest, &actual_digest, sizeof actual_digest)) 102 << path.value(); 103 web_image.getSkBitmap().unlockPixels(); 104 } 105 #endif 106 107 void ImageDecoderTest::SetUp() { 108 base::FilePath data_dir; 109 ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &data_dir)); 110 data_dir_ = data_dir.AppendASCII("webkit").AppendASCII("data").AppendASCII( 111 format_ + "_decoder"); 112 if (!base::PathExists(data_dir_)) { 113 const testing::TestInfo* const test_info = 114 testing::UnitTest::GetInstance()->current_test_info(); 115 VLOG(0) << test_info->name() 116 << " not running because test data wasn't found."; 117 data_dir_.clear(); 118 return; 119 } 120 } 121 122 std::vector<base::FilePath> ImageDecoderTest::GetImageFiles() const { 123 std::string pattern = "*." + format_; 124 base::FileEnumerator enumerator(data_dir_, false, 125 base::FileEnumerator::FILES); 126 std::vector<base::FilePath> image_files; 127 for (base::FilePath next_file_name = enumerator.Next(); 128 !next_file_name.empty(); next_file_name = enumerator.Next()) { 129 base::FilePath base_name = next_file_name.BaseName(); 130 #if defined(OS_WIN) 131 std::string base_name_ascii = base::UTF16ToASCII(base_name.value()); 132 #else 133 std::string base_name_ascii = base_name.value(); 134 #endif 135 if (MatchPattern(base_name_ascii, pattern)) 136 image_files.push_back(next_file_name); 137 } 138 139 return image_files; 140 } 141 142 bool ImageDecoderTest::ShouldImageFail(const base::FilePath& path) const { 143 const base::FilePath::StringType kBadSuffix(FILE_PATH_LITERAL(".bad.")); 144 return (path.value().length() > (kBadSuffix.length() + format_.length()) && 145 !path.value().compare(path.value().length() - format_.length() - 146 kBadSuffix.length(), 147 kBadSuffix.length(), kBadSuffix)); 148 } 149 150 void ImageDecoderTest::TestDecoding( 151 ImageDecoderTestFileSelection file_selection, 152 const int64 threshold) { 153 if (data_dir_.empty()) 154 return; 155 const std::vector<base::FilePath> image_files(GetImageFiles()); 156 for (std::vector<base::FilePath>::const_iterator i = image_files.begin(); 157 i != image_files.end(); ++i) { 158 if (!ShouldSkipFile(*i, file_selection, threshold)) 159 TestWebKitImageDecoder(*i, GetMD5SumPath(*i), kFirstFrameIndex); 160 } 161 } 162 163 void ImageDecoderTest::TestWebKitImageDecoder( 164 const base::FilePath& image_path, 165 const base::FilePath& md5_sum_path, 166 int desired_frame_index) const { 167 #if defined(CALCULATE_MD5_SUMS) 168 // If we're just calculating the MD5 sums, skip failing images quickly. 169 if (ShouldImageFail(image_path)) 170 return; 171 #endif 172 173 std::vector<char> image_contents; 174 ReadFileToVector(image_path, &image_contents); 175 EXPECT_TRUE(image_contents.size()); 176 scoped_ptr<blink::WebImageDecoder> decoder(CreateWebKitImageDecoder()); 177 EXPECT_FALSE(decoder->isFailed()); 178 179 #if !defined(CALCULATE_MD5_SUMS) 180 // Test chunking file into half. 181 const int partial_size = image_contents.size()/2; 182 183 blink::WebData partial_data( 184 reinterpret_cast<const char*>(&(image_contents.at(0))), partial_size); 185 186 // Make Sure the image decoder doesn't fail when we ask for the frame 187 // buffer for this partial image. 188 // NOTE: We can't check that frame 0 is non-NULL, because if this is an 189 // ICO and we haven't yet supplied enough data to read the directory, 190 // there is no framecount and thus no first frame. 191 decoder->setData(const_cast<blink::WebData&>(partial_data), false); 192 EXPECT_FALSE(decoder->isFailed()) << image_path.value(); 193 #endif 194 195 // Make sure passing the complete image results in successful decoding. 196 blink::WebData data(reinterpret_cast<const char*>(&(image_contents.at(0))), 197 image_contents.size()); 198 decoder->setData(const_cast<blink::WebData&>(data), true); 199 if (ShouldImageFail(image_path)) { 200 EXPECT_FALSE(decoder->isFrameCompleteAtIndex(kFirstFrameIndex)); 201 EXPECT_TRUE(decoder->isFailed()); 202 } else { 203 EXPECT_FALSE(decoder->isFailed()) << image_path.value(); 204 #if defined(CALCULATE_MD5_SUMS) 205 SaveMD5Sum(md5_sum_path, decoder->getFrameAtIndex(desired_frame_index)); 206 #else 207 VerifyImage(*decoder, image_path, md5_sum_path, desired_frame_index); 208 #endif 209 } 210 } 211