// Copyright 2008, Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "config.h" #include #include "base/file_util.h" #include "base/md5.h" #include "base/path_service.h" #include "base/scoped_handle.h" #include "base/scoped_ptr.h" #include "base/time.h" #include "webkit/tools/test_shell/image_decoder_unittest.h" void ReadFileToVector(const std::wstring& path, Vector* contents) { std::string contents_str; file_util::ReadFileToString(path, &contents_str); contents->resize(contents_str.size()); memcpy(&contents->first(), contents_str.data(), contents_str.size()); } std::wstring GetMD5SumPath(const std::wstring& path) { static const std::wstring kDecodedDataExtension(L".md5sum"); return path + kDecodedDataExtension; } #ifdef CALCULATE_MD5_SUMS void SaveMD5Sum(const std::wstring& path, WebCore::RGBA32Buffer* buffer) { // Create the file to write. ScopedHandle handle(CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)); ASSERT_TRUE(handle.IsValid()); // Calculate MD5 sum. MD5Digest digest; SkAutoLockPixels bmp_lock(buffer->bitmap()); MD5Sum(buffer->bitmap().getPixels(), buffer->rect().width() * buffer->rect().height() * sizeof(unsigned), &digest); // Write sum to disk. DWORD bytes_written; ASSERT_TRUE(WriteFile(handle, &digest, sizeof digest, &bytes_written, NULL)); ASSERT_EQ(sizeof digest, bytes_written); } #else void VerifyImage(WebCore::ImageDecoder* decoder, const std::wstring& path, const std::wstring& md5_sum_path) { // Make sure decoding can complete successfully. EXPECT_TRUE(decoder->isSizeAvailable()) << path; WebCore::RGBA32Buffer* image_buffer = decoder->frameBufferAtIndex(0); ASSERT_NE(static_cast(NULL), image_buffer) << path; EXPECT_EQ(WebCore::RGBA32Buffer::FrameComplete, image_buffer->status()) << path; EXPECT_FALSE(decoder->failed()) << path; // Calculate MD5 sum. MD5Digest actual_digest; SkAutoLockPixels bmp_lock(image_buffer->bitmap()); MD5Sum(image_buffer->bitmap().getPixels(), image_buffer->rect().width() * image_buffer->rect().height() * sizeof(unsigned), &actual_digest); // Read the MD5 sum off disk. std::string file_bytes; file_util::ReadFileToString(md5_sum_path, &file_bytes); MD5Digest expected_digest; ASSERT_EQ(sizeof expected_digest, file_bytes.size()) << path; 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(MD5Digest))) << path; } #endif void ImageDecoderTest::SetUp() { ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &data_dir_)); file_util::AppendToPath(&data_dir_, L"webkit"); file_util::AppendToPath(&data_dir_, L"data"); file_util::AppendToPath(&data_dir_, format_ + L"_decoder"); ASSERT_TRUE(file_util::PathExists(data_dir_)); } std::vector ImageDecoderTest::GetImageFiles() const { std::wstring find_string(data_dir_); file_util::AppendToPath(&find_string, L"*." + format_); WIN32_FIND_DATA find_data; ScopedFindFileHandle handle(FindFirstFile(find_string.c_str(), &find_data)); EXPECT_TRUE(handle.IsValid()); std::vector image_files; do { std::wstring image_path = data_dir_; file_util::AppendToPath(&image_path, find_data.cFileName); image_files.push_back(image_path); } while (FindNextFile(handle, &find_data)); return image_files; } bool ImageDecoderTest::ShouldImageFail(const std::wstring& path) const { static const std::wstring kBadSuffix(L".bad."); return (path.length() > (kBadSuffix.length() + format_.length()) && !path.compare(path.length() - format_.length() - kBadSuffix.length(), kBadSuffix.length(), kBadSuffix)); } void ImageDecoderTest::TestDecoding() const { const std::vector image_files(GetImageFiles()); for (std::vector::const_iterator i(image_files.begin()); i != image_files.end(); ++i) { Vector image_contents; ReadFileToVector(*i, &image_contents); scoped_ptr decoder(CreateDecoder()); RefPtr shared_contents(new WebCore::SharedBuffer); shared_contents->append(image_contents.data(), static_cast(image_contents.size())); decoder->setData(shared_contents.get(), true); if (ShouldImageFail(*i)) { // We should always get a non-NULL frame buffer, but when the decoder // tries to produce it, it should fail, and the frame buffer shouldn't // complete. WebCore::RGBA32Buffer* const image_buffer = decoder->frameBufferAtIndex(0); ASSERT_NE(static_cast(NULL), image_buffer) << (*i); EXPECT_NE(image_buffer->status(), WebCore::RGBA32Buffer::FrameComplete) << (*i); EXPECT_TRUE(decoder->failed()) << (*i); continue; } #ifdef CALCULATE_MD5_SUMS SaveMD5Sum(GetMD5SumPath(*i), decoder->frameBufferAtIndex(0)); #else VerifyImage(decoder.get(), *i, GetMD5SumPath(*i)); #endif } } #ifndef CALCULATE_MD5_SUMS void ImageDecoderTest::TestChunkedDecoding() const { // Init random number generator with current day, so a failing case will fail // consistently over the course of a whole day. const Time today = Time::Now().LocalMidnight(); srand(static_cast(today.ToInternalValue())); const std::vector image_files(GetImageFiles()); for (std::vector::const_iterator i(image_files.begin()); i != image_files.end(); ++i) { if (ShouldImageFail(*i)) continue; // Read the file and split it at an arbitrary point. Vector image_contents; ReadFileToVector(*i, &image_contents); const int partial_size = static_cast( (static_cast(rand()) / RAND_MAX) * image_contents.size()); RefPtr partial_contents(new WebCore::SharedBuffer); partial_contents->append(image_contents.data(), partial_size); // Make sure the image decoder doesn't fail when we ask for the frame buffer // for this partial image. scoped_ptr decoder(CreateDecoder()); decoder->setData(partial_contents.get(), false); EXPECT_NE(static_cast(NULL), decoder->frameBufferAtIndex(0)) << (*i); EXPECT_FALSE(decoder->failed()) << (*i); // Make sure passing the complete image results in successful decoding. partial_contents->append( &image_contents.data()[partial_size], static_cast(image_contents.size() - partial_size)); decoder->setData(partial_contents.get(), true); VerifyImage(decoder.get(), *i, GetMD5SumPath(*i)); } } #endif