diff options
Diffstat (limited to 'tests/lib/AppFramework/Utility')
3 files changed, 563 insertions, 0 deletions
diff --git a/tests/lib/AppFramework/Utility/ControllerMethodReflectorTest.php b/tests/lib/AppFramework/Utility/ControllerMethodReflectorTest.php new file mode 100644 index 00000000000..00ae4792824 --- /dev/null +++ b/tests/lib/AppFramework/Utility/ControllerMethodReflectorTest.php @@ -0,0 +1,253 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace Test\AppFramework\Utility; + +use OC\AppFramework\Utility\ControllerMethodReflector; + +class BaseController { + /** + * @Annotation + */ + public function test() { + } + + /** + * @Annotation + */ + public function test2() { + } + + /** + * @Annotation + */ + public function test3() { + } +} + +class MiddleController extends BaseController { + /** + * @NoAnnotation + */ + public function test2() { + } + + public function test3() { + } + + /** + * @psalm-param int<-4, 42> $rangedOne + * @psalm-param int<min, max> $rangedTwo + * @psalm-param int<1, 6>|null $rangedThree + * @psalm-param ?int<-70, -30> $rangedFour + * @return void + */ + public function test4(int $rangedOne, int $rangedTwo, ?int $rangedThree, ?int $rangedFour) { + } +} + +class EndController extends MiddleController { +} + +class ControllerMethodReflectorTest extends \Test\TestCase { + /** + * @Annotation + */ + public function testReadAnnotation(): void { + $reader = new ControllerMethodReflector(); + $reader->reflect( + '\Test\AppFramework\Utility\ControllerMethodReflectorTest', + 'testReadAnnotation' + ); + + $this->assertTrue($reader->hasAnnotation('Annotation')); + } + + /** + * @Annotation(parameter=value) + */ + public function testGetAnnotationParameterSingle(): void { + $reader = new ControllerMethodReflector(); + $reader->reflect( + self::class, + __FUNCTION__ + ); + + $this->assertSame('value', $reader->getAnnotationParameter('Annotation', 'parameter')); + } + + /** + * @Annotation(parameter1=value1, parameter2=value2,parameter3=value3) + */ + public function testGetAnnotationParameterMultiple(): void { + $reader = new ControllerMethodReflector(); + $reader->reflect( + self::class, + __FUNCTION__ + ); + + $this->assertSame('value1', $reader->getAnnotationParameter('Annotation', 'parameter1')); + $this->assertSame('value2', $reader->getAnnotationParameter('Annotation', 'parameter2')); + $this->assertSame('value3', $reader->getAnnotationParameter('Annotation', 'parameter3')); + } + + /** + * @Annotation + * @param test + */ + public function testReadAnnotationNoLowercase(): void { + $reader = new ControllerMethodReflector(); + $reader->reflect( + '\Test\AppFramework\Utility\ControllerMethodReflectorTest', + 'testReadAnnotationNoLowercase' + ); + + $this->assertTrue($reader->hasAnnotation('Annotation')); + $this->assertFalse($reader->hasAnnotation('param')); + } + + + /** + * @Annotation + * @param int $test + */ + public function testReadTypeIntAnnotations(): void { + $reader = new ControllerMethodReflector(); + $reader->reflect( + '\Test\AppFramework\Utility\ControllerMethodReflectorTest', + 'testReadTypeIntAnnotations' + ); + + $this->assertEquals('int', $reader->getType('test')); + } + + /** + * @Annotation + * @param int $a + * @param int $b + */ + public function arguments3($a, float $b, int $c, $d) { + } + + /** + * @requires PHP 7 + */ + public function testReadTypeIntAnnotationsScalarTypes(): void { + $reader = new ControllerMethodReflector(); + $reader->reflect( + '\Test\AppFramework\Utility\ControllerMethodReflectorTest', + 'arguments3' + ); + + $this->assertEquals('int', $reader->getType('a')); + $this->assertEquals('float', $reader->getType('b')); + $this->assertEquals('int', $reader->getType('c')); + $this->assertNull($reader->getType('d')); + } + + + /** + * @Annotation + * @param double $test something special + */ + public function testReadTypeDoubleAnnotations(): void { + $reader = new ControllerMethodReflector(); + $reader->reflect( + '\Test\AppFramework\Utility\ControllerMethodReflectorTest', + 'testReadTypeDoubleAnnotations' + ); + + $this->assertEquals('double', $reader->getType('test')); + } + + /** + * @Annotation + * @param string $foo + */ + public function testReadTypeWhitespaceAnnotations(): void { + $reader = new ControllerMethodReflector(); + $reader->reflect( + '\Test\AppFramework\Utility\ControllerMethodReflectorTest', + 'testReadTypeWhitespaceAnnotations' + ); + + $this->assertEquals('string', $reader->getType('foo')); + } + + + public function arguments($arg, $arg2 = 'hi') { + } + public function testReflectParameters(): void { + $reader = new ControllerMethodReflector(); + $reader->reflect( + '\Test\AppFramework\Utility\ControllerMethodReflectorTest', + 'arguments' + ); + + $this->assertEquals(['arg' => null, 'arg2' => 'hi'], $reader->getParameters()); + } + + + public function arguments2($arg) { + } + public function testReflectParameters2(): void { + $reader = new ControllerMethodReflector(); + $reader->reflect( + '\Test\AppFramework\Utility\ControllerMethodReflectorTest', + 'arguments2' + ); + + $this->assertEquals(['arg' => null], $reader->getParameters()); + } + + + public function testInheritance(): void { + $reader = new ControllerMethodReflector(); + $reader->reflect('Test\AppFramework\Utility\EndController', 'test'); + + $this->assertTrue($reader->hasAnnotation('Annotation')); + } + + + public function testInheritanceOverride(): void { + $reader = new ControllerMethodReflector(); + $reader->reflect('Test\AppFramework\Utility\EndController', 'test2'); + + $this->assertTrue($reader->hasAnnotation('NoAnnotation')); + $this->assertFalse($reader->hasAnnotation('Annotation')); + } + + + public function testInheritanceOverrideNoDocblock(): void { + $reader = new ControllerMethodReflector(); + $reader->reflect('Test\AppFramework\Utility\EndController', 'test3'); + + $this->assertFalse($reader->hasAnnotation('Annotation')); + } + + public function testRangeDetection(): void { + $reader = new ControllerMethodReflector(); + $reader->reflect('Test\AppFramework\Utility\EndController', 'test4'); + + $rangeInfo1 = $reader->getRange('rangedOne'); + $this->assertSame(-4, $rangeInfo1['min']); + $this->assertSame(42, $rangeInfo1['max']); + + $rangeInfo2 = $reader->getRange('rangedTwo'); + $this->assertSame(PHP_INT_MIN, $rangeInfo2['min']); + $this->assertSame(PHP_INT_MAX, $rangeInfo2['max']); + + $rangeInfo3 = $reader->getRange('rangedThree'); + $this->assertSame(1, $rangeInfo3['min']); + $this->assertSame(6, $rangeInfo3['max']); + + $rangeInfo3 = $reader->getRange('rangedFour'); + $this->assertSame(-70, $rangeInfo3['min']); + $this->assertSame(-30, $rangeInfo3['max']); + } +} diff --git a/tests/lib/AppFramework/Utility/SimpleContainerTest.php b/tests/lib/AppFramework/Utility/SimpleContainerTest.php new file mode 100644 index 00000000000..33800c7376f --- /dev/null +++ b/tests/lib/AppFramework/Utility/SimpleContainerTest.php @@ -0,0 +1,260 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace Test\AppFramework\Utility; + +use OC\AppFramework\Utility\SimpleContainer; +use OCP\AppFramework\QueryException; +use Psr\Container\NotFoundExceptionInterface; + +interface TestInterface { +} + +class ClassEmptyConstructor implements IInterfaceConstructor { +} + +class ClassSimpleConstructor implements IInterfaceConstructor { + public function __construct( + public $test, + ) { + } +} + +class ClassComplexConstructor { + public function __construct( + public ClassSimpleConstructor $class, + public $test, + ) { + } +} + +class ClassNullableUntypedConstructorArg { + public function __construct( + public $class, + ) { + } +} +class ClassNullableTypedConstructorArg { + public function __construct( + public ?\Some\Class $class, + ) { + } +} + +interface IInterfaceConstructor { +} +class ClassInterfaceConstructor { + public function __construct( + public IInterfaceConstructor $class, + public $test, + ) { + } +} + + +class SimpleContainerTest extends \Test\TestCase { + private $container; + + protected function setUp(): void { + $this->container = new SimpleContainer(); + } + + + + public function testRegister(): void { + $this->container->registerParameter('test', 'abc'); + $this->assertEquals('abc', $this->container->query('test')); + } + + + /** + * Test querying a class that is not registered without autoload enabled + */ + public function testNothingRegistered(): void { + try { + $this->container->query('something really hard', false); + $this->fail('Expected `QueryException` exception was not thrown'); + } catch (\Throwable $exception) { + $this->assertInstanceOf(QueryException::class, $exception); + $this->assertInstanceOf(NotFoundExceptionInterface::class, $exception); + } + } + + + /** + * Test querying a class that is not registered with autoload enabled + */ + public function testNothingRegistered_autoload(): void { + try { + $this->container->query('something really hard'); + $this->fail('Expected `QueryException` exception was not thrown'); + } catch (\Throwable $exception) { + $this->assertInstanceOf(QueryException::class, $exception); + $this->assertInstanceOf(NotFoundExceptionInterface::class, $exception); + } + } + + + + public function testNotAClass(): void { + $this->expectException(QueryException::class); + + $this->container->query('Test\AppFramework\Utility\TestInterface'); + } + + + public function testNoConstructorClass(): void { + $object = $this->container->query('Test\AppFramework\Utility\ClassEmptyConstructor'); + $this->assertTrue($object instanceof ClassEmptyConstructor); + } + + + public function testInstancesOnlyOnce(): void { + $object = $this->container->query('Test\AppFramework\Utility\ClassEmptyConstructor'); + $object2 = $this->container->query('Test\AppFramework\Utility\ClassEmptyConstructor'); + $this->assertSame($object, $object2); + } + + public function testConstructorSimple(): void { + $this->container->registerParameter('test', 'abc'); + $object = $this->container->query( + 'Test\AppFramework\Utility\ClassSimpleConstructor' + ); + $this->assertTrue($object instanceof ClassSimpleConstructor); + $this->assertEquals('abc', $object->test); + } + + + public function testConstructorComplex(): void { + $this->container->registerParameter('test', 'abc'); + $object = $this->container->query( + 'Test\AppFramework\Utility\ClassComplexConstructor' + ); + $this->assertTrue($object instanceof ClassComplexConstructor); + $this->assertEquals('abc', $object->class->test); + $this->assertEquals('abc', $object->test); + } + + + public function testConstructorComplexInterface(): void { + $this->container->registerParameter('test', 'abc'); + $this->container->registerService( + 'Test\AppFramework\Utility\IInterfaceConstructor', function ($c) { + return $c->query('Test\AppFramework\Utility\ClassSimpleConstructor'); + }); + $object = $this->container->query( + 'Test\AppFramework\Utility\ClassInterfaceConstructor' + ); + $this->assertTrue($object instanceof ClassInterfaceConstructor); + $this->assertEquals('abc', $object->class->test); + $this->assertEquals('abc', $object->test); + } + + + public function testOverrideService(): void { + $this->container->registerService( + 'Test\AppFramework\Utility\IInterfaceConstructor', function ($c) { + return $c->query('Test\AppFramework\Utility\ClassSimpleConstructor'); + }); + $this->container->registerService( + 'Test\AppFramework\Utility\IInterfaceConstructor', function ($c) { + return $c->query('Test\AppFramework\Utility\ClassEmptyConstructor'); + }); + $object = $this->container->query( + 'Test\AppFramework\Utility\IInterfaceConstructor' + ); + $this->assertTrue($object instanceof ClassEmptyConstructor); + } + + public function testRegisterAliasParamter(): void { + $this->container->registerParameter('test', 'abc'); + $this->container->registerAlias('test1', 'test'); + $this->assertEquals('abc', $this->container->query('test1')); + } + + public function testRegisterAliasService(): void { + $this->container->registerService('test', function () { + return new \StdClass; + }, true); + $this->container->registerAlias('test1', 'test'); + $this->assertSame( + $this->container->query('test'), $this->container->query('test')); + $this->assertSame( + $this->container->query('test1'), $this->container->query('test1')); + $this->assertSame( + $this->container->query('test'), $this->container->query('test1')); + } + + public static function sanitizeNameProvider(): array { + return [ + ['ABC\\Foo', 'ABC\\Foo'], + ['\\ABC\\Foo', '\\ABC\\Foo'], + ['\\ABC\\Foo', 'ABC\\Foo'], + ['ABC\\Foo', '\\ABC\\Foo'], + ]; + } + + #[\PHPUnit\Framework\Attributes\DataProvider('sanitizeNameProvider')] + public function testSanitizeName($register, $query): void { + $this->container->registerService($register, function () { + return 'abc'; + }); + $this->assertEquals('abc', $this->container->query($query)); + } + + + public function testConstructorComplexNoTestParameterFound(): void { + $this->expectException(QueryException::class); + + $object = $this->container->query( + 'Test\AppFramework\Utility\ClassComplexConstructor' + ); + /* Use the object to trigger DI on PHP >= 8.4 */ + get_object_vars($object); + } + + public function testRegisterFactory(): void { + $this->container->registerService('test', function () { + return new \StdClass(); + }, false); + $this->assertNotSame( + $this->container->query('test'), $this->container->query('test')); + } + + public function testRegisterAliasFactory(): void { + $this->container->registerService('test', function () { + return new \StdClass(); + }, false); + $this->container->registerAlias('test1', 'test'); + $this->assertNotSame( + $this->container->query('test'), $this->container->query('test')); + $this->assertNotSame( + $this->container->query('test1'), $this->container->query('test1')); + $this->assertNotSame( + $this->container->query('test'), $this->container->query('test1')); + } + + public function testQueryUntypedNullable(): void { + $this->expectException(QueryException::class); + + $object = $this->container->query( + ClassNullableUntypedConstructorArg::class + ); + /* Use the object to trigger DI on PHP >= 8.4 */ + get_object_vars($object); + } + + public function testQueryTypedNullable(): void { + /** @var ClassNullableTypedConstructorArg $service */ + $service = $this->container->query(ClassNullableTypedConstructorArg::class); + + self::assertNull($service->class); + } +} diff --git a/tests/lib/AppFramework/Utility/TimeFactoryTest.php b/tests/lib/AppFramework/Utility/TimeFactoryTest.php new file mode 100644 index 00000000000..276b2d6da4f --- /dev/null +++ b/tests/lib/AppFramework/Utility/TimeFactoryTest.php @@ -0,0 +1,50 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +namespace Test\AppFramework\Utility; + +use OC\AppFramework\Utility\TimeFactory; + +class TimeFactoryTest extends \Test\TestCase { + protected TimeFactory $timeFactory; + + protected function setUp(): void { + $this->timeFactory = new TimeFactory(); + } + + public function testNow(): void { + $now = $this->timeFactory->now(); + self::assertSame('UTC', $now->getTimezone()->getName()); + } + + public function testNowWithTimeZone(): void { + $timezone = new \DateTimeZone('Europe/Berlin'); + $withTimeZone = $this->timeFactory->withTimeZone($timezone); + + $now = $withTimeZone->now(); + self::assertSame('Europe/Berlin', $now->getTimezone()->getName()); + } + + public function testGetTimeZone(): void { + $expected = new \DateTimeZone('Europe/Berlin'); + $actual = $this->timeFactory->getTimeZone('Europe/Berlin'); + self::assertEquals($expected, $actual); + } + + public function testGetTimeZoneUTC(): void { + $expected = new \DateTimeZone('UTC'); + $actual = $this->timeFactory->getTimeZone(); + self::assertEquals($expected, $actual); + } + + public function testGetTimeZoneInvalid(): void { + $this->expectException(\Exception::class); + $this->timeFactory->getTimeZone('blubblub'); + } +} |