Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/image/test/gtest/TestMetadata.cpp
Line
Count
Source (jump to first uncovered line)
1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
 * License, v. 2.0. If a copy of the MPL was not distributed with this
3
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5
#include "gtest/gtest.h"
6
7
#include "Common.h"
8
#include "Decoder.h"
9
#include "DecoderFactory.h"
10
#include "decoders/nsBMPDecoder.h"
11
#include "IDecodingTask.h"
12
#include "imgIContainer.h"
13
#include "imgITools.h"
14
#include "ImageFactory.h"
15
#include "mozilla/gfx/2D.h"
16
#include "nsComponentManagerUtils.h"
17
#include "nsCOMPtr.h"
18
#include "nsIInputStream.h"
19
#include "nsIRunnable.h"
20
#include "nsIThread.h"
21
#include "mozilla/RefPtr.h"
22
#include "nsStreamUtils.h"
23
#include "nsString.h"
24
#include "nsThreadUtils.h"
25
#include "ProgressTracker.h"
26
#include "SourceBuffer.h"
27
28
using namespace mozilla;
29
using namespace mozilla::gfx;
30
using namespace mozilla::image;
31
32
enum class BMPWithinICO
33
{
34
  NO,
35
  YES
36
};
37
38
static void
39
CheckMetadata(const ImageTestCase& aTestCase,
40
              BMPWithinICO aBMPWithinICO = BMPWithinICO::NO)
41
0
{
42
0
  nsCOMPtr<nsIInputStream> inputStream = LoadFile(aTestCase.mPath);
43
0
  ASSERT_TRUE(inputStream != nullptr);
44
0
45
0
  // Figure out how much data we have.
46
0
  uint64_t length;
47
0
  nsresult rv = inputStream->Available(&length);
48
0
  ASSERT_TRUE(NS_SUCCEEDED(rv));
49
0
50
0
  // Write the data into a SourceBuffer.
51
0
  auto sourceBuffer = MakeNotNull<RefPtr<SourceBuffer>>();
52
0
  sourceBuffer->ExpectLength(length);
53
0
  rv = sourceBuffer->AppendFromInputStream(inputStream, length);
54
0
  ASSERT_TRUE(NS_SUCCEEDED(rv));
55
0
  sourceBuffer->Complete(NS_OK);
56
0
57
0
  // Create a metadata decoder.
58
0
  DecoderType decoderType =
59
0
    DecoderFactory::GetDecoderType(aTestCase.mMimeType);
60
0
  RefPtr<Decoder> decoder =
61
0
    DecoderFactory::CreateAnonymousMetadataDecoder(decoderType, sourceBuffer);
62
0
  ASSERT_TRUE(decoder != nullptr);
63
0
  RefPtr<IDecodingTask> task = new AnonymousDecodingTask(WrapNotNull(decoder));
64
0
65
0
  if (aBMPWithinICO == BMPWithinICO::YES) {
66
0
    static_cast<nsBMPDecoder*>(decoder.get())->SetIsWithinICO();
67
0
  }
68
0
69
0
  // Run the metadata decoder synchronously.
70
0
  task->Run();
71
0
72
0
  // Ensure that the metadata decoder didn't make progress it shouldn't have
73
0
  // (which would indicate that it decoded past the header of the image).
74
0
  Progress metadataProgress = decoder->TakeProgress();
75
0
  EXPECT_TRUE(0 == (metadataProgress & ~(FLAG_SIZE_AVAILABLE |
76
0
                                         FLAG_HAS_TRANSPARENCY |
77
0
                                         FLAG_IS_ANIMATED)));
78
0
79
0
  // If the test case is corrupt, assert what we can and return early.
80
0
  if (aTestCase.mFlags & TEST_CASE_HAS_ERROR) {
81
0
    EXPECT_TRUE(decoder->GetDecodeDone());
82
0
    EXPECT_TRUE(decoder->HasError());
83
0
    return;
84
0
  }
85
0
86
0
  EXPECT_TRUE(decoder->GetDecodeDone() && !decoder->HasError());
87
0
88
0
  // Check that we got the expected metadata.
89
0
  EXPECT_TRUE(metadataProgress & FLAG_SIZE_AVAILABLE);
90
0
91
0
  IntSize metadataSize = decoder->Size();
92
0
  EXPECT_EQ(aTestCase.mSize.width, metadataSize.width);
93
0
  if (aBMPWithinICO == BMPWithinICO::YES) {
94
0
    // Half the data is considered to be part of the AND mask if embedded
95
0
    EXPECT_EQ(aTestCase.mSize.height / 2, metadataSize.height);
96
0
  } else {
97
0
    EXPECT_EQ(aTestCase.mSize.height, metadataSize.height);
98
0
  }
99
0
100
0
  bool expectTransparency = aBMPWithinICO == BMPWithinICO::YES
101
0
                          ? true
102
0
                          : bool(aTestCase.mFlags & TEST_CASE_IS_TRANSPARENT);
103
0
  EXPECT_EQ(expectTransparency, bool(metadataProgress & FLAG_HAS_TRANSPARENCY));
104
0
105
0
  EXPECT_EQ(bool(aTestCase.mFlags & TEST_CASE_IS_ANIMATED),
106
0
            bool(metadataProgress & FLAG_IS_ANIMATED));
107
0
108
0
  // Create a full decoder, so we can compare the result.
109
0
  decoder =
110
0
    DecoderFactory::CreateAnonymousDecoder(decoderType, sourceBuffer, Nothing(),
111
0
                                           DecoderFlags::FIRST_FRAME_ONLY,
112
0
                                           DefaultSurfaceFlags());
113
0
  ASSERT_TRUE(decoder != nullptr);
114
0
  task = new AnonymousDecodingTask(WrapNotNull(decoder));
115
0
116
0
  if (aBMPWithinICO == BMPWithinICO::YES) {
117
0
    static_cast<nsBMPDecoder*>(decoder.get())->SetIsWithinICO();
118
0
  }
119
0
120
0
  // Run the full decoder synchronously.
121
0
  task->Run();
122
0
123
0
  EXPECT_TRUE(decoder->GetDecodeDone() && !decoder->HasError());
124
0
  Progress fullProgress = decoder->TakeProgress();
125
0
126
0
  // If the metadata decoder set a progress bit, the full decoder should also
127
0
  // have set the same bit.
128
0
  EXPECT_EQ(fullProgress, metadataProgress | fullProgress);
129
0
130
0
  // The full decoder and the metadata decoder should agree on the image's size.
131
0
  IntSize fullSize = decoder->Size();
132
0
  EXPECT_EQ(metadataSize.width, fullSize.width);
133
0
  EXPECT_EQ(metadataSize.height, fullSize.height);
134
0
135
0
  // We should not discover transparency during the full decode that we didn't
136
0
  // discover during the metadata decode, unless the image is animated.
137
0
  EXPECT_TRUE(!(fullProgress & FLAG_HAS_TRANSPARENCY) ||
138
0
              (metadataProgress & FLAG_HAS_TRANSPARENCY) ||
139
0
              (fullProgress & FLAG_IS_ANIMATED));
140
0
}
141
142
class ImageDecoderMetadata : public ::testing::Test
143
{
144
protected:
145
  AutoInitializeImageLib mInit;
146
};
147
148
0
TEST_F(ImageDecoderMetadata, PNG) { CheckMetadata(GreenPNGTestCase()); }
149
0
TEST_F(ImageDecoderMetadata, TransparentPNG) { CheckMetadata(TransparentPNGTestCase()); }
150
0
TEST_F(ImageDecoderMetadata, GIF) { CheckMetadata(GreenGIFTestCase()); }
151
0
TEST_F(ImageDecoderMetadata, TransparentGIF) { CheckMetadata(TransparentGIFTestCase()); }
152
0
TEST_F(ImageDecoderMetadata, JPG) { CheckMetadata(GreenJPGTestCase()); }
153
0
TEST_F(ImageDecoderMetadata, BMP) { CheckMetadata(GreenBMPTestCase()); }
154
0
TEST_F(ImageDecoderMetadata, ICO) { CheckMetadata(GreenICOTestCase()); }
155
0
TEST_F(ImageDecoderMetadata, Icon) { CheckMetadata(GreenIconTestCase()); }
156
157
TEST_F(ImageDecoderMetadata, AnimatedGIF)
158
0
{
159
0
  CheckMetadata(GreenFirstFrameAnimatedGIFTestCase());
160
0
}
161
162
TEST_F(ImageDecoderMetadata, AnimatedPNG)
163
0
{
164
0
  CheckMetadata(GreenFirstFrameAnimatedPNGTestCase());
165
0
}
166
167
TEST_F(ImageDecoderMetadata, FirstFramePaddingGIF)
168
0
{
169
0
  CheckMetadata(FirstFramePaddingGIFTestCase());
170
0
}
171
172
TEST_F(ImageDecoderMetadata, TransparentIfWithinICOBMPNotWithinICO)
173
0
{
174
0
  CheckMetadata(TransparentIfWithinICOBMPTestCase(TEST_CASE_DEFAULT_FLAGS),
175
0
                BMPWithinICO::NO);
176
0
}
177
178
TEST_F(ImageDecoderMetadata, TransparentIfWithinICOBMPWithinICO)
179
0
{
180
0
  CheckMetadata(TransparentIfWithinICOBMPTestCase(TEST_CASE_IS_TRANSPARENT),
181
0
                BMPWithinICO::YES);
182
0
}
183
184
0
TEST_F(ImageDecoderMetadata, RLE4BMP) { CheckMetadata(RLE4BMPTestCase()); }
185
0
TEST_F(ImageDecoderMetadata, RLE8BMP) { CheckMetadata(RLE8BMPTestCase()); }
186
187
0
TEST_F(ImageDecoderMetadata, Corrupt) { CheckMetadata(CorruptTestCase()); }
188
189
TEST_F(ImageDecoderMetadata, NoFrameDelayGIF)
190
0
{
191
0
  CheckMetadata(NoFrameDelayGIFTestCase());
192
0
}
193
194
TEST_F(ImageDecoderMetadata, NoFrameDelayGIFFullDecode)
195
0
{
196
0
  ImageTestCase testCase = NoFrameDelayGIFTestCase();
197
0
198
0
  // The previous test (NoFrameDelayGIF) verifies that we *don't* detect that
199
0
  // this test case is animated, because it has a zero frame delay for the first
200
0
  // frame. This test verifies that when we do a full decode, we detect the
201
0
  // animation at that point and successfully decode all the frames.
202
0
203
0
  // Create an image.
204
0
  RefPtr<Image> image =
205
0
    ImageFactory::CreateAnonymousImage(nsDependentCString(testCase.mMimeType));
206
0
  ASSERT_TRUE(!image->HasError());
207
0
208
0
  nsCOMPtr<nsIInputStream> inputStream = LoadFile(testCase.mPath);
209
0
  ASSERT_TRUE(inputStream != nullptr);
210
0
211
0
  // Figure out how much data we have.
212
0
  uint64_t length;
213
0
  nsresult rv = inputStream->Available(&length);
214
0
  ASSERT_TRUE(NS_SUCCEEDED(rv));
215
0
216
0
  // Write the data into the image.
217
0
  rv = image->OnImageDataAvailable(nullptr, nullptr, inputStream, 0,
218
0
                                   static_cast<uint32_t>(length));
219
0
  ASSERT_TRUE(NS_SUCCEEDED(rv));
220
0
221
0
  // Let the image know we've sent all the data.
222
0
  rv = image->OnImageDataComplete(nullptr, nullptr, NS_OK, true);
223
0
  ASSERT_TRUE(NS_SUCCEEDED(rv));
224
0
225
0
  RefPtr<ProgressTracker> tracker = image->GetProgressTracker();
226
0
  tracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE);
227
0
228
0
  // Use GetFrame() to force a sync decode of the image.
229
0
  RefPtr<SourceSurface> surface =
230
0
    image->GetFrame(imgIContainer::FRAME_CURRENT,
231
0
                    imgIContainer::FLAG_SYNC_DECODE);
232
0
233
0
  // Ensure that the image's metadata meets our expectations.
234
0
  IntSize imageSize(0, 0);
235
0
  rv = image->GetWidth(&imageSize.width);
236
0
  EXPECT_TRUE(NS_SUCCEEDED(rv));
237
0
  rv = image->GetHeight(&imageSize.height);
238
0
  EXPECT_TRUE(NS_SUCCEEDED(rv));
239
0
240
0
  EXPECT_EQ(testCase.mSize.width, imageSize.width);
241
0
  EXPECT_EQ(testCase.mSize.height, imageSize.height);
242
0
243
0
  Progress imageProgress = tracker->GetProgress();
244
0
245
0
  EXPECT_TRUE(bool(imageProgress & FLAG_HAS_TRANSPARENCY) == false);
246
0
  EXPECT_TRUE(bool(imageProgress & FLAG_IS_ANIMATED) == true);
247
0
248
0
  // Ensure that we decoded both frames of the image.
249
0
  LookupResult result =
250
0
    SurfaceCache::Lookup(ImageKey(image.get()),
251
0
                         RasterSurfaceKey(imageSize,
252
0
                                          DefaultSurfaceFlags(),
253
0
                                          PlaybackType::eAnimated));
254
0
  ASSERT_EQ(MatchType::EXACT, result.Type());
255
0
256
0
  EXPECT_TRUE(NS_SUCCEEDED(result.Surface().Seek(0)));
257
0
  EXPECT_TRUE(bool(result.Surface()));
258
0
259
0
  RawAccessFrameRef partialFrame = result.Surface().RawAccessRef(1);
260
0
  EXPECT_TRUE(bool(partialFrame));
261
0
}