root/content/test/image_decoder_test.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ShouldSkipFile
  2. ReadFileToVector
  3. GetMD5SumPath
  4. SaveMD5Sum
  5. VerifyImage
  6. SetUp
  7. GetImageFiles
  8. ShouldImageFail
  9. TestDecoding
  10. TestWebKitImageDecoder

// 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 "base/strings/utf_string_conversions.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"

// Uncomment this to recalculate the MD5 sums; see header comments.
// #define CALCULATE_MD5_SUMS

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->front(), 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 = base::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 actual_digest))
      << 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;
  for (base::FilePath next_file_name = enumerator.Next();
       !next_file_name.empty(); next_file_name = enumerator.Next()) {
    base::FilePath base_name = next_file_name.BaseName();
#if defined(OS_WIN)
    std::string base_name_ascii = base::UTF16ToASCII(base_name.value());
#else
    std::string base_name_ascii = base_name.value();
#endif
    if (MatchPattern(base_name_ascii, pattern))
      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))
      TestWebKitImageDecoder(*i, GetMD5SumPath(*i), kFirstFrameIndex);
  }
}

void ImageDecoderTest::TestWebKitImageDecoder(
    const base::FilePath& image_path,
    const base::FilePath& md5_sum_path,
    int desired_frame_index) const {
#if defined(CALCULATE_MD5_SUMS)
  // If we're just calculating the MD5 sums, skip failing images quickly.
  if (ShouldImageFail(image_path))
    return;
#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 !defined(CALCULATE_MD5_SUMS)
  // 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();
#endif

  // 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 (ShouldImageFail(image_path)) {
    EXPECT_FALSE(decoder->isFrameCompleteAtIndex(kFirstFrameIndex));
    EXPECT_TRUE(decoder->isFailed());
  } else {
    EXPECT_FALSE(decoder->isFailed()) << image_path.value();
#if defined(CALCULATE_MD5_SUMS)
    SaveMD5Sum(md5_sum_path, decoder->getFrameAtIndex(desired_frame_index));
#else
    VerifyImage(*decoder, image_path, md5_sum_path, desired_frame_index);
#endif
  }
}

/* [<][>][^][v][top][bottom][index][help] */