// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "content/test/image_decoder_test.h" #include "base/file_util.h" #include "base/files/file_enumerator.h" #include "base/files/file_path.h" #include "base/md5.h" #include "base/memory/scoped_ptr.h" #include "base/path_service.h" #include "base/strings/string_util.h" #include "third_party/WebKit/public/platform/WebData.h" #include "third_party/WebKit/public/platform/WebImage.h" #include "third_party/WebKit/public/platform/WebSize.h" #include "third_party/WebKit/public/web/WebImageDecoder.h" using base::Time; namespace { const int kFirstFrameIndex = 0; // Determine if we should test with file specified by |path| based // on |file_selection| and the |threshold| for the file size. bool ShouldSkipFile(const base::FilePath& path, ImageDecoderTestFileSelection file_selection, const int64 threshold) { if (file_selection == TEST_ALL) return false; int64 image_size = 0; base::GetFileSize(path, &image_size); return (file_selection == TEST_SMALLER) == (image_size > threshold); } } // namespace void ReadFileToVector(const base::FilePath& path, std::vector<char>* contents) { std::string raw_image_data; base::ReadFileToString(path, &raw_image_data); contents->resize(raw_image_data.size()); memcpy(&contents->at(0), raw_image_data.data(), raw_image_data.size()); } base::FilePath GetMD5SumPath(const base::FilePath& path) { static const base::FilePath::StringType kDecodedDataExtension( FILE_PATH_LITERAL(".md5sum")); return base::FilePath(path.value() + kDecodedDataExtension); } #if defined(CALCULATE_MD5_SUMS) void SaveMD5Sum(const base::FilePath& path, const blink::WebImage& web_image) { // Calculate MD5 sum. base::MD5Digest digest; web_image.getSkBitmap().lockPixels(); base::MD5Sum(web_image.getSkBitmap().getPixels(), web_image.getSkBitmap().width() * web_image.getSkBitmap().height() * sizeof(uint32_t), &digest); // Write sum to disk. int bytes_written = file_util::WriteFile(path, reinterpret_cast<const char*>(&digest), sizeof digest); ASSERT_EQ(sizeof digest, bytes_written); web_image.getSkBitmap().unlockPixels(); } #endif #if !defined(CALCULATE_MD5_SUMS) void VerifyImage(const blink::WebImageDecoder& decoder, const base::FilePath& path, const base::FilePath& md5_sum_path, size_t frame_index) { // Make sure decoding can complete successfully. EXPECT_TRUE(decoder.isSizeAvailable()) << path.value(); EXPECT_GE(decoder.frameCount(), frame_index) << path.value(); EXPECT_TRUE(decoder.isFrameCompleteAtIndex(frame_index)) << path.value(); EXPECT_FALSE(decoder.isFailed()); // Calculate MD5 sum. base::MD5Digest actual_digest; blink::WebImage web_image = decoder.getFrameAtIndex(frame_index); web_image.getSkBitmap().lockPixels(); base::MD5Sum(web_image.getSkBitmap().getPixels(), web_image.getSkBitmap().width() * web_image.getSkBitmap().height() * sizeof(uint32_t), &actual_digest); // Read the MD5 sum off disk. std::string file_bytes; base::ReadFileToString(md5_sum_path, &file_bytes); base::MD5Digest expected_digest; ASSERT_EQ(sizeof expected_digest, file_bytes.size()) << path.value(); memcpy(&expected_digest, file_bytes.data(), sizeof expected_digest); // Verify that the sums are the same. EXPECT_EQ(0, memcmp(&expected_digest, &actual_digest, sizeof(base::MD5Digest))) << path.value(); web_image.getSkBitmap().unlockPixels(); } #endif void ImageDecoderTest::SetUp() { base::FilePath data_dir; ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &data_dir)); data_dir_ = data_dir.AppendASCII("webkit"). AppendASCII("data"). AppendASCII(format_ + "_decoder"); if (!base::PathExists(data_dir_)) { const testing::TestInfo* const test_info = testing::UnitTest::GetInstance()->current_test_info(); VLOG(0) << test_info->name() << " not running because test data wasn't found."; data_dir_.clear(); return; } } std::vector<base::FilePath> ImageDecoderTest::GetImageFiles() const { std::string pattern = "*." + format_; base::FileEnumerator enumerator(data_dir_, false, base::FileEnumerator::FILES); std::vector<base::FilePath> image_files; base::FilePath next_file_name; while (!(next_file_name = enumerator.Next()).empty()) { base::FilePath base_name = next_file_name.BaseName(); #if defined(OS_WIN) std::string base_name_ascii = WideToASCII(base_name.value()); #else std::string base_name_ascii = base_name.value(); #endif if (!MatchPattern(base_name_ascii, pattern)) continue; image_files.push_back(next_file_name); } return image_files; } bool ImageDecoderTest::ShouldImageFail(const base::FilePath& path) const { const base::FilePath::StringType kBadSuffix(FILE_PATH_LITERAL(".bad.")); return (path.value().length() > (kBadSuffix.length() + format_.length()) && !path.value().compare(path.value().length() - format_.length() - kBadSuffix.length(), kBadSuffix.length(), kBadSuffix)); } void ImageDecoderTest::TestDecoding( ImageDecoderTestFileSelection file_selection, const int64 threshold) { if (data_dir_.empty()) return; const std::vector<base::FilePath> image_files(GetImageFiles()); for (std::vector<base::FilePath>::const_iterator i = image_files.begin(); i != image_files.end(); ++i) { if (ShouldSkipFile(*i, file_selection, threshold)) continue; const base::FilePath md5_sum_path(GetMD5SumPath(*i)); TestWebKitImageDecoder(*i, md5_sum_path, kFirstFrameIndex); } } void ImageDecoderTest::TestWebKitImageDecoder(const base::FilePath& image_path, const base::FilePath& md5_sum_path, int desired_frame_index) const { bool should_test_chunking = true; bool should_test_failed_images = true; #ifdef CALCULATE_MD5_SUMS // Do not test anything just get the md5 sums. should_test_chunking = false; should_test_failed_images = false; #endif std::vector<char> image_contents; ReadFileToVector(image_path, &image_contents); EXPECT_TRUE(image_contents.size()); scoped_ptr<blink::WebImageDecoder> decoder(CreateWebKitImageDecoder()); EXPECT_FALSE(decoder->isFailed()); if (should_test_chunking) { // Test chunking file into half. const int partial_size = image_contents.size()/2; blink::WebData partial_data( reinterpret_cast<const char*>(&(image_contents.at(0))), partial_size); // Make Sure the image decoder doesn't fail when we ask for the frame // buffer for this partial image. // NOTE: We can't check that frame 0 is non-NULL, because if this is an // ICO and we haven't yet supplied enough data to read the directory, // there is no framecount and thus no first frame. decoder->setData(const_cast<blink::WebData&>(partial_data), false); EXPECT_FALSE(decoder->isFailed()) << image_path.value(); } // Make sure passing the complete image results in successful decoding. blink::WebData data(reinterpret_cast<const char*>(&(image_contents.at(0))), image_contents.size()); decoder->setData(const_cast<blink::WebData&>(data), true); if (should_test_failed_images) { if (ShouldImageFail(image_path)) { EXPECT_FALSE(decoder->isFrameCompleteAtIndex(kFirstFrameIndex)); EXPECT_TRUE(decoder->isFailed()); return; } } EXPECT_FALSE(decoder->isFailed()) << image_path.value(); #ifdef CALCULATE_MD5_SUMS // Since WebImage does not expose get data by frame, get the size // through decoder and pass it to fromData so that the closest // image dats to the size is returned. blink::WebSize size(decoder->getImage(desired_frame_index).size()); const blink::WebImage& image = blink::WebImage::fromData(data, size); SaveMD5Sum(md5_sum_path, image); #else VerifyImage(*decoder, image_path, md5_sum_path, desired_frame_index); #endif }