resourceloader: Add version to ResourceLoaderImage urls for long-cache
[lhc/web/wiklou.git] / tests / phpunit / unit / includes / FactoryArgTestTrait.php
1 <?php
2
3 /**
4 * Test that a factory class correctly forwards all arguments to the class it constructs. This is
5 * useful because sometimes a class' constructor will have more arguments added, and it's easy to
6 * accidentally have the factory's constructor fall out of sync.
7 */
8 trait FactoryArgTestTrait {
9 /**
10 * @return string Name of factory class
11 */
12 abstract protected static function getFactoryClass();
13
14 /**
15 * @return string Name of instance class
16 */
17 abstract protected static function getInstanceClass();
18
19 /**
20 * @return int The number of arguments that the instance constructor receives but the factory
21 * constructor doesn't. Used for a simple argument count check. Override if this isn't zero.
22 */
23 protected static function getExtraClassArgCount() {
24 return 0;
25 }
26
27 /**
28 * Override if your factory method name is different from newInstanceClassName.
29 *
30 * @return string
31 */
32 protected function getFactoryMethodName() {
33 return 'new' . $this->getInstanceClass();
34 }
35
36 /**
37 * Override if $factory->$method( ...$args ) isn't the right way to create an instance, where
38 * $method is returned from getFactoryMethodName(), and $args is constructed by applying
39 * getMockValueForParam() to the factory method's parameters.
40 *
41 * @param object $factory Factory object
42 * @return object Object created by factory
43 */
44 protected function createInstanceFromFactory( $factory ) {
45 $methodName = $this->getFactoryMethodName();
46 $methodObj = new ReflectionMethod( $factory, $methodName );
47 $mocks = [];
48 foreach ( $methodObj->getParameters() as $param ) {
49 $mocks[] = $this->getMockValueForParam( $param );
50 }
51
52 return $factory->$methodName( ...$mocks );
53 }
54
55 public function testConstructorArgNum() {
56 $factoryClass = static::getFactoryClass();
57 $instanceClass = static::getInstanceClass();
58 $factoryConstructor = new ReflectionMethod( $factoryClass, '__construct' );
59 $instanceConstructor = new ReflectionMethod( $instanceClass, '__construct' );
60 $this->assertSame(
61 $instanceConstructor->getNumberOfParameters() - static::getExtraClassArgCount(),
62 $factoryConstructor->getNumberOfParameters(),
63 "$instanceClass and $factoryClass constructors have an inconsistent number of " .
64 ' parameters. Did you add a parameter to one and not the other?' );
65 }
66
67 /**
68 * Override if getMockValueForParam doesn't produce suitable values for one or more of the
69 * parameters to your factory constructor or create method.
70 *
71 * @param ReflectionParameter $param One of the factory constructor's arguments
72 * @return array Empty to not override, or an array of one element which is the value to pass
73 * that will allow the object to be constructed successfully
74 */
75 protected function getOverriddenMockValueForParam( ReflectionParameter $param ) {
76 return [];
77 }
78
79 /**
80 * Override if this doesn't produce suitable values for one or more of the parameters to your
81 * factory constructor or create method.
82 *
83 * @param ReflectionParameter $param One of the factory constructor's arguments
84 * @return mixed A value to pass that will allow the object to be constructed successfully
85 */
86 protected function getMockValueForParam( ReflectionParameter $param ) {
87 $overridden = $this->getOverriddenMockValueForParam( $param );
88 if ( $overridden ) {
89 return $overridden[0];
90 }
91
92 $pos = $param->getPosition();
93
94 $type = (string)$param->getType();
95
96 if ( $type === 'array' ) {
97 return [ "some unlikely string $pos" ];
98 }
99
100 if ( class_exists( $type ) || interface_exists( $type ) ) {
101 return $this->createMock( $type );
102 }
103
104 if ( $type === '' ) {
105 // Optimistically assume a string is okay
106 return "some unlikely string $pos";
107 }
108
109 $this->fail( "Unrecognized parameter type $type" );
110 }
111
112 /**
113 * Assert that the given $instance correctly received $val as the value for parameter $name. By
114 * default, checks that the instance has some member whose value is the same as $val.
115 *
116 * @param object $instance
117 * @param string $name Name of parameter to the factory object's constructor
118 * @param mixed $val
119 */
120 protected function assertInstanceReceivedParam( $instance, $name, $val ) {
121 foreach ( ( new ReflectionObject( $instance ) )->getProperties() as $prop ) {
122 $prop->setAccessible( true );
123 if ( $prop->getValue( $instance ) === $val ) {
124 $this->assertTrue( true );
125 return;
126 }
127 }
128
129 $this->assertFalse( true, "Param $name not received by " . static::getInstanceClass() );
130 }
131
132 public function testAllArgumentsWerePassed() {
133 $factoryClass = static::getFactoryClass();
134
135 $factoryConstructor = new ReflectionMethod( $factoryClass, '__construct' );
136 $mocks = [];
137 foreach ( $factoryConstructor->getParameters() as $param ) {
138 $mocks[$param->getName()] = $this->getMockValueForParam( $param );
139 }
140
141 $instance =
142 $this->createInstanceFromFactory( new $factoryClass( ...array_values( $mocks ) ) );
143
144 foreach ( $mocks as $name => $mock ) {
145 $this->assertInstanceReceivedParam( $instance, $name, $mock );
146 }
147 }
148 }