6

I am developing a PHP software that creates thumbnails from images.

Now I need to make sure that thumbnails are created successfully, in other words that the initial image has been resized/cropped correctly.

I think there's only one way to do this: I manually create the thumbnail to compare with the thumbnail created by the software.

But how to test?

If I use assertFileEquals() to compare the thumbnail created by me and the one created by the software, of course the test fails, even if the two images are identical.

I imagine that happening if only because the creation date of the two files is different, or for similar reasons.

So, how to do?

Mirko Pagliai
  • 1,220
  • 1
  • 19
  • 36

2 Answers2

4

Storing a pair of source.png and expected_result.png (generated once by the software, verified as good and stored as the reference image) will suffice. Implementing a comparison function seems to be an overhead.

The main purpose of the unit tests is to signalize if system behavior changes, and that's what such test going to do if newly created thumbnail won't match with the reference one.

Yet, if for whichever reason software generates slightly different images every time, then, in case it's not a bug, use the suggested compare similar images approach.

What if image contents differ

In case of PNG files used in this example their contents might contain some auxiliary info such as EXIF.

So you might have to try creating a copy image without this additional info. Please verify if the following code works for you:

public function testThumbnails()
{
    $this->assertPngImageContentsEquals(__DIR__ . '/test1.png', __DIR__ . '/test2.png');
}

public static function assertPngImageContentsEquals(
    $expected,
    $actual,
    $message = 'Contents of PNG files differ'
)
{
    self::assertFileExists($expected, $message);
    self::assertFileExists($actual, $message);

    $copy_expected = self::_makePngCopy($expected, __DIR__ . '/expected.png');
    $copy_actual = self::_makePngCopy($actual, __DIR__ . '/actual.png');

    var_dump($copy_expected);
    var_dump($copy_actual);

    self::assertFileEquals($copy_expected, $copy_actual, 'Thumbnails differ');

    unlink($copy_expected);
    unlink($copy_actual);
}

private static function _makePngCopy($sourceFile, $resultFile)
{
    $image = imagecreatefrompng($sourceFile);
    imagepng($image, $resultFile);
    imagedestroy($image);
    return $resultFile;
}
Community
  • 1
  • 1
BVengerov
  • 2,947
  • 1
  • 18
  • 32
  • I did just as you suggested: I copied the thumbnail elsewhere, I recreated the thumbnail using the same parameters and I took the test. Maybe a bug? – Mirko Pagliai Oct 06 '16 at 12:27
  • @MirkoPagliai That's peculiar! Well, the binary strings do differ. Which format are the thumbnails in? Maybe it's some auxiliary info that is written to the file by the software. Maybe you can share the images as well? – BVengerov Oct 06 '16 at 13:15
  • 1
    Ok, I understand that I have to investigate further my software, there is something that escapes me.. Thumbs are in png. – Mirko Pagliai Oct 06 '16 at 13:20
  • I think that maybe there is a bug. I did two tests. First test: create an image with an editor (e.g. Gimp), copy and paste the file, run the comparison. SUCCESS! Second test: create an image, save a first copy save a second copy, run the comparison. FAILURE! Wtf?! – Mirko Pagliai Oct 06 '16 at 14:49
  • @MirkoPagliai Can't repro with Paint.NET, separately saved copies are the same to me and assertion passes. The creation/modification timestamp and a bunch of other stuff from the file info (provided by `stat`) differs, so that's clearly a signal we should focus on the file contents. – BVengerov Oct 06 '16 at 15:16
  • @MirkoPagliai I'm no expert on images, but it seems that the difference might be in the EXIF data. Apparently [it can be stored in PNG files in mysterious ways](http://stackoverflow.com/questions/9542359/does-png-contain-exif-data-like-jpg) – BVengerov Oct 06 '16 at 15:24
  • Ok, now I try with jpg images – Mirko Pagliai Oct 06 '16 at 15:38
  • Nothing to do. Locally it works with jpeg files, but on Travis [it fails](https://travis-ci.org/mirko-pagliai/cakephp-thumber/jobs/165774974) – Mirko Pagliai Oct 07 '16 at 11:19
  • It's a CakePHP plugin for creating thumbs using [Intervention Image](http://image.intervention.io). Code is [here](https://github.com/mirko-pagliai/cakephp-thumber). I also tried to set the same version of Imagick in all tests. – Mirko Pagliai Oct 07 '16 at 11:49
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/125171/discussion-between-bvengerov-and-mirko-pagliai). – BVengerov Oct 07 '16 at 11:51
3

If assertFileEquals is failing, then there is something different between the two files. The internal code invokes file_get_contents on both files and asserts true if there are zero differences (so creation date is not part of the assertion).

Since you are manually creating the thumbnail, there must be slight differences. Instead you would need to do a "mostly the same" comparison, there are two questions related to coding this:

Then decide how much difference is considered a pass. So you would do the "mostly the same comparison", then use asserts to determine if the answer of the "mostly the same" comparison falls within a range you can accept.

Update

I ran a quick test to ensure that assertFileEquals works properly on a binary file:

class FileEqualsTest extends PHPUnit_Framework_TestCase {

public function test_yes_no_answer() {

    file_put_contents("a.txt","\e[0m");
    file_put_contents("b.txt","\e[0m");
    file_put_contents("c.txt","\e[30m");

    // straight get contents and comparisons
    $contentsA = file_get_contents("a.txt");
    $contentsB = file_get_contents("b.txt");
    $this->assertEquals($contentsA, $contentsB);

    $contentsC = file_get_contents("c.txt");
    $this->assertNotEquals($contentsA, $contentsC);

    // using file equals has same answer
    $this->assertFileEquals("a.txt","b.txt");
    $this->assertFileNotEquals("a.txt","c.txt");

}

..and it worked as expected on a very small scale. So it would seem there is some tiny difference in some way. You could try the options shown in the other questions above to see if there is a tiny difference, if that is important to your testing.

Community
  • 1
  • 1
Katie
  • 2,594
  • 3
  • 23
  • 31
  • I have tried so too: I created a thumbnail, I have checked it (visually), so I did it again to create the same thumbnail and I did compare the two. It fails, however, it is strange. See [here](http://pastebin.com/QaFkBHa7). The strings seem equal, maybe a bug? – Mirko Pagliai Oct 06 '16 at 12:23
  • Interesting, it might have to do with it being a binary string...but it would seem the two binary strings would compare correctly. – Katie Oct 06 '16 at 12:26
  • That is the output of assertFileEquals(). I do not know PHPUnit well enough, but it seems normal to compare the files it turns them into binary strings – Mirko Pagliai Oct 06 '16 at 12:30
  • Thanks @Katie, the important thing here was to make sure that `assertFileEquals()` compares only the contents of files (and not even the date of the last creation / updating, for example) and there were no bugs for PHPUnit. Thanks! – Mirko Pagliai Oct 06 '16 at 13:22
  • @MirkoPagliai - Your Welcome! – Katie Oct 06 '16 at 13:24
  • 1
    if PHPUnit works right, then it means that my method returns 2 different results with the same arguments... and this is not what I want! PHPUnit should works just that, so in the end all this is good :-) – Mirko Pagliai Oct 06 '16 at 13:28
  • [Just another alternative solution in addition to the mentioned above](http://compareimages.nikhazy-dizajn.hu/) – BVengerov Oct 06 '16 at 13:29
  • @Katie & BVengerov I think that maybe there is a bug. I did two tests. First test: create an image with an editor (e.g. Gimp), copy and paste the file, run the comparison. SUCCESS! Second test: create an image, save a first copy save a second copy, run the comparison. FAILURE! Wtf?! – Mirko Pagliai Oct 06 '16 at 14:49
  • @MirkoPagliai - weird...I do know that binary strings are tough to work with. For example, when I was doing that short binary file test, when I opened a.txt and typed \e[0m, it wasn't actually binary, PHPStorm turned it into the string with the slash, versus ASCII 27, which is actually the escape character, in order to get the actual ASCII 27 into the file, I had to do the code you see above, where I use put_contents with the escape character. Maybe that is an avenue you can pursue... – Katie Oct 06 '16 at 15:54