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