diff options
Diffstat (limited to 'image/test/gtest/Common.h')
-rw-r--r-- | image/test/gtest/Common.h | 419 |
1 files changed, 419 insertions, 0 deletions
diff --git a/image/test/gtest/Common.h b/image/test/gtest/Common.h new file mode 100644 index 0000000000..79bed9fc1b --- /dev/null +++ b/image/test/gtest/Common.h @@ -0,0 +1,419 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_image_test_gtest_Common_h +#define mozilla_image_test_gtest_Common_h + +#include <vector> + +#include "gtest/gtest.h" + +#include "mozilla/Maybe.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/gfx/2D.h" +#include "Decoder.h" +#include "gfxColor.h" +#include "imgITools.h" +#include "nsCOMPtr.h" +#include "SurfacePipe.h" +#include "SurfacePipeFactory.h" + +class nsIInputStream; + +namespace mozilla { +namespace image { + +/////////////////////////////////////////////////////////////////////////////// +// Types +/////////////////////////////////////////////////////////////////////////////// + +enum TestCaseFlags +{ + TEST_CASE_DEFAULT_FLAGS = 0, + TEST_CASE_IS_FUZZY = 1 << 0, + TEST_CASE_HAS_ERROR = 1 << 1, + TEST_CASE_IS_TRANSPARENT = 1 << 2, + TEST_CASE_IS_ANIMATED = 1 << 3, + TEST_CASE_IGNORE_OUTPUT = 1 << 4, +}; + +struct ImageTestCase +{ + ImageTestCase(const char* aPath, + const char* aMimeType, + gfx::IntSize aSize, + uint32_t aFlags = TEST_CASE_DEFAULT_FLAGS) + : mPath(aPath) + , mMimeType(aMimeType) + , mSize(aSize) + , mOutputSize(aSize) + , mFlags(aFlags) + { } + + ImageTestCase(const char* aPath, + const char* aMimeType, + gfx::IntSize aSize, + gfx::IntSize aOutputSize, + uint32_t aFlags = TEST_CASE_DEFAULT_FLAGS) + : mPath(aPath) + , mMimeType(aMimeType) + , mSize(aSize) + , mOutputSize(aOutputSize) + , mFlags(aFlags) + { } + + const char* mPath; + const char* mMimeType; + gfx::IntSize mSize; + gfx::IntSize mOutputSize; + uint32_t mFlags; +}; + +struct BGRAColor +{ + BGRAColor() : BGRAColor(0, 0, 0, 0) { } + + BGRAColor(uint8_t aBlue, uint8_t aGreen, uint8_t aRed, uint8_t aAlpha) + : mBlue(aBlue) + , mGreen(aGreen) + , mRed(aRed) + , mAlpha(aAlpha) + { } + + static BGRAColor Green() { return BGRAColor(0x00, 0xFF, 0x00, 0xFF); } + static BGRAColor Red() { return BGRAColor(0x00, 0x00, 0xFF, 0xFF); } + static BGRAColor Blue() { return BGRAColor(0xFF, 0x00, 0x00, 0xFF); } + static BGRAColor Transparent() { return BGRAColor(0x00, 0x00, 0x00, 0x00); } + + uint32_t AsPixel() const { return gfxPackedPixel(mAlpha, mRed, mGreen, mBlue); } + + uint8_t mBlue; + uint8_t mGreen; + uint8_t mRed; + uint8_t mAlpha; +}; + + +/////////////////////////////////////////////////////////////////////////////// +// General Helpers +/////////////////////////////////////////////////////////////////////////////// + +/** + * A RAII class that ensure that ImageLib services are available. Any tests that + * require ImageLib to be initialized (for example, any test that uses the + * SurfaceCache; see image::EnsureModuleInitialized() for the full list) can + * use this class to ensure that ImageLib services are available. Failure to do + * so can result in strange, non-deterministic failures. + */ +struct AutoInitializeImageLib +{ + AutoInitializeImageLib() + { + // Ensure that ImageLib services are initialized. + nsCOMPtr<imgITools> imgTools = do_CreateInstance("@mozilla.org/image/tools;1"); + EXPECT_TRUE(imgTools != nullptr); + } +}; + +/// Loads a file from the current directory. @return an nsIInputStream for it. +already_AddRefed<nsIInputStream> LoadFile(const char* aRelativePath); + +/** + * @returns true if every pixel of @aSurface is @aColor. + * + * If @aFuzz is nonzero, a tolerance of @aFuzz is allowed in each color + * component. This may be necessary for tests that involve JPEG images or + * downscaling. + */ +bool IsSolidColor(gfx::SourceSurface* aSurface, + BGRAColor aColor, + uint8_t aFuzz = 0); + +/** + * @returns true if every pixel of @aDecoder's surface has the palette index + * specified by @aColor. + */ +bool IsSolidPalettedColor(Decoder* aDecoder, uint8_t aColor); + +/** + * @returns true if every pixel in the range of rows specified by @aStartRow and + * @aRowCount of @aSurface is @aColor. + * + * If @aFuzz is nonzero, a tolerance of @aFuzz is allowed in each color + * component. This may be necessary for tests that involve JPEG images or + * downscaling. + */ +bool RowsAreSolidColor(gfx::SourceSurface* aSurface, + int32_t aStartRow, + int32_t aRowCount, + BGRAColor aColor, + uint8_t aFuzz = 0); + +/** + * @returns true if every pixel in the range of rows specified by @aStartRow and + * @aRowCount of @aDecoder's surface has the palette index specified by @aColor. + */ +bool PalettedRowsAreSolidColor(Decoder* aDecoder, + int32_t aStartRow, + int32_t aRowCount, + uint8_t aColor); + +/** + * @returns true if every pixel in the rect specified by @aRect is @aColor. + * + * If @aFuzz is nonzero, a tolerance of @aFuzz is allowed in each color + * component. This may be necessary for tests that involve JPEG images or + * downscaling. + */ +bool RectIsSolidColor(gfx::SourceSurface* aSurface, + const gfx::IntRect& aRect, + BGRAColor aColor, + uint8_t aFuzz = 0); + +/** + * @returns true if every pixel in the rect specified by @aRect has the palette + * index specified by @aColor. + */ +bool PalettedRectIsSolidColor(Decoder* aDecoder, + const gfx::IntRect& aRect, + uint8_t aColor); + +/** + * @returns true if the pixels in @aRow of @aSurface match the pixels given in + * @aPixels. + */ +bool RowHasPixels(gfx::SourceSurface* aSurface, + int32_t aRow, + const std::vector<BGRAColor>& aPixels); + +// ExpectNoResume is an IResumable implementation for use by tests that expect +// Resume() to never get called. +class ExpectNoResume final : public IResumable +{ +public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ExpectNoResume, override) + + void Resume() override { FAIL() << "Resume() should not get called"; } + +private: + ~ExpectNoResume() override { } +}; + +// CountResumes is an IResumable implementation for use by tests that expect +// Resume() to get called a certain number of times. +class CountResumes : public IResumable +{ +public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CountResumes, override) + + CountResumes() : mCount(0) { } + + void Resume() override { mCount++; } + uint32_t Count() const { return mCount; } + +private: + ~CountResumes() override { } + + uint32_t mCount; +}; + + +/////////////////////////////////////////////////////////////////////////////// +// SurfacePipe Helpers +/////////////////////////////////////////////////////////////////////////////// + +/** + * Creates a decoder with no data associated with, suitable for testing code + * that requires a decoder to initialize or to allocate surfaces but doesn't + * actually need the decoder to do any decoding. + * + * XXX(seth): We only need this because SurfaceSink and PalettedSurfaceSink + * defer to the decoder for surface allocation. Once all decoders use + * SurfacePipe we won't need to do that anymore and we can remove this function. + */ +already_AddRefed<Decoder> CreateTrivialDecoder(); + +/** + * Creates a pipeline of SurfaceFilters from a list of Config structs and passes + * it to the provided lambda @aFunc. Assertions that the pipeline is constructly + * correctly and cleanup of any allocated surfaces is handled automatically. + * + * @param aDecoder The decoder to use for allocating surfaces. + * @param aFunc The lambda function to pass the filter pipeline to. + * @param aConfigs The configuration for the pipeline. + */ +template <typename Func, typename... Configs> +void WithFilterPipeline(Decoder* aDecoder, Func aFunc, Configs... aConfigs) +{ + auto pipe = MakeUnique<typename detail::FilterPipeline<Configs...>::Type>(); + nsresult rv = pipe->Configure(aConfigs...); + ASSERT_TRUE(NS_SUCCEEDED(rv)); + + aFunc(aDecoder, pipe.get()); + + RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef(); + if (currentFrame) { + currentFrame->Finish(); + } +} + +/** + * Creates a pipeline of SurfaceFilters from a list of Config structs and + * asserts that configuring it fails. Cleanup of any allocated surfaces is + * handled automatically. + * + * @param aDecoder The decoder to use for allocating surfaces. + * @param aConfigs The configuration for the pipeline. + */ +template <typename... Configs> +void AssertConfiguringPipelineFails(Decoder* aDecoder, Configs... aConfigs) +{ + auto pipe = MakeUnique<typename detail::FilterPipeline<Configs...>::Type>(); + nsresult rv = pipe->Configure(aConfigs...); + + // Callers expect configuring the pipeline to fail. + ASSERT_TRUE(NS_FAILED(rv)); + + RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef(); + if (currentFrame) { + currentFrame->Finish(); + } +} + +/** + * Asserts that the provided filter pipeline is in the correct final state, + * which is to say, the entire surface has been written to (IsSurfaceFinished() + * returns true) and the invalid rects are as expected. + * + * @param aFilter The filter pipeline to check. + * @param aInputSpaceRect The expect invalid rect, in input space. + * @param aoutputSpaceRect The expect invalid rect, in output space. + */ +void AssertCorrectPipelineFinalState(SurfaceFilter* aFilter, + const gfx::IntRect& aInputSpaceRect, + const gfx::IntRect& aOutputSpaceRect); + +/** + * Checks a generated image for correctness. Reports any unexpected deviation + * from the expected image as GTest failures. + * + * @param aDecoder The decoder which contains the image. The decoder's current + * frame will be checked. + * @param aRect The region in the space of the output surface that the filter + * pipeline will actually write to. It's expected that pixels in + * this region are green, while pixels outside this region are + * transparent. + * @param aFuzz The amount of fuzz to use in pixel comparisons. + */ +void CheckGeneratedImage(Decoder* aDecoder, + const gfx::IntRect& aRect, + uint8_t aFuzz = 0); + +/** + * Checks a generated paletted image for correctness. Reports any unexpected + * deviation from the expected image as GTest failures. + * + * @param aDecoder The decoder which contains the image. The decoder's current + * frame will be checked. + * @param aRect The region in the space of the output surface that the filter + * pipeline will actually write to. It's expected that pixels in + * this region have a palette index of 255, while pixels outside + * this region have a palette index of 0. + */ +void CheckGeneratedPalettedImage(Decoder* aDecoder, const gfx::IntRect& aRect); + +/** + * Tests the result of calling WritePixels() using the provided SurfaceFilter + * pipeline. The pipeline must be a normal (i.e., non-paletted) pipeline. + * + * The arguments are specified in the an order intended to minimize the number + * of arguments that most test cases need to pass. + * + * @param aDecoder The decoder whose current frame will be written to. + * @param aFilter The SurfaceFilter pipeline to use. + * @param aOutputRect The region in the space of the output surface that will be + * invalidated by the filter pipeline. Defaults to + * (0, 0, 100, 100). + * @param aInputRect The region in the space of the input image that will be + * invalidated by the filter pipeline. Defaults to + * (0, 0, 100, 100). + * @param aInputWriteRect The region in the space of the input image that the + * filter pipeline will allow writes to. Note the + * difference from @aInputRect: @aInputRect is the actual + * region invalidated, while @aInputWriteRect is the + * region that is written to. These can differ in cases + * where the input is not clipped to the size of the image. + * Defaults to the entire input rect. + * @param aOutputWriteRect The region in the space of the output surface that + * the filter pipeline will actually write to. It's + * expected that pixels in this region are green, while + * pixels outside this region are transparent. Defaults + * to the entire output rect. + */ +void CheckWritePixels(Decoder* aDecoder, + SurfaceFilter* aFilter, + Maybe<gfx::IntRect> aOutputRect = Nothing(), + Maybe<gfx::IntRect> aInputRect = Nothing(), + Maybe<gfx::IntRect> aInputWriteRect = Nothing(), + Maybe<gfx::IntRect> aOutputWriteRect = Nothing(), + uint8_t aFuzz = 0); + +/** + * Tests the result of calling WritePixels() using the provided SurfaceFilter + * pipeline. The pipeline must be a paletted pipeline. + * @see CheckWritePixels() for documentation of the arguments. + */ +void CheckPalettedWritePixels(Decoder* aDecoder, + SurfaceFilter* aFilter, + Maybe<gfx::IntRect> aOutputRect = Nothing(), + Maybe<gfx::IntRect> aInputRect = Nothing(), + Maybe<gfx::IntRect> aInputWriteRect = Nothing(), + Maybe<gfx::IntRect> aOutputWriteRect = Nothing(), + uint8_t aFuzz = 0); + + +/////////////////////////////////////////////////////////////////////////////// +// Test Data +/////////////////////////////////////////////////////////////////////////////// + +ImageTestCase GreenPNGTestCase(); +ImageTestCase GreenGIFTestCase(); +ImageTestCase GreenJPGTestCase(); +ImageTestCase GreenBMPTestCase(); +ImageTestCase GreenICOTestCase(); +ImageTestCase GreenIconTestCase(); + +ImageTestCase GreenFirstFrameAnimatedGIFTestCase(); +ImageTestCase GreenFirstFrameAnimatedPNGTestCase(); + +ImageTestCase CorruptTestCase(); +ImageTestCase CorruptBMPWithTruncatedHeader(); +ImageTestCase CorruptICOWithBadBMPWidthTestCase(); +ImageTestCase CorruptICOWithBadBMPHeightTestCase(); + +ImageTestCase TransparentPNGTestCase(); +ImageTestCase TransparentGIFTestCase(); +ImageTestCase FirstFramePaddingGIFTestCase(); +ImageTestCase NoFrameDelayGIFTestCase(); +ImageTestCase ExtraImageSubBlocksAnimatedGIFTestCase(); + +ImageTestCase TransparentBMPWhenBMPAlphaEnabledTestCase(); +ImageTestCase RLE4BMPTestCase(); +ImageTestCase RLE8BMPTestCase(); + +ImageTestCase DownscaledPNGTestCase(); +ImageTestCase DownscaledGIFTestCase(); +ImageTestCase DownscaledJPGTestCase(); +ImageTestCase DownscaledBMPTestCase(); +ImageTestCase DownscaledICOTestCase(); +ImageTestCase DownscaledIconTestCase(); +ImageTestCase DownscaledTransparentICOWithANDMaskTestCase(); + +ImageTestCase TruncatedSmallGIFTestCase(); + +} // namespace image +} // namespace mozilla + +#endif // mozilla_image_test_gtest_Common_h |