ParserTest::testMatchConcatenation()   B
last analyzed

Complexity

Conditions 4
Paths 27

Size

Total Lines 159
Code Lines 102

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 159
c 0
b 0
f 0
rs 8.1935
cc 4
eloc 102
nc 27
nop 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Netdudes\DataSourceryBundle\Tests\UQL;
4
5
use Netdudes\DataSourceryBundle\UQL\AST\ASTArray;
6
use Netdudes\DataSourceryBundle\UQL\AST\ASTAssertion;
7
use Netdudes\DataSourceryBundle\UQL\AST\ASTGroup;
8
use Netdudes\DataSourceryBundle\UQL\Exception\UQLSyntaxError;
9
use Netdudes\DataSourceryBundle\UQL\Lexer;
10
use Netdudes\DataSourceryBundle\UQL\Parser;
11
use PHPUnit\Framework\TestCase;
12
13
class ParserTest extends TestCase
14
{
15
    public function testMatchOperator()
16
    {
17
        $parser = new Parser();
18
        $tokensToTest = [
19
            'T_OP_NEQ',
20
            'T_OP_LTE',
21
            'T_OP_LT',
22
            'T_OP_GTE',
23
            'T_OP_GT',
24
            'T_OP_EQ',
25
            'T_OP_IN',
26
            'T_OP_NIN',
27
        ];
28
29
        // Build a token stream from the list of tokens to test
30
        $tokenStream = array_map(
31
            function ($token) {
32
                return [
33
                    'token' => $token
34
                ];
35
            },
36
            $tokensToTest
37
        );
38
39
        // Initialise
40
        $parser->setTokenStream($tokenStream);
41
        $parser->setTokenIndex(-1);
42
43
        // Try them all
44
        foreach ($tokensToTest as $token) {
45
            $this->assertEquals($token, $parser->matchOperator()['token'], "Token $token should match as an operator");
46
        }
47
    }
48
49
    public function testMatchLogic()
50
    {
51
        $parser = new Parser();
52
        $tokensToTest = [
53
            'T_LOGIC_AND',
54
            'T_LOGIC_OR',
55
            'T_LOGIC_XOR',
56
        ];
57
58
        // Build a token stream from the list of tokens to test
59
        $tokenStream = array_map(
60
            function ($token) {
61
                return [
62
                    'token' => $token
63
                ];
64
            },
65
            $tokensToTest
66
        );
67
68
        // Initialise
69
        $parser->setTokenStream($tokenStream);
70
        $parser->setTokenIndex(-1);
71
72
        // Try them all
73
        foreach ($tokensToTest as $token) {
74
            $this->assertEquals($token, $parser->matchLogic()['token'], "Token $token should match as a logic token");
75
        }
76
    }
77
78
    public function testMatchAssertion()
79
    {
80
        // Case 1: Correct assertion
81
82
        $assertionTokenStream = [
83
            [
84
                'token' => 'T_IDENTIFIER',
85
                'match' => 'testIdentifier',
86
            ],
87
            [
88
                'token' => 'T_OP_EQ',
89
            ],
90
            [
91
                'token' => 'T_LITERAL',
92
                "match" => '"Some Literal"'
93
            ],
94
        ];
95
96
        $parser = new Parser();
97
        $parser->setTokenIndex(-1);
98
        $parser->setTokenStream($assertionTokenStream);
99
        $result = $parser->matchAssertion();
100
        $this->assertTrue($result instanceof ASTAssertion, "Result of matchAssertion should be a ASTAssertion");
101
        $this->assertEquals("testIdentifier", $result->getIdentifier());
102
        $this->assertEquals('"Some Literal"', $result->getValue());
103
104
        // Case 2: Not an assertion (first element not a literal)
105
106
        $nonAssertionTokenStream = [
107
            [
108
                'token' => 'T_LOGIC_AND',
109
            ],
110
        ];
111
112
        $parser = new Parser();
113
        $parser->setTokenIndex(-1);
114
        $parser->setTokenStream($nonAssertionTokenStream);
115
        $result = $parser->matchAssertion();
116
        $this->assertFalse($result, "Result for non-matching assertion should be false");
0 ignored issues
show
Bug introduced by
It seems like $result defined by $parser->matchAssertion() on line 115 can also be of type object<Netdudes\DataSour...e\UQL\AST\ASTAssertion>; however, PHPUnit\Framework\Assert::assertFalse() does only seem to accept boolean, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
117
118
        $invalidAssertionTokenStream = [
119
            [
120
                'token' => 'T_IDENTIFIER',
121
                'match' => 'valid',
122
            ],
123
            [
124
                'token' => 'T_IDENTIFIER',
125
                'match' => 'invalid'
126
            ],
127
        ];
128
129
        // Case 3: Wrongly formatted assertion
130
131
        $parser = new Parser();
132
        $parser->setTokenIndex(-1);
133
        $parser->setTokenStream($invalidAssertionTokenStream);
134
        try {
135
            $parser->matchAssertion();
136
            $this->fail("Matching invalid assertion token stream should raise UQLSyntaxError");
137
        } catch (UQLSyntaxError $e) {
138
            // Caught the exception. Pass the test.
139
        }
140
141
        $invalidAssertionTokenStream = [
142
            [
143
                'token' => 'T_IDENTIFIER',
144
                'match' => 'valid',
145
            ],
146
            [
147
                'token' => 'T_OP_GTE'
148
            ],
149
            [
150
                'token' => 'T_LOGIC_AND',
151
                'match' => 'invalid'
152
            ],
153
        ];
154
155
        $parser = new Parser();
156
        $parser->setTokenIndex(-1);
157
        $parser->setTokenStream($invalidAssertionTokenStream);
158
        try {
159
            $parser->matchAssertion();
160
            $this->fail("Matching invalid assertion token stream should raise UQLSyntaxError");
161
        } catch (UQLSyntaxError $e) {
162
            // Caught the exception. Pass the test.
163
        }
164
    }
165
166
    public function testMatchStatement()
167
    {
168
        // Case 1: Returns a group
169
170
        $mockParser = $this->getMockBuilder('Netdudes\DataSourceryBundle\UQL\Parser')
171
            ->setMethods(['matchGroup'])
172
            ->getMock();
173
        $mockParser->expects($this->any())
174
            ->method('matchGroup')
175
            ->will($this->returnValue('MockValidGroup'));
176
177
        $this->assertEquals('MockValidGroup', $mockParser->matchStatement());
178
179
        // Case 2: Returns an assertion
180
181
        $mockParser = $this->getMockBuilder('Netdudes\DataSourceryBundle\UQL\Parser')
182
            ->setMethods(['matchGroup', 'matchAssertion'])
183
            ->getMock();
184
        $mockParser->expects($this->any())
185
            ->method('matchGroup')
186
            ->will($this->returnValue(false));
187
        $mockParser->expects($this->any())
188
            ->method('matchAssertion')
189
            ->will($this->returnValue('MockValidAssertion'));
190
191
        $this->assertEquals('MockValidAssertion', $mockParser->matchStatement());
192
193
        // Case 3: Neither, returns false
194
195
        $mockParser = $this->getMockBuilder('Netdudes\DataSourceryBundle\UQL\Parser')
196
            ->setMethods(['matchGroup', 'matchAssertion'])
197
            ->getMock();
198
        $mockParser->expects($this->any())
199
            ->method('matchGroup')
200
            ->will($this->returnValue(false));
201
        $mockParser->expects($this->any())
202
            ->method('matchAssertion')
203
            ->will($this->returnValue(false));
204
205
        $this->assertFalse($mockParser->matchStatement());
206
    }
207
208
    public function testMatchGroup()
209
    {
210
        // Case 1: Correct
211
212
        $mockParser = $this->getMockBuilder('Netdudes\DataSourceryBundle\UQL\Parser')
213
            ->setMethods(['matchConcatenation'])
214
            ->getMock();
215
        $mockParser->expects($this->any())
216
            ->method('matchConcatenation')
217
            ->will($this->returnValue('MockValidConcatenation'));
218
219
        $tokenStream = [
220
            [
221
                'token' => 'T_BRACKET_OPEN',
222
            ],
223
            [
224
                'token' => 'T_BRACKET_CLOSE'
225
            ],
226
        ];
227
228
        // Case 2: Not a group (no opening bracket)
229
230
        $mockParser->setTokenStream($tokenStream);
231
        $mockParser->setTokenIndex(-1);
232
        $this->assertEquals('MockValidConcatenation', $mockParser->matchGroup());
233
234
        $nonGroupTokenStream = [
235
            [
236
                'token' => 'T_INVALID',
237
            ],
238
        ];
239
240
        $mockParser->setTokenStream($nonGroupTokenStream);
241
        $mockParser->setTokenIndex(-1);
242
        $this->assertFalse($mockParser->matchGroup());
243
244
        // Case 3: Non-matching closing bracket. Invalid.
245
246
        $invalidGroupTokenStream = [
247
            [
248
                'token' => 'T_BRACKET_OPEN',
249
                'match' => 'valid',
250
            ],
251
            [
252
                'token' => 'T_BRACKET_OPEN',
253
                'match' => 'invalid'
254
            ],
255
        ];
256
257
        $mockParser->setTokenStream($invalidGroupTokenStream);
258
        $mockParser->setTokenIndex(-1);
259
260
        try {
261
            $mockParser->matchGroup();
262
            $this->fail("Parser should throw UQLSyntaxError on wrongly formatted group");
263
        } catch (UQLSyntaxError $e) {
264
            // Caught the exception, the test passes.
265
        }
266
    }
267
268
    public function testMatchConcatenation()
269
    {
270
        // Case 1: There is no statement at the beginning
271
272
        $mockParser = $this->getMockBuilder('Netdudes\DataSourceryBundle\UQL\Parser')
273
            ->setMethods(['matchStatement'])
274
            ->getMock();
275
        $mockParser->expects($this->any())
276
            ->method('matchStatement')
277
            ->will($this->returnValue(false));
278
279
        try {
280
            $mockParser->matchConcatenation();
281
            $this->fail('Should fail on testing concatenation if doesn\'t start with a statement');
282
        } catch (UQLSyntaxError $e) {
283
            // Caught the exception, pass.
284
        }
285
286
        // Case 2: There is only one element and no logic.
287
288
        $mockParser = $this->getMockBuilder('Netdudes\DataSourceryBundle\UQL\Parser')
289
            ->setMethods(['matchStatement', 'matchLogic'])
290
            ->getMock();
291
        $mockParser->expects($this->any())
292
            ->method('matchStatement')
293
            ->will($this->returnValue('MockValidStatement'));
294
        $mockParser->expects($this->any())
295
            ->method('matchLogic')
296
            ->will($this->returnValue(false));
297
298
        $result = $mockParser->matchConcatenation();
299
        $this->assertEquals('MockValidStatement', $result, "Concatenations with only one member should return the only statement");
300
301
        // Case 3: There isn't a statement after the first logic
302
303
        $mockParser = $this->getMockBuilder('Netdudes\DataSourceryBundle\UQL\Parser')
304
            ->setMethods(['matchStatement', 'matchLogic', 'getCurrentToken'])
305
            ->getMock();
306
        $mockParser->expects($this->any())
307
            ->method('getCurrentToken')
308
            ->will(
309
                $this->returnValue(
310
                    [
311
                        'token' => 'T_INVALID',
312
                        'match' => 'invalid',
313
                    ]
314
                )
315
            );
316
        $mockParser->expects($this->at(0))
317
            ->method('matchStatement')
318
            ->will($this->returnValue('MockValidStatement'));
319
        $mockParser->expects($this->at(2))
320
            ->method('matchStatement')
321
            ->will($this->returnValue(false));
322
        $mockParser->expects($this->any())
323
            ->method('matchLogic')
324
            ->will(
325
                $this->returnValue(
326
                    [
327
                        'token' => 'MockValidLogic',
328
                    ]
329
                )
330
            );
331
332
        try {
333
            $mockParser->matchConcatenation();
334
            $this->fail('Should throw exception if no statement after logic.');
335
        } catch (UQLSyntaxError $e) {
336
            // caught. Pass.
337
        }
338
        // Case 4: Mismatching logics in concatenation
339
340
        $mockParser = $this->getMockBuilder('Netdudes\DataSourceryBundle\UQL\Parser')
341
            ->setMethods(['matchStatement', 'matchLogic', 'getCurrentToken'])
342
            ->getMock();
343
        $mockParser->expects($this->any())
344
            ->method('getCurrentToken')
345
            ->will(
346
                $this->returnValue(
347
                    [
348
                        'token' => 'T_INVALID',
349
                        'match' => 'invalid',
350
                    ]
351
                )
352
            );
353
        $mockParser->expects($this->any())
354
            ->method('matchStatement')
355
            ->will($this->returnValue('MockValidStatement'));
356
        $mockParser->expects($this->at(0))
357
            ->method('matchLogic')
358
            ->will(
359
                $this->returnValue(
360
                    [
361
                        'token' => 'T_LOGIC_AND',
362
                    ]
363
                )
364
            );
365
        $mockParser->expects($this->at(1))
366
            ->method('matchLogic')
367
            ->will(
368
                $this->returnValue(
369
                    [
370
                        'token' => 'T_LOGIC_OR',
371
                    ]
372
                )
373
            );
374
375
        try {
376
            $mockParser->matchConcatenation();
377
            $this->fail('Should throw exception with mismatching logic in concatenation.');
378
        } catch (UQLSyntaxError $e) {
379
            // caught. Pass.
380
        }
381
382
        // Case 5: Correct
383
384
        $mockParser = $this->getMockBuilder('Netdudes\DataSourceryBundle\UQL\Parser')
385
            ->setMethods(['matchStatement', 'matchLogic', 'getCurrentToken'])
386
            ->getMock();
387
        $mockParser->expects($this->any())
388
            ->method('getCurrentToken')
389
            ->will(
390
                $this->returnValue(
391
                    [
392
                        'token' => 'T_INVALID',
393
                        'match' => 'invalid',
394
                    ]
395
                )
396
            );
397
        $mockParser->expects($this->at(0))
398
            ->method('matchStatement')
399
            ->will($this->returnValue('MockValidStatement0'));
400
        $mockParser->expects($this->at(2))
401
            ->method('matchStatement')
402
            ->will($this->returnValue('MockValidStatement1'));
403
        $mockParser->expects($this->at(1))
404
            ->method('matchLogic')
405
            ->will(
406
                $this->returnValue(
407
                    [
408
                        'token' => 'T_LOGIC_AND',
409
                    ]
410
                )
411
            );
412
        $mockParser->expects($this->at(3))
413
            ->method('matchLogic')
414
            ->will($this->returnValue(false));
415
416
        $result = $mockParser->matchConcatenation();
417
        $this->assertTrue($result instanceof ASTGroup);
418
        $this->assertEquals(
419
            [
420
                'MockValidStatement0',
421
                'MockValidStatement1',
422
            ],
423
            $result->getElements()
424
        );
425
        $this->assertEquals('T_LOGIC_AND', $result->getLogic());
426
    }
427
428
    public function testMatchArray()
429
    {
430
        $testArrays = [
431
            "[1, 2, 3, 4, 5]" => [1, 2, 3, 4, 5],
432
            "[1, \"abc\", 2, \"def\"]" => [1, "\"abc\"", 2, "\"def\""],
433
        ];
434
435
        $parser = new Parser();
436
        foreach ($testArrays as $testArray => $expectedResult) {
437
            $tokenStream = Lexer::lex($testArray);
438
            $parser->setTokenStream($tokenStream);
439
            $parser->setTokenIndex(-1);
440
441
            $array = $parser->matchArray();
442
443
            $this->assertNotEquals($array, false, 'Array should not be false (meaning it did interpret an array)');
444
            $this->assertTrue($array instanceof ASTArray, 'Arrays should Parse into ASTArrays');
445
            $this->assertCount(count($expectedResult), $array->getElements(), 'Array doesn\'t match the expected number of items');
446
            foreach ($array->getElements() as $index => $element) {
447
                $this->assertEquals($expectedResult[$index], $element, "Element '$element' on index $index doesn't match the expected $expectedResult[$index]");
448
            }
449
        }
450
    }
451
}
452