1
/* This file is part of KDevelop
2
Copyright 2006 Hamish Rodda <rodda@kde.org>
3
Copyright 2008 Niko Sams <niko.sams@gmail.com>
5
This library is free software; you can redistribute it and/or
6
modify it under the terms of the GNU Library General Public
7
License version 2 as published by the Free Software Foundation.
9
This library is distributed in the hope that it will be useful,
10
but WITHOUT ANY WARRANTY; without even the implied warranty of
11
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
Library General Public License for more details.
14
You should have received a copy of the GNU Library General Public License
15
along with this library; see the file COPYING.LIB. If not, write to
16
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17
Boston, MA 02110-1301, USA.
20
#include "lexertest.h"
22
#include <QtTest/QtTest>
24
#include "parsesession.h"
26
#include "phptokentext.h"
28
QTEST_MAIN(Php::LexerTest)
32
void compareEndPosition(TokenStream* tokenStream, qint64 index, qint64 expectedLine, qint64 expectedColumn)
36
tokenStream->endPosition(index, &line, &column);
37
kDebug() << " end" << index << ": actual" << line << column << "expected" << expectedLine << expectedColumn;
38
QCOMPARE(line, expectedLine);
39
QCOMPARE(column, expectedColumn);
42
void compareStartPosition(TokenStream* tokenStream, qint64 index, qint64 expectedLine, qint64 expectedColumn)
46
tokenStream->startPosition(index, &line, &column);
47
kDebug() << "start" << index << ": actual" << line << column << "expected" << expectedLine << expectedColumn;
48
QCOMPARE(line, expectedLine);
49
QCOMPARE(column, expectedColumn);
52
LexerTest::LexerTest()
56
void LexerTest::testOpenTagWithNewline()
58
TokenStream* ts = tokenize("<?php\nfoo;");
59
QVERIFY(ts->size() == 3);
61
QVERIFY(ts->token(0).kind == Parser::Token_OPEN_TAG);
62
compareStartPosition(ts, 0, 0, 0);
63
compareEndPosition(ts, 0, 0, 5);
65
QVERIFY(ts->token(1).kind == Parser::Token_STRING);
66
compareStartPosition(ts, 1, 1, 0);
67
compareEndPosition(ts, 1, 1, 2);
69
QVERIFY(ts->token(2).kind == Parser::Token_SEMICOLON);
70
compareStartPosition(ts, 2, 1, 3);
71
compareEndPosition(ts, 2, 1, 3);
75
void LexerTest::testOpenTagWithSpace()
77
TokenStream* ts = tokenize("<?php foo;");
78
QVERIFY(ts->size() == 3);
80
QVERIFY(ts->token(0).kind == Parser::Token_OPEN_TAG);
81
compareStartPosition(ts, 0, 0, 0);
82
compareEndPosition(ts, 0, 0, 5);
84
QVERIFY(ts->token(1).kind == Parser::Token_STRING);
85
compareStartPosition(ts, 1, 0, 6);
86
compareEndPosition(ts, 1, 0, 8);
88
QVERIFY(ts->token(2).kind == Parser::Token_SEMICOLON);
89
compareStartPosition(ts, 2, 0, 9);
90
compareEndPosition(ts, 2, 0, 9);
94
void LexerTest::testCommentOneLine()
96
TokenStream* ts = tokenize("<?php\n//comment\nfoo;");
97
QVERIFY(ts->size() == 4);
99
QVERIFY(ts->token(0).kind == Parser::Token_OPEN_TAG);
100
compareStartPosition(ts, 0, 0, 0);
101
compareEndPosition(ts, 0, 0, 5);
103
QVERIFY(ts->token(1).kind == Parser::Token_COMMENT);
104
compareStartPosition(ts, 1, 1, 0);
105
compareEndPosition(ts, 1, 1, 9);
107
QVERIFY(ts->token(2).kind == Parser::Token_STRING);
108
compareStartPosition(ts, 2, 2, 0);
109
compareEndPosition(ts, 2, 2, 2);
111
QVERIFY(ts->token(3).kind == Parser::Token_SEMICOLON);
112
compareStartPosition(ts, 3, 2, 3);
113
compareEndPosition(ts, 3, 2, 3);
117
void LexerTest::testCommentOneLine2()
119
TokenStream* ts = tokenize("<?php\n#comment\nfoo;");
120
QVERIFY(ts->size() == 4);
122
QVERIFY(ts->token(0).kind == Parser::Token_OPEN_TAG);
123
compareStartPosition(ts, 0, 0, 0);
124
compareEndPosition(ts, 0, 0, 5);
126
QVERIFY(ts->token(1).kind == Parser::Token_COMMENT);
127
compareStartPosition(ts, 1, 1, 0);
128
compareEndPosition(ts, 1, 1, 8);
130
QVERIFY(ts->token(2).kind == Parser::Token_STRING);
131
compareStartPosition(ts, 2, 2, 0);
132
compareEndPosition(ts, 2, 2, 2);
134
QVERIFY(ts->token(3).kind == Parser::Token_SEMICOLON);
135
compareStartPosition(ts, 3, 2, 3);
136
compareEndPosition(ts, 3, 2, 3);
140
void LexerTest::testCommentMultiLine()
142
TokenStream* ts = tokenize("<?php\n/*com\nment*/\nfoo;", true);
143
QVERIFY(ts->size() == 5);
145
QVERIFY(ts->token(0).kind == Parser::Token_OPEN_TAG);
146
compareStartPosition(ts, 0, 0, 0);
147
compareEndPosition(ts, 0, 0, 5);
149
QVERIFY(ts->token(1).kind == Parser::Token_COMMENT);
150
compareStartPosition(ts, 1, 1, 0);
151
compareEndPosition(ts, 1, 2, 5);
153
QVERIFY(ts->token(2).kind == Parser::Token_WHITESPACE);
154
compareStartPosition(ts, 2, 2, 6);
155
compareEndPosition(ts, 2, 2, 6);
157
QVERIFY(ts->token(3).kind == Parser::Token_STRING);
158
compareStartPosition(ts, 3, 3, 0);
159
compareEndPosition(ts, 3, 3, 2);
161
QVERIFY(ts->token(4).kind == Parser::Token_SEMICOLON);
162
compareStartPosition(ts, 4, 3, 3);
163
compareEndPosition(ts, 4, 3, 3);
167
void LexerTest::testCommentMultiLine2()
169
TokenStream* ts = tokenize("<?php\n/*\nment*/\nfoo;", true);
170
QVERIFY(ts->size() == 5);
172
QVERIFY(ts->token(0).kind == Parser::Token_OPEN_TAG);
173
compareStartPosition(ts, 0, 0, 0);
174
compareEndPosition(ts, 0, 0, 5);
176
QVERIFY(ts->token(1).kind == Parser::Token_COMMENT);
177
compareStartPosition(ts, 1, 1, 0);
178
compareEndPosition(ts, 1, 2, 5);
180
QVERIFY(ts->token(2).kind == Parser::Token_WHITESPACE);
181
compareStartPosition(ts, 2, 2, 6);
182
compareEndPosition(ts, 2, 2, 6);
184
QVERIFY(ts->token(3).kind == Parser::Token_STRING);
185
compareStartPosition(ts, 3, 3, 0);
186
compareEndPosition(ts, 3, 3, 2);
188
QVERIFY(ts->token(4).kind == Parser::Token_SEMICOLON);
189
compareStartPosition(ts, 4, 3, 3);
190
compareEndPosition(ts, 4, 3, 3);
194
void LexerTest::testEndTag()
196
TokenStream* ts = tokenize("<?\n':\n'?>\n>", true, Lexer::DefaultState);
197
//don't crash and we are fine
201
void LexerTest::testNewlineInString()
204
//012345 6 7 890123456789
205
TokenStream* ts = tokenize("<?php \"\n\";", true);
206
QVERIFY(ts->size() == 3);
208
QVERIFY(ts->token(1).kind == Parser::Token_CONSTANT_ENCAPSED_STRING);
209
compareStartPosition(ts, 1, 0, 6);
210
compareEndPosition(ts, 1, 1, 0);
212
QVERIFY(ts->token(2).kind == Parser::Token_SEMICOLON);
213
compareStartPosition(ts, 2, 1, 1);
214
compareEndPosition(ts, 2, 1, 1);
218
void LexerTest::testNewlineInString2()
222
TokenStream* ts = tokenize("<?php '\n';", true);
223
QCOMPARE((int)ts->size(), 3);
225
QVERIFY(ts->token(1).kind == Parser::Token_CONSTANT_ENCAPSED_STRING);
226
compareStartPosition(ts, 1, 0, 6);
227
compareEndPosition(ts, 1, 1, 0);
229
QVERIFY(ts->token(2).kind == Parser::Token_SEMICOLON);
230
compareStartPosition(ts, 2, 1, 1);
231
compareEndPosition(ts, 2, 1, 1);
235
void LexerTest::testNewlineInStringWithVar()
237
TokenStream* ts = tokenize("<?php \"$a\n\";", true);
238
QCOMPARE((int)ts->size(), 6);
240
QVERIFY(ts->token(1).kind == Parser::Token_DOUBLE_QUOTE);
241
compareStartPosition(ts, 1, 0, 6);
242
compareEndPosition(ts, 1, 0, 6);
244
QVERIFY(ts->token(2).kind == Parser::Token_VARIABLE);
245
compareStartPosition(ts, 2, 0, 7);
246
compareEndPosition(ts, 2, 0, 8);
248
QVERIFY(ts->token(3).kind == Parser::Token_ENCAPSED_AND_WHITESPACE);
249
compareStartPosition(ts, 3, 0, 9);
250
compareEndPosition(ts, 3, 0, 9);
252
QVERIFY(ts->token(4).kind == Parser::Token_DOUBLE_QUOTE);
253
compareStartPosition(ts, 4, 1, 0);
254
compareEndPosition(ts, 4, 1, 0);
256
QVERIFY(ts->token(5).kind == Parser::Token_SEMICOLON);
257
compareStartPosition(ts, 5, 1, 1);
258
compareEndPosition(ts, 5, 1, 1);
262
void LexerTest::testNewlineInStringWithVar2()
265
//012345 6 789 0123456789
266
TokenStream* ts = tokenize("<?php \"\n$a\n\";", true);
267
QCOMPARE((int)ts->size(), 7);
269
QVERIFY(ts->token(1).kind == Parser::Token_DOUBLE_QUOTE);
270
compareStartPosition(ts, 1, 0, 6);
271
compareEndPosition(ts, 1, 0, 6);
273
QVERIFY(ts->token(2).kind == Parser::Token_ENCAPSED_AND_WHITESPACE);
274
compareStartPosition(ts, 2, 0, 7);
275
compareEndPosition(ts, 2, 0, 7);
277
QVERIFY(ts->token(3).kind == Parser::Token_VARIABLE);
278
compareStartPosition(ts, 3, 1, 0);
279
compareEndPosition(ts, 3, 1, 1);
281
QVERIFY(ts->token(4).kind == Parser::Token_ENCAPSED_AND_WHITESPACE);
282
compareStartPosition(ts, 4, 1, 2);
283
compareEndPosition(ts, 4, 1, 2);
285
QVERIFY(ts->token(5).kind == Parser::Token_DOUBLE_QUOTE);
286
compareStartPosition(ts, 5, 2, 0);
287
compareEndPosition(ts, 5, 2, 0);
289
QVERIFY(ts->token(6).kind == Parser::Token_SEMICOLON);
290
compareStartPosition(ts, 6, 2, 1);
291
compareEndPosition(ts, 6, 2, 1);
295
void LexerTest::testNewlineInStringWithVar3()
298
//012345 6 789 0123456789
299
TokenStream* ts = tokenize("<?php \"{$$a}\";", true);
300
QCOMPARE((int)ts->size(), 7);
302
QVERIFY(ts->token(1).kind == Parser::Token_DOUBLE_QUOTE);
303
compareStartPosition(ts, 1, 0, 6);
304
compareEndPosition(ts, 1, 0, 6);
306
QVERIFY(ts->token(2).kind == Parser::Token_ENCAPSED_AND_WHITESPACE);
307
compareStartPosition(ts, 2, 0, 7);
308
compareEndPosition(ts, 2, 0, 8);
310
QVERIFY(ts->token(3).kind == Parser::Token_VARIABLE);
311
compareStartPosition(ts, 3, 0, 9);
312
compareEndPosition(ts, 3, 0, 10);
314
QVERIFY(ts->token(4).kind == Parser::Token_ENCAPSED_AND_WHITESPACE);
315
compareStartPosition(ts, 4, 0, 11);
316
compareEndPosition(ts, 4, 0, 11);
318
QVERIFY(ts->token(5).kind == Parser::Token_DOUBLE_QUOTE);
319
compareStartPosition(ts, 5, 0, 12);
320
compareEndPosition(ts, 5, 0, 12);
322
QVERIFY(ts->token(6).kind == Parser::Token_SEMICOLON);
323
compareStartPosition(ts, 6, 0, 13);
324
compareEndPosition(ts, 6, 0, 13);
328
void LexerTest::testMultiplePhpSections()
332
//012345 6 789 0123456789
333
TokenStream* ts = tokenize("<?php $a;?>\n<html>\n<?php $a;?>", true);
334
QCOMPARE((int)ts->size(), 9);
337
for (qint64 line = 0; line <= 2; ++line) {
339
// the html stuff in the middle
340
QVERIFY(ts->token(index).kind == Parser::Token_INLINE_HTML);
341
compareStartPosition(ts, index, 0, 11);
342
compareEndPosition(ts, index, 1, 6);
345
// the php stuff (symmetric) at the start and end
346
QVERIFY(ts->token(index).kind == Parser::Token_OPEN_TAG);
347
compareStartPosition(ts, index, line, 0);
348
compareEndPosition(ts, index, line, 5);
351
QVERIFY(ts->token(index).kind == Parser::Token_VARIABLE);
352
compareStartPosition(ts, index, line, 6);
353
compareEndPosition(ts, index, line, 7);
356
QVERIFY(ts->token(index).kind == Parser::Token_SEMICOLON);
357
compareStartPosition(ts, index, line, 8);
358
compareEndPosition(ts, index, line, 8);
361
QVERIFY(ts->token(index).kind == Parser::Token_CLOSE_TAG);
362
compareStartPosition(ts, index, line, 9);
363
compareEndPosition(ts, index, line, 10);
370
void LexerTest::testHereDoc()
372
TokenStream* ts = tokenize("<?php\necho <<<EOD1\nstart $text\nend\nEOD1;\n$extern;", true);
373
QCOMPARE((int)ts->size(), 12);
375
QVERIFY(ts->token(0).kind == Parser::Token_OPEN_TAG);
376
compareStartPosition(ts, 0, 0, 0);
377
compareEndPosition(ts, 0, 0, 5);
379
QVERIFY(ts->token(1).kind == Parser::Token_ECHO);
380
compareStartPosition(ts, 1, 1, 0);
381
compareEndPosition(ts, 1, 1, 3);
383
QVERIFY(ts->token(3).kind == Parser::Token_START_HEREDOC);
384
compareStartPosition(ts, 3, 1, 5);
385
compareEndPosition(ts, 3, 1, 12);
387
QVERIFY(ts->token(4).kind == Parser::Token_ENCAPSED_AND_WHITESPACE);
388
compareStartPosition(ts, 4, 2, 0);
389
compareEndPosition(ts, 4, 2, 5);
391
QVERIFY(ts->token(5).kind == Parser::Token_VARIABLE);
392
compareStartPosition(ts, 5, 2, 6);
393
compareEndPosition(ts, 5, 2, 10);
395
QVERIFY(ts->token(6).kind == Parser::Token_ENCAPSED_AND_WHITESPACE);
396
compareStartPosition(ts, 6, 2, 11);
397
compareEndPosition(ts, 6, 3, 3);
399
QVERIFY(ts->token(7).kind == Parser::Token_END_HEREDOC);
400
compareStartPosition(ts, 7, 4, 0);
401
compareEndPosition(ts, 7, 4, 3);
403
QVERIFY(ts->token(8).kind == Parser::Token_SEMICOLON);
404
compareStartPosition(ts, 8, 4, 4);
405
compareEndPosition(ts, 8, 4, 4);
407
QVERIFY(ts->token(10).kind == Parser::Token_VARIABLE);
408
compareStartPosition(ts, 10, 5, 0);
409
compareEndPosition(ts, 10, 5, 6);
411
QVERIFY(ts->token(11).kind == Parser::Token_SEMICOLON);
412
compareStartPosition(ts, 11, 5, 7);
413
compareEndPosition(ts, 11, 5, 7);
417
void LexerTest::testCommonStringTokens()
419
// all these should have open_tag followed by constant encapsed string
420
foreach ( const QString& code, QStringList() << "<?php ''" << "<?php \"\"" << "<?php '" << "<?php \"" ) {
422
TokenStream* ts = tokenize(code, true);
424
QCOMPARE((int)ts->size(), 2);
426
QVERIFY(ts->token(0).kind == Parser::Token_OPEN_TAG);
427
compareStartPosition(ts, 0, 0, 0);
428
compareEndPosition(ts, 0, 0, 5);
430
QVERIFY(ts->token(1).kind == Parser::Token_CONSTANT_ENCAPSED_STRING);
431
compareStartPosition(ts, 1, 0, 6);
432
compareEndPosition(ts, 1, 0, code.size() - 1);
438
void LexerTest::testNonTerminatedStringWithVar()
440
TokenStream* ts = tokenize("<?php \"$a", true);
442
QCOMPARE((int)ts->size(), 3);
444
QVERIFY(ts->token(0).kind == Parser::Token_OPEN_TAG);
445
compareStartPosition(ts, 0, 0, 0);
446
compareEndPosition(ts, 0, 0, 5);
448
QVERIFY(ts->token(1).kind == Parser::Token_DOUBLE_QUOTE);
449
compareStartPosition(ts, 1, 0, 6);
450
compareEndPosition(ts, 1, 0, 6);
452
QVERIFY(ts->token(2).kind == Parser::Token_VARIABLE);
453
compareStartPosition(ts, 2, 0, 7);
454
compareEndPosition(ts, 2, 0, 8);
459
void LexerTest::testPhpBlockWithComment()
461
TokenStream* ts = tokenize(
468
QCOMPARE((int)ts->size(), 5);
470
QVERIFY(ts->token(0).kind == Parser::Token_OPEN_TAG);
471
compareStartPosition(ts, 0, 0, 0);
472
compareEndPosition(ts, 0, 0, 5);
474
QVERIFY(ts->token(1).kind == Parser::Token_COMMENT);
475
compareStartPosition(ts, 1, 1, 0);
476
compareEndPosition(ts, 1, 1, 6);
478
QVERIFY(ts->token(2).kind == Parser::Token_CLOSE_TAG);
479
compareStartPosition(ts, 2, 2, 0);
480
compareEndPosition(ts, 2, 2, 1);
482
QVERIFY(ts->token(3).kind == Parser::Token_INLINE_HTML);
483
compareStartPosition(ts, 3, 2, 2);
484
compareEndPosition(ts, 3, 2, 2);
486
QVERIFY(ts->token(4).kind == Parser::Token_OPEN_TAG);
487
compareStartPosition(ts, 4, 3, 0);
488
compareEndPosition(ts, 4, 3, 5);
491
TokenStream* LexerTest::tokenize(const QString& unit, bool debug, int initialState)
493
TokenStream* tokenStream = new TokenStream;
494
Lexer lexer(tokenStream, unit, initialState);
497
QList<Parser::Token> tokens;
498
while ((token = lexer.nextTokenKind())) {
499
Parser::Token &t = tokenStream->next();
500
t.begin = lexer.tokenBegin();
501
t.end = lexer.tokenEnd();
506
foreach(const Parser::Token &t, tokens) {
509
tokenStream->startPosition(i, &beginLine, &beginColumn);
512
tokenStream->endPosition(i, &endLine, &endColumn);
513
kDebug() << tokenText(t.kind)
514
<< unit.mid(t.begin, t.end - t.begin + 1).replace('\n', "\\n")
515
<< QString("[%0-%1] - [%2-%3]").arg(beginLine).arg(beginColumn).arg(endLine).arg(endColumn);
523
#include "lexertest.moc"