UserAvatarTest.php 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. <?php
  2. /**
  3. * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
  4. * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
  5. * SPDX-License-Identifier: AGPL-3.0-or-later
  6. */
  7. namespace Test\Avatar;
  8. use OC\Files\SimpleFS\SimpleFolder;
  9. use OC\User\User;
  10. use OCP\Files\File;
  11. use OCP\Files\NotFoundException;
  12. use OCP\Files\SimpleFS\ISimpleFile;
  13. use OCP\IConfig;
  14. use OCP\IL10N;
  15. use OCP\Image;
  16. use Psr\Log\LoggerInterface;
  17. class UserAvatarTest extends \Test\TestCase {
  18. /** @var SimpleFolder | \PHPUnit\Framework\MockObject\MockObject */
  19. private $folder;
  20. /** @var \OC\Avatar\UserAvatar */
  21. private $avatar;
  22. /** @var \OC\User\User | \PHPUnit\Framework\MockObject\MockObject $user */
  23. private $user;
  24. /** @var \OCP\IConfig|\PHPUnit\Framework\MockObject\MockObject */
  25. private $config;
  26. protected function setUp(): void {
  27. parent::setUp();
  28. $this->folder = $this->createMock(SimpleFolder::class);
  29. // abcdefghi is a convenient name that our algorithm convert to our nextcloud blue 0082c9
  30. $this->user = $this->getUserWithDisplayName('abcdefghi');
  31. $this->config = $this->createMock(IConfig::class);
  32. $this->avatar = $this->getUserAvatar($this->user);
  33. }
  34. public function avatarTextData() {
  35. return [
  36. ['', '?'],
  37. ['matchish', 'M'],
  38. ['Firstname Lastname', 'FL'],
  39. ['Firstname Lastname Rest', 'FL'],
  40. ];
  41. }
  42. public function testGetNoAvatar(): void {
  43. $file = $this->createMock(ISimpleFile::class);
  44. $this->folder->method('newFile')
  45. ->willReturn($file);
  46. $this->folder->method('getFile')
  47. ->willReturnCallback(function (string $path) {
  48. if ($path === 'avatar.64.png') {
  49. throw new NotFoundException();
  50. }
  51. });
  52. $this->folder->method('fileExists')
  53. ->willReturnCallback(function ($path) {
  54. if ($path === 'generated') {
  55. return true;
  56. }
  57. return false;
  58. });
  59. $data = null;
  60. $file->method('putContent')
  61. ->with($this->callback(function ($d) use (&$data) {
  62. $data = $d;
  63. return true;
  64. }));
  65. $file->method('getContent')
  66. ->willReturnCallback(function () use (&$data) {
  67. return $data;
  68. });
  69. $result = $this->avatar->get();
  70. $this->assertTrue($result->valid());
  71. }
  72. public function testGetAvatarSizeMatch(): void {
  73. $this->folder->method('fileExists')
  74. ->willReturnMap([
  75. ['avatar.jpg', true],
  76. ['avatar.128.jpg', true],
  77. ['generated', false],
  78. ]);
  79. $expected = new Image();
  80. $expected->loadFromFile(\OC::$SERVERROOT . '/tests/data/testavatar.png');
  81. $file = $this->createMock(ISimpleFile::class);
  82. $file->method('getContent')->willReturn($expected->data());
  83. $this->folder->method('getFile')->with('avatar.128.jpg')->willReturn($file);
  84. $this->assertEquals($expected->data(), $this->avatar->get(128)->data());
  85. }
  86. public function testGetAvatarSizeMinusOne(): void {
  87. $this->folder->method('fileExists')
  88. ->willReturnMap([
  89. ['avatar.jpg', true],
  90. ['generated', false],
  91. ]);
  92. $expected = new Image();
  93. $expected->loadFromFile(\OC::$SERVERROOT . '/tests/data/testavatar.png');
  94. $file = $this->createMock(ISimpleFile::class);
  95. $file->method('getContent')->willReturn($expected->data());
  96. $this->folder->method('getFile')->with('avatar.jpg')->willReturn($file);
  97. $this->assertEquals($expected->data(), $this->avatar->get(-1)->data());
  98. }
  99. public function testGetAvatarNoSizeMatch(): void {
  100. $this->folder->method('fileExists')
  101. ->willReturnMap([
  102. ['avatar.jpg', false],
  103. ['avatar.png', true],
  104. ['avatar.32.png', false],
  105. ['generated', false],
  106. ]);
  107. $expected = new Image();
  108. $expected->loadFromFile(\OC::$SERVERROOT . '/tests/data/testavatar.png');
  109. $expected2 = new Image();
  110. $expected2->loadFromFile(\OC::$SERVERROOT . '/tests/data/testavatar.png');
  111. $expected2->resize(32);
  112. $file = $this->createMock(ISimpleFile::class);
  113. $file->method('getContent')->willReturn($expected->data());
  114. $this->folder->method('getFile')
  115. ->willReturnCallback(
  116. function ($path) use ($file) {
  117. if ($path === 'avatar.png') {
  118. return $file;
  119. } else {
  120. throw new \OCP\Files\NotFoundException;
  121. }
  122. }
  123. );
  124. $newFile = $this->createMock(ISimpleFile::class);
  125. $newFile->expects($this->once())
  126. ->method('putContent')
  127. ->with($expected2->data());
  128. $newFile->expects($this->once())
  129. ->method('getContent')
  130. ->willReturn($expected2->data());
  131. $this->folder->expects($this->once())
  132. ->method('newFile')
  133. ->with('avatar.32.png')
  134. ->willReturn($newFile);
  135. $this->assertEquals($expected2->data(), $this->avatar->get(32)->data());
  136. }
  137. public function testExistsNo(): void {
  138. $this->assertFalse($this->avatar->exists());
  139. }
  140. public function testExiststJPG(): void {
  141. $this->folder->method('fileExists')
  142. ->willReturnMap([
  143. ['avatar.jpg', true],
  144. ['avatar.png', false],
  145. ]);
  146. $this->assertTrue($this->avatar->exists());
  147. }
  148. public function testExistsPNG(): void {
  149. $this->folder->method('fileExists')
  150. ->willReturnMap([
  151. ['avatar.jpg', false],
  152. ['avatar.png', true],
  153. ]);
  154. $this->assertTrue($this->avatar->exists());
  155. }
  156. public function testSetAvatar(): void {
  157. $avatarFileJPG = $this->createMock(File::class);
  158. $avatarFileJPG->method('getName')
  159. ->willReturn('avatar.jpg');
  160. $avatarFileJPG->expects($this->once())->method('delete');
  161. $avatarFilePNG = $this->createMock(File::class);
  162. $avatarFilePNG->method('getName')
  163. ->willReturn('avatar.png');
  164. $avatarFilePNG->expects($this->once())->method('delete');
  165. $resizedAvatarFile = $this->createMock(File::class);
  166. $resizedAvatarFile->method('getName')
  167. ->willReturn('avatar.32.jpg');
  168. $resizedAvatarFile->expects($this->once())->method('delete');
  169. $this->folder->method('getDirectoryListing')
  170. ->willReturn([$avatarFileJPG, $avatarFilePNG, $resizedAvatarFile]);
  171. $generated = $this->createMock(ISimpleFile::class);
  172. $this->folder->method('getFile')
  173. ->with('generated')
  174. ->willReturn($generated);
  175. $newFile = $this->createMock(ISimpleFile::class);
  176. $this->folder->expects($this->once())
  177. ->method('newFile')
  178. ->with('avatar.png')
  179. ->willReturn($newFile);
  180. $image = new Image();
  181. $image->loadFromFile(\OC::$SERVERROOT . '/tests/data/testavatar.png');
  182. $newFile->expects($this->once())
  183. ->method('putContent')
  184. ->with($image->data());
  185. $this->config->expects($this->exactly(3))
  186. ->method('setUserValue');
  187. $this->config->expects($this->once())
  188. ->method('getUserValue');
  189. $this->user->expects($this->exactly(1))->method('triggerChange');
  190. $this->avatar->set($image->data());
  191. }
  192. public function testGenerateSvgAvatar(): void {
  193. $avatar = $this->invokePrivate($this->avatar, 'getAvatarVector', [64, false]);
  194. $svg = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>
  195. <svg width="64" height="64" version="1.1" viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg">
  196. <rect width="100%" height="100%" fill="#e5f2f9"></rect>
  197. <text x="50%" y="350" style="font-weight:normal;font-size:280px;font-family:\'Noto Sans\';text-anchor:middle;fill:#0082c9">A</text>
  198. </svg>';
  199. $this->assertEquals($avatar, $svg);
  200. }
  201. /**
  202. * @dataProvider avatarTextData
  203. */
  204. public function testGetAvatarText($displayName, $expectedAvatarText): void {
  205. $user = $this->getUserWithDisplayName($displayName);
  206. $avatar = $this->getUserAvatar($user);
  207. $avatarText = $this->invokePrivate($avatar, 'getAvatarText');
  208. $this->assertEquals($expectedAvatarText, $avatarText);
  209. }
  210. public function testHashToInt(): void {
  211. $hashToInt = $this->invokePrivate($this->avatar, 'hashToInt', ['abcdef', 18]);
  212. $this->assertTrue(gettype($hashToInt) === 'integer');
  213. }
  214. public function testMixPalette(): void {
  215. $colorFrom = new \OCP\Color(0, 0, 0);
  216. $colorTo = new \OCP\Color(6, 12, 18);
  217. $steps = 6;
  218. $palette = \OCP\Color::mixPalette($steps, $colorFrom, $colorTo);
  219. foreach ($palette as $j => $color) {
  220. // calc increment
  221. $incR = $colorTo->red() / $steps * $j;
  222. $incG = $colorTo->green() / $steps * $j;
  223. $incB = $colorTo->blue() / $steps * $j;
  224. // ensure everything is equal
  225. $this->assertEquals($color, new \OCP\Color($incR, $incG, $incB));
  226. }
  227. $hashToInt = $this->invokePrivate($this->avatar, 'hashToInt', ['abcdef', 18]);
  228. $this->assertTrue(gettype($hashToInt) === 'integer');
  229. }
  230. private function getUserWithDisplayName($name) {
  231. $user = $this->createMock(User::class);
  232. $user->method('getDisplayName')->willReturn($name);
  233. return $user;
  234. }
  235. private function getUserAvatar($user) {
  236. /** @var \OCP\IL10N | \PHPUnit\Framework\MockObject\MockObject $l */
  237. $l = $this->createMock(IL10N::class);
  238. $l->method('t')->willReturnArgument(0);
  239. return new \OC\Avatar\UserAvatar(
  240. $this->folder,
  241. $l,
  242. $user,
  243. $this->createMock(LoggerInterface::class),
  244. $this->config
  245. );
  246. }
  247. }