RouteCollectionTest   A
last analyzed

Complexity

Total Complexity 20

Size/Duplication

Total Lines 284
Duplicated Lines 25.7 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 20
c 1
b 0
f 0
lcom 1
cbo 5
dl 73
loc 284
rs 10

19 Methods

Rating   Name   Duplication   Size   Complexity  
A setUp() 0 4 1
A testGroup() 0 16 2
A testAddRoute() 0 12 1
A testIsUnique() 0 7 1
A testCount() 0 9 1
A testKey() 9 9 1
A testNext() 10 10 1
A testCurrent() 9 9 1
A testValidTrue() 8 8 1
A testValidFalse() 9 9 1
A testRewind() 0 13 1
A testMergeCollection() 0 14 1
A testMergeCollectionDuplicate() 0 13 1
A testGetRoutes() 0 10 1
A testFormatRoutePatternForRoute() 0 6 1
A testFormatRoutePatternForGroup() 0 11 1
A testMergeGroupNames() 0 13 1
A testFindHandlerNamespace() 14 14 1
A testFindHandlerNamespaceNoBacklash() 14 14 1

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
/**
3
 * This file is part of the DS Framework.
4
 *
5
 * (c) Dan Smith <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
namespace Tests\Ds\Router;
11
12
use Ds\Router\Exceptions\RouteException;
13
use Ds\Router\Route;
14
use Ds\Router\RouteCollection;
15
16
/**
17
 * Route Collection Tests.
18
 * @package Tests\Ds\Router
19
 */
20
class RouteCollectionTest extends \PHPUnit_Framework_TestCase
21
{
22
23
    /**
24
     * @var RouteCollection
25
     */
26
    public $collection;
27
28
    /**
29
     * Route Collection Set up.
30
     */
31
    public function setUp()
32
    {
33
        $this->collection = new RouteCollection();
34
    }
35
36
    /**
37
     *
38
     */
39
    public function testGroup()
40
    {
41
        $handler = 'my-handler';
42
        $routes = $this->collection;
43
        $routes->group('/path', function () use ($routes, $handler) {
44
            $routes->addRoute(['GET', 'POST'], '/foo', $handler, ['global']);
45
            $routes->group('/sub-dir', function () use ($routes, $handler) {
46
                $routes->addRoute(['GET'], '/sub-page', $handler, ['sub-dir']);
47
            });
48
        });
49
        foreach ($routes as $route) {
50
            $expected[] = $route;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$expected was never initialized. Although not strictly required by PHP, it is generally a good practice to add $expected = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
51
        }
52
        $actual = Helpers\Reflection::getProperty(RouteCollection::class, 'collection', $routes);
53
        $this->assertEquals($expected, $actual);
0 ignored issues
show
Bug introduced by
The variable $expected does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
54
    }
55
56
    /**
57
     *
58
     */
59
    public function testAddRoute()
60
    {
61
        $handler = 'my-handler';
62
        $collection = new RouteCollection();
63
        $collection->addRoute(['GET', 'POST'], '/foo', $handler, ['global']);
64
        $actual = Helpers\Reflection::getProperty(RouteCollection::class, 'collection', $collection);
65
        $expected = [
66
            0 => new Route('GET', '/foo', $handler, ['global']),
67
            1 => new Route('POST', '/foo', $handler, ['global'])
68
        ];
69
        $this->assertEquals($expected, $actual);
70
    }
71
72
    /**
73
     * Test that RouterException is thrown on duplicate route.
74
     */
75
    public function testIsUnique()
76
    {
77
        $this->setExpectedException(RouteException::class);
78
        $handler = 'my-handler';
79
        $this->collection->addRoute('GET', '/foo', $handler, ['global']);
80
        $this->collection->addRoute('GET', '/foo', $handler, ['global']);
81
    }
82
83
    /**
84
     *
85
     */
86
    public function testCount()
87
    {
88
        $handler = 'my-handler';
89
        $collection = new RouteCollection();
90
        $collection->addRoute(['GET', 'POST'], '/foo', $handler, ['global']);
91
        $collection->addRoute(['GET', 'POST'], '/bar', $handler, ['global']);
92
        $expected = 4;
93
        $this->assertEquals($expected, $collection->count());
94
    }
95
96
    /**
97
     *
98
     */
99 View Code Duplication
    public function testKey()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
100
    {
101
        $handler = 'my-handler';
102
        $collection = new RouteCollection();
103
        $collection->addRoute(['GET'], '/foo', $handler, ['global']);
104
        $collection->addRoute(['GET'], '/bar', $handler, ['global']);
105
        $expected = 0;
106
        $this->assertEquals($expected, $collection->key());
107
    }
108
109
    /**
110
     *
111
     */
112 View Code Duplication
    public function testNext()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
113
    {
114
        $handler = 'my-handler';
115
        $collection = new RouteCollection();
116
        $collection->addRoute(['GET'], '/foo', $handler, ['global']);
117
        $collection->addRoute(['GET'], '/bar', $handler, ['global']);
118
        $expected = 1;
119
        $collection->next();
120
        $this->assertEquals($expected, $collection->key());
121
    }
122
123
    /**
124
     *
125
     */
126 View Code Duplication
    public function testCurrent()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
127
    {
128
        $handler = 'my-handler';
129
        $collection = new RouteCollection();
130
        $collection->addRoute(['GET'], '/foo', $handler, ['global']);
131
        $collection->addRoute(['GET'], '/bar', $handler, ['global']);
132
        $expected = new Route('GET', '/foo', $handler, ['global']);
133
        $this->assertEquals($expected, $collection->current());
134
    }
135
136
    /**
137
     *
138
     */
139 View Code Duplication
    public function testValidTrue()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
140
    {
141
        $handler = 'my-handler';
142
        $collection = new RouteCollection();
143
        $collection->addRoute(['GET'], '/foo', $handler, ['global']);
144
        $expected = true;
145
        $this->assertEquals($expected, $collection->valid());
146
    }
147
148
    /**
149
     *
150
     */
151 View Code Duplication
    public function testValidFalse()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
152
    {
153
        $expected = false;
154
        $handler = 'my-handler';
155
        $collection = new RouteCollection();
156
        $collection->addRoute(['GET'], '/foo', $handler, ['global']);
157
        $collection->next();
158
        $this->assertEquals($expected, $collection->valid());
159
    }
160
161
162
    /**
163
     *
164
     */
165
    public function testRewind()
166
    {
167
        $handler = 'my-handler';
168
        $expected = new Route('GET', '/bar', $handler, ['global']);
169
        $collection = new RouteCollection();
170
        $collection->addRoute(['GET'], '/foo', $handler, ['global']);
171
        $collection->addRoute(['GET'], '/bar', $handler, ['global']);
172
        $collection->next();
173
        $this->assertEquals($expected, $collection->current());
174
        $expectedKey = 0;
175
        $collection->rewind();
176
        $this->assertEquals($expectedKey, $collection->key());
177
    }
178
179
    /**
180
     *
181
     */
182
    public function testMergeCollection()
183
    {
184
        $handler = 'handler::string';
185
        $names = ['routes','names'];
186
        $collection = new RouteCollection();
187
        $collection->addRoute('GET', '/path', $handler, $names);
188
        $collection->addRoute('GET', '/another', $handler, $names);
189
        $secondCollection = new RouteCollection();
190
        $secondCollection->addRoute('GET', '/path-2', $handler, $names);
191
        $secondCollection->addRoute('GET', '/another-2', $handler, $names);
192
        $combined = $collection->mergeCollection($secondCollection, false);
193
        $expectedRouteCount = 4;
194
        $this->assertEquals($expectedRouteCount, $combined->count());
195
    }
196
197
    /**
198
     *
199
     */
200
    public function testMergeCollectionDuplicate()
201
    {
202
        $this->setExpectedException(RouteException::class);
203
        $handler = 'handler::string';
204
        $names = ['routes','names'];
205
        $collection = new RouteCollection();
206
        $collection->addRoute('GET', '/path', $handler, $names);
207
        $collection->addRoute('GET', '/another', $handler, $names);
208
        $secondCollection = new RouteCollection();
209
        $secondCollection->addRoute('GET', '/path', $handler, $names);
210
        $secondCollection->addRoute('GET', '/another-2', $handler, $names);
211
        $collection->mergeCollection($secondCollection, true);
212
    }
213
214
    /**
215
     *
216
     */
217
    public function testGetRoutes()
218
    {
219
        $method = 'GET';
220
        $pattern = '/path';
221
        $handler = 'myhandler';
222
        $names = ['names'];
223
        $expected = [new Route($method, $pattern, $handler, $names)];
224
        $this->collection->addRoute($method, $pattern, $handler, $names);
225
        $this->assertEquals($expected, $this->collection->getRoutes());
226
    }
227
228
    public function testFormatRoutePatternForRoute()
229
    {
230
        $rawPattern = '/foo/';
231
        $expectedPattern = '/' . \ltrim($rawPattern, '/');
232
        $this->assertEquals($expectedPattern, $this->collection->formatRoutePattern($rawPattern));
233
    }
234
235
    /**
236
     * Test that forward slashes are replaces with backslash
237
     */
238
    public function testFormatRoutePatternForGroup()
239
    {
240
        $groups = ['group'];
241
        $collection = $this->collection;
242
        $collection = Helpers\Reflection::setProperty(RouteCollection::class, '_isGrouped', $collection, true);
243
        $collection = Helpers\Reflection::setProperty(RouteCollection::class, 'group', $collection, $groups);
244
        $rawPattern = '/mypattern';
245
        $expectedPattern = '/' . \ltrim($rawPattern, '/');
246
        $expected = \implode('', $groups) . $expectedPattern;
247
        $this->assertEquals($expected, $collection->formatRoutePattern($rawPattern));
248
    }
249
250
    /**
251
     * Test that route names are merge with grouped routes.
252
     */
253
    public function testMergeGroupNames()
254
    {
255
        $expected = ['route-name','group-name'];
256
        $handler = 'myhandler';
257
        $routes = $this->collection;
258
259
        $routes->group('/path', function () use ($routes, $handler) {
260
            $routes->addRoute(['GET', 'POST'], '/foo', $handler, ['route-name']);
261
        }, ['group-name']);
262
263
        $route = $routes->current();
264
        $this->assertEquals($expected, $route->getNames());
265
    }
266
267
    /**
268
     * Test that the namespace is matched to the handler and returned.
269
     */
270 View Code Duplication
    public function testFindHandlerNamespace(){
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
271
272
        $routeCollection = new RouteCollection();
273
        $routeCollection->addNamespace('\Ds\Router');
274
275
        $handler = 'RouteCollection::addRoute';
276
        $expected = '\\Ds\\Router\\RouteCollection::addRoute';
277
278
        $reflectionMethod = new \ReflectionMethod(RouteCollection::class, '_findHandlerNamespace');
279
        $reflectionMethod->setAccessible(true);
280
        $actual = $reflectionMethod->invoke($routeCollection, $handler);
281
282
        $this->assertEquals($expected,$actual);
283
    }
284
285
    /**
286
     * Test that backlash is added to start of namespace on response.
287
     */
288 View Code Duplication
    public function testFindHandlerNamespaceNoBacklash(){
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
289
290
        $routeCollection = new RouteCollection();
291
        $routeCollection->addNamespace('Ds\Router');
292
293
        $handler = 'RouteCollection::addRoute';
294
        $expected = '\\Ds\\Router\\RouteCollection::addRoute';
295
296
        $reflectionMethod = new \ReflectionMethod(RouteCollection::class, '_findHandlerNamespace');
297
        $reflectionMethod->setAccessible(true);
298
        $actual = $reflectionMethod->invoke($routeCollection, $handler);
299
300
        $this->assertEquals($expected,$actual);
301
    }
302
303
}
304