Completed
Push — 7.5 ( 50d956...42925a )
by
unknown
34:53 queued 13:16
created

AbstractServiceTest::testForLanguagesLookup()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 40

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
nc 8
nop 6
dl 0
loc 40
rs 9.28
c 0
b 0
f 0
1
<?php
2
3
namespace eZ\Publish\Core\Repository\SiteAccessAware\Tests;
4
5
use eZ\Publish\API\Repository\LanguageResolver;
6
use PHPUnit\Framework\TestCase;
7
use Closure;
8
use ReflectionClass;
9
use ReflectionMethod;
10
11
/**
12
 * Abstract tests for SiteAccessAware Services.
13
 *
14
 * Implies convention for methods on these services to either:
15
 * - Do nothing, pass-through call and optionally (default:true) return value
16
 * - lookup languages [IF not defined by callee] on one of the arguments given and pass it to next one.
17
 */
18
abstract class AbstractServiceTest extends TestCase
19
{
20
    /**
21
     * Purely to attempt to make tests easier to read.
22
     *
23
     * As language parameter is ignored from providers and replced with values in tests, this is used to mark value of
24
     * language argument instead of either askingproviders to use 0, or a valid language array which would then not be
25
     * used.
26
     */
27
    const LANG_ARG = 0;
28
29
    /** @var \object|\PHPUnit_Framework_MockObject_MockObject */
30
    protected $innerApiServiceMock;
31
32
    /** @var object */
33
    protected $service;
34
35
    /** @var \eZ\Publish\API\Repository\LanguageResolver|\PHPUnit_Framework_MockObject_MockObject */
36
    protected $languageResolverMock;
37
38
    abstract public function getAPIServiceClassName();
39
40
    abstract public function getSiteAccessAwareServiceClassName();
41
42
    public function setUp()
43
    {
44
        parent::setUp();
45
        $this->innerApiServiceMock = $this->getMockBuilder($this->getAPIServiceClassName())->getMock();
46
        $this->languageResolverMock = $this->getMockBuilder(LanguageResolver::class)
47
            ->disableOriginalConstructor()
48
            ->getMock();
49
        $serviceClassName = $this->getSiteAccessAwareServiceClassName();
50
51
        $this->service = new $serviceClassName(
52
            $this->innerApiServiceMock,
53
            $this->languageResolverMock
54
        );
55
    }
56
57
    protected function tearDown()
58
    {
59
        unset($this->service);
60
        unset($this->languageResolverMock);
61
        unset($this->innerApiServiceMock);
62
        parent::tearDown();
63
    }
64
65
    /**
66
     * @return array See signature on {@link testForPassTrough} for arguments and their type.
67
     */
68
    abstract public function providerForPassTroughMethods();
69
70
    /**
71
     * Make sure these methods does nothing more then passing the arguments to inner service.
72
     *
73
     * Methods tested here are basically those without as languages argument.
74
     *
75
     * @dataProvider providerForPassTroughMethods
76
     *
77
     * @param string $method
78
     * @param array $arguments
79
     * @param bool $return
80
     */
81
    final public function testForPassTrough($method, array $arguments, $return = true)
82
    {
83
        $this->innerApiServiceMock
84
            ->expects($this->once())
85
            ->method($method)
86
            ->with(...$arguments)
87
            ->willReturn($return);
88
89
        $actualReturn = $this->service->$method(...$arguments);
90
91
        $this->assertEquals($return, $actualReturn);
92
    }
93
94
    /**
95
     * @return array See signature on {@link testForLanguagesLookup} for arguments and their type.
96
     *               NOTE: languages / prioritizedLanguage, can be set to 0, it will be replaced by tests methods.
97
     */
98
    abstract public function providerForLanguagesLookupMethods();
99
100
    /**
101
     * Method to be able to customize the logic for setting expected language argument during {@see testForLanguagesLookup()}.
102
     *
103
     * @param array $arguments
104
     * @param int $languageArgumentIndex
105
     * @param array $languages
106
     *
107
     * @return array
108
     */
109
    protected function setLanguagesLookupExpectedArguments(array $arguments, $languageArgumentIndex, array $languages)
110
    {
111
        $arguments[$languageArgumentIndex] = $languages;
112
113
        return $arguments;
114
    }
115
116
    /**
117
     * Method to be able to customize the logic for setting expected language argument during {@see testForLanguagesLookup()}.
118
     *
119
     * @param array $arguments
120
     * @param int $languageArgumentIndex
121
     *
122
     * @return array
123
     */
124
    protected function setLanguagesLookupArguments(array $arguments, $languageArgumentIndex)
125
    {
126
        $arguments[$languageArgumentIndex] = [];
127
128
        return $arguments;
129
    }
130
131
    /**
132
     * Test that language aware methods does a language lookup when language is not set.
133
     *
134
     * @dataProvider providerForLanguagesLookupMethods
135
     *
136
     * @param string $method
137
     * @param array $arguments
138
     * @param mixed|null $return
139
     * @param int $languageArgumentIndex From 0 and up, so the array index on $arguments.
140
     */
141
    final public function testForLanguagesLookup($method, array $arguments, $return, $languageArgumentIndex, callable $callback = null, int $alwaysAvailableArgumentIndex = null)
142
    {
143
        $languages = ['eng-GB', 'eng-US'];
144
145
        $arguments = $this->setLanguagesLookupArguments($arguments, $languageArgumentIndex, $languages);
146
147
        $expectedArguments = $this->setLanguagesLookupExpectedArguments($arguments, $languageArgumentIndex, $languages);
148
149
        $this->languageResolverMock
150
            ->expects($this->once())
151
            ->method('getPrioritizedLanguages')
152
            ->with([])
153
            ->willReturn($languages);
154
155
        if ($alwaysAvailableArgumentIndex) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $alwaysAvailableArgumentIndex of type null|integer is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
156
            $arguments[$alwaysAvailableArgumentIndex] = null;
157
            $expectedArguments[$alwaysAvailableArgumentIndex] = true;
158
            $this->languageResolverMock
159
                ->expects($this->once())
160
                ->method('getUseAlwaysAvailable')
161
                ->with(null)
162
                ->willReturn(true);
163
        }
164
165
        $this->innerApiServiceMock
166
            ->expects($this->once())
167
            ->method($method)
168
            ->with(...$expectedArguments)
169
            ->willReturn($return);
170
171
        if ($callback instanceof Closure) {
172
            $callback->bindTo($this, static::class)(true);
173
        }
174
175
        $actualReturn = $this->service->$method(...$arguments);
176
177
        if ($return) {
178
            $this->assertEquals($return, $actualReturn);
179
        }
180
    }
181
182
    /**
183
     * Method to be able to customize the logic for setting expected language argument during {@see testForLanguagesPassTrough()}.
184
     *
185
     * @param array $arguments
186
     * @param int $languageArgumentIndex
187
     * @param array $languages
188
     *
189
     * @return array
190
     */
191
    protected function setLanguagesPassTroughArguments(array $arguments, $languageArgumentIndex, array $languages)
192
    {
193
        $arguments[$languageArgumentIndex] = $languages;
194
195
        return $arguments;
196
    }
197
198
    /**
199
     * Make sure these methods does nothing more then passing the arguments to inner service.
200
     *
201
     * @dataProvider providerForLanguagesLookupMethods
202
     *
203
     * @param string $method
204
     * @param array $arguments
205
     * @param mixed|null $return
206
     * @param int $languageArgumentIndex From 0 and up, so the array index on $arguments.
207
     */
208
    final public function testForLanguagesPassTrough($method, array $arguments, $return, $languageArgumentIndex, callable $callback = null, int $alwaysAvailableArgumentIndex = null)
209
    {
210
        $languages = ['eng-GB', 'eng-US'];
211
        $arguments = $this->setLanguagesPassTroughArguments($arguments, $languageArgumentIndex, $languages);
212
213
        $this->languageResolverMock
214
            ->expects($this->once())
215
            ->method('getPrioritizedLanguages')
216
            ->with($languages)
217
            ->willReturn($languages);
218
219
        if ($alwaysAvailableArgumentIndex) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $alwaysAvailableArgumentIndex of type null|integer is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
220
            $this->languageResolverMock
221
                ->expects($this->once())
222
                ->method('getUseAlwaysAvailable')
223
                ->with($arguments[$alwaysAvailableArgumentIndex])
224
                ->willReturn($arguments[$alwaysAvailableArgumentIndex]);
225
        }
226
227
        $this->innerApiServiceMock
228
            ->expects($this->once())
229
            ->method($method)
230
            ->with(...$arguments)
231
            ->willReturn($return);
232
233
        if ($callback instanceof Closure) {
234
            $callback->bindTo($this, static::class)(false);
235
        }
236
237
        $actualReturn = $this->service->$method(...$arguments);
238
239
        if ($return) {
240
            $this->assertEquals($return, $actualReturn);
241
        }
242
    }
243
244
    /**
245
     * @todo replace with coverage testing (see EZP-31035)
246
     */
247
    final public function testIfThereIsMissingTest(): void
248
    {
249
        $tested = array_merge(
250
            array_column($this->providerForLanguagesLookupMethods(), 0),
251
            array_column($this->providerForPassTroughMethods(), 0)
252
        );
253
254
        $class = new ReflectionClass($this->getSiteAccessAwareServiceClassName());
255
        foreach ($class->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
256
            if (!$method->isConstructor() && !in_array($method->getShortName(), $tested)) {
257
                $this->addWarning(
258
                    sprintf(
259
                        'Test for the %s::%s method is missing',
260
                        $this->getSiteAccessAwareServiceClassName(),
261
                        $method->getName()
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
262
                    )
263
                );
264
            }
265
        }
266
    }
267
}
268