source: webkit/trunk/Source/JavaScriptCore/testRegExp.cpp@ 94644

Last change on this file since 94644 was 94470, checked in by [email protected], 14 years ago

Add JSC:RegExp functional tests
https://bugs.webkit.org/show_bug.cgi?id=67339

Source/JavaScriptCore:

Added new test driver program (testRegExp) and corresponding data file
along with build scripts changes.

Reviewed by Gavin Barraclough.

(Options::Options):
(StopWatch::start):
(StopWatch::stop):
(StopWatch::getElapsedMS):
(RegExpTest::RegExpTest):
(GlobalObject::create):
(GlobalObject::className):
(GlobalObject::GlobalObject):
(main):
(cleanupGlobalData):
(testOneRegExp):
(scanString):
(parseRegExpLine):
(parseTestLine):
(runFromFiles):
(printUsageStatement):
(parseArguments):
(realMain):

  • tests/regexp: Added.
  • tests/regexp/RegExpTest.data: Added.

Tools:

New perl script to build JavaScriptCore and run the RegExp tests.

Reviewed by Gavin Barraclough.

  • Scripts/run-regexp-tests: Added.
File size: 13.9 KB
Line 
1/*
2 * Copyright (C) 2011 Apple Inc. All rights reserved.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
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.
13 *
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.
18 *
19 */
20
21#include "config.h"
22#include "RegExp.h"
23
24#include "CurrentTime.h"
25#include "InitializeThreading.h"
26#include "JSGlobalObject.h"
27#include "UStringBuilder.h"
28#include <errno.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32
33#if !OS(WINDOWS)
34#include <unistd.h>
35#endif
36
37#if HAVE(SYS_TIME_H)
38#include <sys/time.h>
39#endif
40
41#if COMPILER(MSVC) && !OS(WINCE)
42#include <crtdbg.h>
43#include <mmsystem.h>
44#include <windows.h>
45#endif
46
47#if PLATFORM(QT)
48#include <QCoreApplication>
49#include <QDateTime>
50#endif
51
52const int MaxLineLength = 100 * 1024;
53
54using namespace JSC;
55using namespace WTF;
56
57static void cleanupGlobalData(JSGlobalData*);
58
59struct Options {
60 Options()
61 : interactive(false)
62 , verbose(false)
63 {
64 }
65
66 bool interactive;
67 bool verbose;
68 Vector<UString> arguments;
69 Vector<UString> files;
70};
71
72class StopWatch {
73public:
74 void start();
75 void stop();
76 long getElapsedMS(); // call stop() first
77
78private:
79 double m_startTime;
80 double m_stopTime;
81};
82
83void StopWatch::start()
84{
85 m_startTime = currentTime();
86}
87
88void StopWatch::stop()
89{
90 m_stopTime = currentTime();
91}
92
93long StopWatch::getElapsedMS()
94{
95 return static_cast<long>((m_stopTime - m_startTime) * 1000);
96}
97
98struct RegExpTest {
99 RegExpTest()
100 : offset(0)
101 , result(0)
102 {
103 }
104
105 UString subject;
106 int offset;
107 int result;
108 Vector<int, 32> expectVector;
109};
110
111class GlobalObject : public JSGlobalObject {
112private:
113 GlobalObject(JSGlobalData&, Structure*, const Vector<UString>& arguments);
114
115public:
116 typedef JSGlobalObject Base;
117
118 static GlobalObject* create(JSGlobalData& globalData, Structure* structure, const Vector<UString>& arguments)
119 {
120 return new (allocateCell<GlobalObject>(globalData.heap)) GlobalObject(globalData, structure, arguments);
121 }
122 virtual UString className() const { return "global"; }
123};
124
125COMPILE_ASSERT(!IsInteger<GlobalObject>::value, WTF_IsInteger_GlobalObject_false);
126ASSERT_CLASS_FITS_IN_CELL(GlobalObject);
127
128GlobalObject::GlobalObject(JSGlobalData& globalData, Structure* structure, const Vector<UString>& arguments)
129 : JSGlobalObject(globalData, structure)
130{
131 UNUSED_PARAM(arguments);
132}
133
134// Use SEH for Release builds only to get rid of the crash report dialog
135// (luckily the same tests fail in Release and Debug builds so far). Need to
136// be in a separate main function because the realMain function requires object
137// unwinding.
138
139#if COMPILER(MSVC) && !COMPILER(INTEL) && !defined(_DEBUG) && !OS(WINCE)
140#define TRY __try {
141#define EXCEPT(x) } __except (EXCEPTION_EXECUTE_HANDLER) { x; }
142#else
143#define TRY
144#define EXCEPT(x)
145#endif
146
147int realMain(int argc, char** argv, JSGlobalData*);
148
149int main(int argc, char** argv)
150{
151#if OS(WINDOWS)
152#if !OS(WINCE)
153 // Cygwin calls ::SetErrorMode(SEM_FAILCRITICALERRORS), which we will inherit. This is bad for
154 // testing/debugging, as it causes the post-mortem debugger not to be invoked. We reset the
155 // error mode here to work around Cygwin's behavior. See <http://webkit.org/b/55222>.
156 ::SetErrorMode(0);
157#endif
158
159#if defined(_DEBUG)
160 _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
161 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
162 _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
163 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
164 _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
165 _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
166#endif
167
168 timeBeginPeriod(1);
169#endif
170
171#if PLATFORM(QT)
172 QCoreApplication app(argc, argv);
173#endif
174
175 // Initialize JSC before getting JSGlobalData.
176 JSC::initializeThreading();
177
178 // We can't use destructors in the following code because it uses Windows
179 // Structured Exception Handling
180 int res = 0;
181 JSGlobalData* globalData = JSGlobalData::create(ThreadStackTypeLarge, LargeHeap).leakRef();
182 TRY
183 res = realMain(argc, argv, globalData);
184 EXCEPT(res = 3)
185
186 cleanupGlobalData(globalData);
187 return res;
188}
189
190static void cleanupGlobalData(JSGlobalData* globalData)
191{
192 JSLock lock(SilenceAssertionsOnly);
193 globalData->clearBuiltinStructures();
194 globalData->heap.destroy();
195 globalData->deref();
196}
197
198static bool testOneRegExp(JSGlobalData& globalData, RegExp* regexp, RegExpTest* regExpTest, bool verbose, unsigned int lineNumber)
199{
200 bool result = true;
201 Vector<int, 32> outVector;
202 outVector.resize(regExpTest->expectVector.size());
203 int matchResult = regexp->match(globalData, regExpTest->subject, regExpTest->offset, &outVector);
204
205 if (matchResult != regExpTest->result) {
206 result = false;
207 if (verbose)
208 printf("Line %d: results mismatch - expected %d got %d\n", lineNumber, regExpTest->result, matchResult);
209 } else if (matchResult != -1) {
210 if (outVector.size() != regExpTest->expectVector.size()) {
211 result = false;
212 if (verbose)
213 printf("Line %d: output vector size mismatch - expected %lu got %lu\n", lineNumber, regExpTest->expectVector.size(), outVector.size());
214 } else if (outVector.size() % 2) {
215 result = false;
216 if (verbose)
217 printf("Line %d: output vector size is odd (%lu), should be even\n", lineNumber, outVector.size());
218 } else {
219 // Check in pairs since the first value of the pair could be -1 in which case the second doesn't matter.
220 size_t pairCount = outVector.size() / 2;
221 for (size_t i = 0; i < pairCount; ++i) {
222 size_t startIndex = i*2;
223 if (outVector[startIndex] != regExpTest->expectVector[startIndex]) {
224 result = false;
225 if (verbose)
226 printf("Line %d: output vector mismatch at index %lu - expected %d got %d\n", lineNumber, startIndex, regExpTest->expectVector[startIndex], outVector[startIndex]);
227 }
228 if ((i > 0) && (regExpTest->expectVector[startIndex] != -1) && (outVector[startIndex+1] != regExpTest->expectVector[startIndex+1])) {
229 result = false;
230 if (verbose)
231 printf("Line %d: output vector mismatch at index %lu - expected %d got %d\n", lineNumber, startIndex+1, regExpTest->expectVector[startIndex+1], outVector[startIndex+1]);
232 }
233 }
234 }
235 }
236
237 return result;
238}
239
240static int scanString(char* buffer, int bufferLength, UStringBuilder& builder, char termChar)
241{
242 bool escape = false;
243
244 for (int i = 0; i < bufferLength; ++i) {
245 UChar c = buffer[i];
246
247 if (escape) {
248 switch (c) {
249 case '0':
250 c = '\0';
251 break;
252 case 'a':
253 c = '\a';
254 break;
255 case 'b':
256 c = '\b';
257 break;
258 case 'f':
259 c = '\f';
260 break;
261 case 'n':
262 c = '\n';
263 break;
264 case 'r':
265 c = '\r';
266 break;
267 case 't':
268 c = '\t';
269 break;
270 case 'v':
271 c = '\v';
272 break;
273 case '\\':
274 c = '\\';
275 break;
276 case '?':
277 c = '\?';
278 break;
279 case 'u':
280 if ((i + 4) >= bufferLength)
281 return -1;
282 unsigned int charValue;
283 if (sscanf(buffer+i+1, "%04x", &charValue) != 1)
284 return -1;
285 c = static_cast<UChar>(charValue);
286 i += 4;
287 break;
288 }
289
290 builder.append(c);
291 escape = false;
292 } else {
293 if (c == termChar)
294 return i;
295
296 if (c == '\\')
297 escape = true;
298 else
299 builder.append(c);
300 }
301 }
302
303 return -1;
304}
305
306static RegExp* parseRegExpLine(JSGlobalData& globalData, char* line, int lineLength)
307{
308 UStringBuilder pattern;
309
310 if (line[0] != '/')
311 return 0;
312
313 int i = scanString(line + 1, lineLength - 1, pattern, '/') + 1;
314
315 if ((i >= lineLength) || (line[i] != '/'))
316 return 0;
317
318 ++i;
319
320 return RegExp::create(globalData, pattern.toUString(), regExpFlags(line + i));
321}
322
323static RegExpTest* parseTestLine(char* line, int lineLength)
324{
325 UStringBuilder subjectString;
326
327 if ((line[0] != ' ') || (line[1] != '"'))
328 return 0;
329
330 int i = scanString(line + 2, lineLength - 2, subjectString, '"') + 2;
331
332 if ((i >= (lineLength - 2)) || (line[i] != '"') || (line[i+1] != ',') || (line[i+2] != ' '))
333 return 0;
334
335 i += 3;
336
337 int offset;
338
339 if (sscanf(line + i, "%d, ", &offset) != 1)
340 return 0;
341
342 while (line[i] && line[i] != ' ')
343 ++i;
344
345 ++i;
346
347 int matchResult;
348
349 if (sscanf(line + i, "%d, ", &matchResult) != 1)
350 return 0;
351
352 while (line[i] && line[i] != ' ')
353 ++i;
354
355 ++i;
356
357 if (line[i++] != '(')
358 return 0;
359
360 int start, end;
361
362 RegExpTest* result = new RegExpTest();
363
364 result->subject = subjectString.toUString();
365 result->offset = offset;
366 result->result = matchResult;
367
368 while (line[i] && line[i] != ')') {
369 if (sscanf(line + i, "%d, %d", &start, &end) != 2) {
370 delete result;
371 return 0;
372 }
373
374 result->expectVector.append(start);
375 result->expectVector.append(end);
376
377 while (line[i] && (line[i] != ',') && (line[i] != ')'))
378 i++;
379 i++;
380 while (line[i] && (line[i] != ',') && (line[i] != ')'))
381 i++;
382
383 if (line[i] == ')')
384 break;
385 if (!line[i] || (line[i] != ',')) {
386 delete result;
387 return 0;
388 }
389 i++;
390 }
391
392 return result;
393}
394
395static bool runFromFiles(GlobalObject* globalObject, const Vector<UString>& files, bool verbose)
396{
397 UString script;
398 UString fileName;
399 Vector<char> scriptBuffer;
400 unsigned tests = 0;
401 unsigned failures = 0;
402 char* lineBuffer = new char[MaxLineLength + 1];
403
404 JSGlobalData& globalData = globalObject->globalData();
405
406 bool success = true;
407 for (size_t i = 0; i < files.size(); i++) {
408 FILE* testCasesFile = fopen(files[i].utf8().data(), "rb");
409
410 if (!testCasesFile) {
411 printf("Unable to open test data file \"%s\"\n", files[i].utf8().data());
412 continue;
413 }
414
415 RegExp* regexp = 0;
416 size_t lineLength = 0;
417 char* linePtr = 0;
418 unsigned int lineNumber = 0;
419
420 while ((linePtr = fgets(&lineBuffer[0], MaxLineLength, testCasesFile))) {
421 lineLength = strlen(linePtr);
422 if (linePtr[lineLength - 1] == '\n') {
423 linePtr[lineLength - 1] = '\0';
424 --lineLength;
425 }
426 ++lineNumber;
427
428 if (linePtr[0] == '#')
429 continue;
430
431 if (linePtr[0] == '/') {
432 regexp = parseRegExpLine(globalData, linePtr, lineLength);
433 } else if (linePtr[0] == ' ') {
434 RegExpTest* regExpTest = parseTestLine(linePtr, lineLength);
435
436 if (regexp && regExpTest) {
437 ++tests;
438 if (!testOneRegExp(globalData, regexp, regExpTest, verbose, lineNumber)) {
439 failures++;
440 printf("Failure on line %u\n", lineNumber);
441 }
442 }
443
444 if (regExpTest)
445 delete regExpTest;
446 }
447 }
448
449 fclose(testCasesFile);
450 }
451
452 if (failures)
453 printf("%u tests run, %u failures\n", tests, failures);
454 else
455 printf("%u tests passed\n", tests);
456
457 delete[] lineBuffer;
458
459 globalData.dumpSampleData(globalObject->globalExec());
460#if ENABLE(REGEXP_TRACING)
461 globalData.dumpRegExpTrace();
462#endif
463 return success;
464}
465
466#define RUNNING_FROM_XCODE 0
467
468static NO_RETURN void printUsageStatement(JSGlobalData* globalData, bool help = false)
469{
470 fprintf(stderr, "Usage: regexp_test [options] file\n");
471 fprintf(stderr, " -h|--help Prints this help message\n");
472 fprintf(stderr, " -v|--verbose Verbose output\n");
473
474 cleanupGlobalData(globalData);
475 exit(help ? EXIT_SUCCESS : EXIT_FAILURE);
476}
477
478static void parseArguments(int argc, char** argv, Options& options, JSGlobalData* globalData)
479{
480 int i = 1;
481 for (; i < argc; ++i) {
482 const char* arg = argv[i];
483 if (!strcmp(arg, "-h") || !strcmp(arg, "--help"))
484 printUsageStatement(globalData, true);
485 if (!strcmp(arg, "-v") || !strcmp(arg, "--verbose"))
486 options.verbose = true;
487 else
488 options.files.append(argv[i]);
489 }
490
491 for (; i < argc; ++i)
492 options.arguments.append(argv[i]);
493}
494
495int realMain(int argc, char** argv, JSGlobalData* globalData)
496{
497 JSLock lock(SilenceAssertionsOnly);
498
499 Options options;
500 parseArguments(argc, argv, options, globalData);
501
502 GlobalObject* globalObject = GlobalObject::create(*globalData, GlobalObject::createStructure(*globalData, jsNull()), options.arguments);
503 bool success = runFromFiles(globalObject, options.files, options.verbose);
504
505 return success ? 0 : 3;
506}
Note: See TracBrowser for help on using the repository browser.