Add integration tests for resized user avatars

Even on solid color images the resizing can cause some small artifacts
that slightly modify the color of certain pixels. Due to this now the
color comparison is no longer strict but fuzzy.

Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
This commit is contained in:
Daniel Calviño Sánchez 2020-12-04 04:28:51 +01:00
parent 2cc22a06b4
commit 1552add4ca
2 changed files with 88 additions and 7 deletions

View File

@ -133,3 +133,35 @@ Feature: avatar
| X-NC-IsCustomAvatar | 0 | | X-NC-IsCustomAvatar | 0 |
And last avatar is a square of size 128 And last avatar is a square of size 128
And last avatar is not a single color And last avatar is not a single color
Scenario: get user avatar with a larger size than the original one
Given Logging in using web as "user0"
And logged in user posts temporary avatar from file "data/coloured-pattern.png"
And logged in user crops temporary avatar
| x | 384 |
| y | 256 |
| w | 128 |
| h | 128 |
When user "user0" gets avatar for user "user0" with size "192"
Then The following headers should be set
| Content-Type | image/png |
| X-NC-IsCustomAvatar | 1 |
And last avatar is a square of size 192
And last avatar is a single "#FF0000" color
Scenario: get user avatar with a smaller size than the original one
Given Logging in using web as "user0"
And logged in user posts temporary avatar from file "data/coloured-pattern.png"
And logged in user crops temporary avatar
| x | 384 |
| y | 256 |
| w | 128 |
| h | 128 |
When user "user0" gets avatar for user "user0" with size "96"
Then The following headers should be set
| Content-Type | image/png |
| X-NC-IsCustomAvatar | 1 |
And last avatar is a square of size 96
And last avatar is a single "#FF0000" color

View File

@ -179,19 +179,50 @@ trait Avatar {
* @param string $size * @param string $size
*/ */
public function lastAvatarIsASingleColor(string $color) { public function lastAvatarIsASingleColor(string $color) {
Assert::assertEquals($color, $this->getColorFromLastAvatar()); $expectedColor = $this->hexStringToRgbColor($color);
$colorFromLastAvatar = $this->getColorFromLastAvatar();
Assert::assertTrue($this->isSameColor($expectedColor, $colorFromLastAvatar),
$this->rgbColorToHexString($colorFromLastAvatar) . ' does not match expected ' . $color);
}
private function hexStringToRgbColor($hexString) {
// Strip initial "#"
$hexString = substr($hexString, 1);
$rgbColorInt = hexdec($hexString);
// RGBA hex strings are not supported; the given string is assumed to be
// an RGB hex string.
return [
'red' => ($rgbColorInt >> 16) & 0xFF,
'green' => ($rgbColorInt >> 8) & 0xFF,
'blue' => $rgbColorInt & 0xFF,
'alpha' => 0
];
}
private function rgbColorToHexString($rgbColor) {
$rgbColorInt = ($rgbColor['red'] << 16) + ($rgbColor['green'] << 8) + ($rgbColor['blue']);
return '#' . str_pad(strtoupper(dechex($rgbColorInt)), 6, '0', STR_PAD_LEFT);
} }
private function getColorFromLastAvatar() { private function getColorFromLastAvatar() {
$image = imagecreatefromstring($this->lastAvatar); $image = imagecreatefromstring($this->lastAvatar);
$firstPixelColor = imagecolorat($image, 0, 0); $firstPixelColorIndex = imagecolorat($image, 0, 0);
$firstPixelColor = imagecolorsforindex($image, $firstPixelColorIndex);
for ($i = 0; $i < imagesx($image); $i++) { for ($i = 0; $i < imagesx($image); $i++) {
for ($j = 0; $j < imagesx($image); $j++) { for ($j = 0; $j < imagesx($image); $j++) {
$currentPixelColor = imagecolorat($image, $i, $j); $currentPixelColorIndex = imagecolorat($image, $i, $j);
$currentPixelColor = imagecolorsforindex($image, $currentPixelColorIndex);
if ($firstPixelColor !== $currentPixelColor) { // The colors are compared with a small allowed delta, as even
// on solid color images the resizing can cause some small
// artifacts that slightly modify the color of certain pixels.
if (!$this->isSameColor($firstPixelColor, $currentPixelColor)) {
imagedestroy($image); imagedestroy($image);
return null; return null;
@ -201,8 +232,26 @@ trait Avatar {
imagedestroy($image); imagedestroy($image);
// Assume that the image is a truecolor image and thus the index is the return $firstPixelColor;
// RGB value of the pixel as an integer. }
return '#' . str_pad(strtoupper(dechex($firstPixelColor)), 6, '0', STR_PAD_LEFT);
private function isSameColor(array $firstColor, array $secondColor, int $allowedDelta = 1) {
if ($this->isSameColorComponent($firstColor['red'], $secondColor['red'], $allowedDelta) &&
$this->isSameColorComponent($firstColor['green'], $secondColor['green'], $allowedDelta) &&
$this->isSameColorComponent($firstColor['blue'], $secondColor['blue'], $allowedDelta) &&
$this->isSameColorComponent($firstColor['alpha'], $secondColor['alpha'], $allowedDelta)) {
return true;
}
return false;
}
private function isSameColorComponent(int $firstColorComponent, int $secondColorComponent, int $allowedDelta) {
if ($firstColorComponent >= ($secondColorComponent - $allowedDelta) &&
$firstColorComponent <= ($secondColorComponent + $allowedDelta)) {
return true;
}
return false;
} }
} }