<?php
/*
* RCMCardDAV - CardDAV plugin for Roundcube webmail
*
* Copyright (C) 2011-2022 Benjamin Schieder <rcmcarddav@wegwerf.anderdonau.de>,
* Michael Stilkerich <ms@mike2k.de>
*
* This file is part of RCMCardDAV.
*
* RCMCardDAV is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* RCMCardDAV is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RCMCardDAV. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace MStilkerich\Tests\RCMCardDAV\Unit;
use PHasher;
use PHPUnit\Framework\TestCase;
use Sabre\VObject\Component\VCard;
use MStilkerich\RCMCardDAV\DataConversion;
use MStilkerich\RCMCardDAV\DelayedPhotoLoader;
use MStilkerich\Tests\RCMCardDAV\TestInfrastructure;
/**
* @psalm-import-type SaveData from DataConversion
*/
class Utils
{
/**
* Can be used as emulation of WebDavResource::downloadResource in stubs.
* @return array{body: string}
*/
public static function downloadResource(string $uri): array
{
if (preg_match(',^http://localhost/(.+),', $uri, $matches) && isset($matches[1])) {
$filename = "tests/Unit/data/srv/$matches[1]";
TestCase::assertFileIsReadable($filename);
return [ 'body' => file_get_contents($filename) ];
}
throw new \Exception("URI $uri not known to stub");
}
/**
* @return SaveData
*/
public static function readSaveDataFromJson(string $jsonFile): array
{
/** @var SaveData Assume our test samples are valid */
$phpArray = TestInfrastructure::readJsonArray($jsonFile);
// special handling for photo as we cannot encode binary data in json
if (isset($phpArray['photo'])) {
TestCase::assertIsString($phpArray['photo']);
$photoFile = $phpArray['photo'];
if (!empty($photoFile) && $photoFile[0] == '@') {
$photoFile = substr($photoFile, 1);
$phpArray['photo'] = TestInfrastructure::readFileRelative($photoFile, $jsonFile);
}
}
/** @psalm-var SaveData $phpArray XXX temporary workaround because of vimeo/psalm#8980 */
return $phpArray;
}
/**
* Compares two roundcube save data arrays with special handling of photo.
*/
public static function compareSaveData(array $saveDataExp, array $saveDataRc, string $msg): void
{
TestCase::assertSame(isset($saveDataExp['photo']), isset($saveDataRc['photo']), $msg);
if (isset($saveDataExp['photo'])) {
TestCase::assertIsString($saveDataExp["photo"]);
// Check that save data contains a pristine photo loader
TestCase::assertInstanceOf(DelayedPhotoLoader::class, $saveDataRc["photo"]);
TestCase::assertTrue($saveDataRc["photo"]->pristine(), "Photo loader not pristine");
self::comparePhoto($saveDataExp['photo'], (string) $saveDataRc['photo']);
unset($saveDataExp['photo']);
unset($saveDataRc['photo']);
}
if (isset($saveDataRc["vcard"])) {
TestCase::assertIsString($saveDataRc["vcard"]);
unset($saveDataRc["vcard"]);
}
if (isset($saveDataRc["_carddav_vcard"])) {
TestCase::assertInstanceOf(VCard::class, $saveDataRc["_carddav_vcard"]);
unset($saveDataRc["_carddav_vcard"]);
}
ksort($saveDataExp);
ksort($saveDataRc);
TestCase::assertSame($saveDataExp, $saveDataRc, $msg);
}
/**
* Compares two photos given as binary strings.
*
* @param string $pExpStr The expected photo data
* @param string $pRcStr The photo data produced by the test object
*/
public static function comparePhoto(string $pExpStr, string $pRcStr): void
{
TestCase::assertTrue(function_exists('gd_info'), "php-gd required");
// shortcut that also covers URI - if identical strings, save the comparison
if (
empty($pExpStr) || empty($pRcStr) ||
strpos($pExpStr, "http") !== false || strpos($pExpStr, "data:") !== false
) {
TestCase::assertSame($pExpStr, $pRcStr, "PHOTO comparison on URI value failed");
return;
}
// dimensions must be the same
/** @psalm-var false|array{int,int,int} */
$pExp = getimagesizefromstring($pExpStr);
TestCase::assertNotFalse($pExp, "Exp Image could not be identified");
/** @psalm-var false|array{int,int,int} */
$pRc = getimagesizefromstring($pRcStr);
TestCase::assertNotFalse($pRc, "RC Image could not be identified");
TestCase::assertSame($pExp[0], $pRc[0], "X dimension of PHOTO differs");
TestCase::assertSame($pExp[1], $pRc[1], "Y dimension of PHOTO differs");
TestCase::assertSame($pExp[2], $pRc[2], "Image type of PHOTO differs");
// store to temporary files for comparison
$expFile = tempnam("testreports", "imgcomp_") . image_type_to_extension($pExp[2]);
$rcFile = tempnam("testreports", "imgcomp_") . image_type_to_extension($pRc[2]);
TestCase::assertNotFalse(file_put_contents($expFile, $pExpStr), "Cannot write $expFile");
TestCase::assertNotFalse(file_put_contents($rcFile, $pRcStr), "Cannot write $rcFile");
// compare
/** @psalm-var PHasher $phasher */
$phasher = PHasher::Instance();
// similarity is returned as percentage
$compResult = intval($phasher->Compare($expFile, $rcFile));
TestCase::assertSame(100, $compResult, "Image comparison returned too little similarity $compResult%");
}
/**
* Delete temporary files from image comparison
*/
public static function cleanupTempImages(): void
{
$tmpimgs = glob("testreports/imgcomp_*");
if (!empty($tmpimgs)) {
foreach ($tmpimgs as $tmpimg) {
unlink($tmpimg);
}
}
}
}
// vim: ts=4:sw=4:expandtab:fenc=utf8:ff=unix:tw=120:ft=php