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