Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 #!/usr/bin/env python | |
| 2 # Copyright 2014 the V8 project authors. All rights reserved. | |
| 3 # Use of this source code is governed by a BSD-style license that can be | |
| 4 # found in the LICENSE file. | |
| 5 | |
| 6 import os | |
| 7 import re | |
| 8 import shutil | |
| 9 import sys | |
| 10 | |
| 11 # TODO(jkummerow): Support DATA_VIEW_{G,S}ETTER in runtime.cc | |
| 12 | |
| 13 FILENAME = "src/runtime.cc" | |
| 14 HEADERFILENAME = "src/runtime.h" | |
| 15 FUNCTION = re.compile("^RUNTIME_FUNCTION\(Runtime_(\w+)") | |
| 16 ARGSLENGTH = re.compile(".*ASSERT\(.*args\.length\(\) == (\d+)\);") | |
| 17 FUNCTIONEND = "}\n" | |
| 18 | |
| 19 WORKSPACE = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), "..")) | |
| 20 BASEPATH = os.path.join(WORKSPACE, "test", "mjsunit", "runtime-gen") | |
| 21 THIS_SCRIPT = os.path.relpath(sys.argv[0]) | |
| 22 | |
| 23 # Counts of functions in each detection state. These are used to assert | |
| 24 # that the parser doesn't bit-rot. Change the values as needed when you add, | |
| 25 # remove or change runtime functions, but make sure we don't lose our ability | |
| 26 # to parse them! | |
| 27 EXPECTED_FUNCTION_COUNT = 334 | |
| 28 EXPECTED_FUZZABLE_COUNT = 312 | |
| 29 EXPECTED_CCTEST_COUNT = 6 | |
| 30 EXPECTED_UNKNOWN_COUNT = 7 | |
| 31 | |
| 32 | |
| 33 # Don't call these at all. | |
| 34 BLACKLISTED = [ | |
| 35 "Abort", # Kills the process. | |
| 36 "AbortJS", # Kills the process. | |
| 37 "CompileForOnStackReplacement", # Riddled with ASSERTs. | |
| 38 "IS_VAR", # Not implemented in the runtime. | |
| 39 "ListNatives", # Not available in Release mode. | |
| 40 "SetAllocationTimeout", # Too slow for fuzzing. | |
| 41 "SystemBreak", # Kills (int3) the process. | |
| 42 | |
| 43 # TODO(jkummerow): Fix these and un-blacklist them! | |
| 44 "CreateDateTimeFormat", | |
| 45 "CreateNumberFormat", | |
| 46 ] | |
| 47 | |
| 48 | |
| 49 # These will always throw. | |
| 50 THROWS = [ | |
| 51 "CheckExecutionState", # Needs to hit a break point. | |
| 52 "CheckIsBootstrapping", # Needs to be bootstrapping. | |
| 53 "DebugEvaluate", # Needs to hit a break point. | |
| 54 "DebugEvaluateGlobal", # Needs to hit a break point. | |
| 55 "DebugIndexedInterceptorElementValue", # Needs an indexed interceptor. | |
| 56 "DebugNamedInterceptorPropertyValue", # Needs a named interceptor. | |
| 57 "DebugSetScriptSource", # Checks compilation state of script. | |
| 58 "GetAllScopesDetails", # Needs to hit a break point. | |
| 59 "GetFrameCount", # Needs to hit a break point. | |
| 60 "GetFrameDetails", # Needs to hit a break point. | |
| 61 "GetRootNaN", # Needs to be bootstrapping. | |
| 62 "GetScopeCount", # Needs to hit a break point. | |
| 63 "GetScopeDetails", # Needs to hit a break point. | |
| 64 "GetStepInPositions", # Needs to hit a break point. | |
| 65 "GetTemplateField", # Needs a {Function,Object}TemplateInfo. | |
| 66 "GetThreadCount", # Needs to hit a break point. | |
| 67 "GetThreadDetails", # Needs to hit a break point. | |
| 68 "IsAccessAllowedForObserver", # Needs access-check-required object. | |
| 69 "LiveEditFunctionSourceUpdated", # Needs a SharedFunctionInfo. | |
| 70 "LiveEditPatchFunctionPositions", # Needs a SharedFunctionInfo. | |
| 71 "LiveEditReplaceFunctionCode", # Needs a SharedFunctionInfo. | |
| 72 "LiveEditReplaceRefToNestedFunction", # Needs a SharedFunctionInfo. | |
| 73 "LiveEditRestartFrame", # Needs to hit a break point. | |
| 74 "UnblockConcurrentRecompilation" # Needs --block-concurrent-recompilation. | |
| 75 ] | |
| 76 | |
| 77 | |
| 78 # Definitions used in CUSTOM_KNOWN_GOOD_INPUT below. | |
| 79 _BREAK_ITERATOR = ( | |
| 80 "%GetImplFromInitializedIntlObject(new Intl.v8BreakIterator())") | |
| 81 _COLLATOR = "%GetImplFromInitializedIntlObject(new Intl.Collator('en-US'))" | |
| 82 _DATETIME_FORMAT = ( | |
| 83 "%GetImplFromInitializedIntlObject(new Intl.DateTimeFormat('en-US'))") | |
| 84 _NUMBER_FORMAT = ( | |
| 85 "%GetImplFromInitializedIntlObject(new Intl.NumberFormat('en-US'))") | |
| 86 _SCRIPT = "%DebugGetLoadedScripts()[1]" | |
| 87 | |
| 88 | |
| 89 # Custom definitions for function input that does not throw. | |
| 90 # Format: "FunctionName": ["arg0", "arg1", ..., argslength]. | |
| 91 # None means "fall back to autodetected value". | |
| 92 CUSTOM_KNOWN_GOOD_INPUT = { | |
| 93 "Apply": ["function() {}", None, None, None, None, None], | |
| 94 "ArrayBufferSliceImpl": [None, None, 0, None], | |
| 95 "ArrayConcat": ["[1, 'a']", None], | |
| 96 "BreakIteratorAdoptText": [_BREAK_ITERATOR, None, None], | |
| 97 "BreakIteratorBreakType": [_BREAK_ITERATOR, None], | |
| 98 "BreakIteratorCurrent": [_BREAK_ITERATOR, None], | |
| 99 "BreakIteratorFirst": [_BREAK_ITERATOR, None], | |
| 100 "BreakIteratorNext": [_BREAK_ITERATOR, None], | |
| 101 "CompileString": [None, "false", None], | |
| 102 "CreateBreakIterator": ["'en-US'", "{type: 'string'}", None, None], | |
| 103 "CreateJSFunctionProxy": [None, "function() {}", None, None, None], | |
| 104 "CreatePrivateSymbol": ["\"foo\"", None], | |
| 105 "CreateSymbol": ["\"foo\"", None], | |
| 106 "DateParseString": [None, "new Array(8)", None], | |
| 107 "DebugSetScriptSource": [_SCRIPT, None, None], | |
| 108 "DefineOrRedefineAccessorProperty": [None, None, "function() {}", | |
| 109 "function() {}", 2, None], | |
| 110 "GetBreakLocations": [None, 0, None], | |
| 111 "GetDefaultReceiver": ["function() {}", None], | |
| 112 "GetImplFromInitializedIntlObject": ["new Intl.NumberFormat('en-US')", None], | |
| 113 "InternalCompare": [_COLLATOR, None, None, None], | |
| 114 "InternalDateFormat": [_DATETIME_FORMAT, None, None], | |
| 115 "InternalDateParse": [_DATETIME_FORMAT, None, None], | |
| 116 "InternalNumberFormat": [_NUMBER_FORMAT, None, None], | |
| 117 "InternalNumberParse": [_NUMBER_FORMAT, None, None], | |
| 118 "IsSloppyModeFunction": ["function() {}", None], | |
| 119 "LiveEditFindSharedFunctionInfosForScript": [_SCRIPT, None], | |
| 120 "LiveEditGatherCompileInfo": [_SCRIPT, None, None], | |
| 121 "LiveEditReplaceScript": [_SCRIPT, None, None, None], | |
| 122 "LoadMutableDouble": ["{foo: 1.2}", None, None], | |
| 123 "NewObjectFromBound": ["(function() {}).bind({})", None], | |
| 124 "NumberToRadixString": [None, "2", None], | |
| 125 "ParseJson": ["\"{}\"", 1], | |
| 126 "SetAccessorProperty": [None, None, "undefined", "undefined", None, None, | |
| 127 None], | |
| 128 "SetCreateIterator": [None, "2", None], | |
| 129 "SetDebugEventListener": ["undefined", None, None], | |
| 130 "SetScriptBreakPoint": [_SCRIPT, None, 0, None, None], | |
| 131 "StringBuilderConcat": ["[1, 2, 3]", 3, None, None], | |
| 132 "StringBuilderJoin": ["['a', 'b']", 4, None, None], | |
| 133 "TypedArrayInitialize": [None, None, "new ArrayBuffer(8)", None, None, None], | |
| 134 "TypedArraySetFastCases": [None, None, "0", None], | |
| 135 } | |
| 136 | |
| 137 | |
| 138 # Types of arguments that cannot be generated in a JavaScript testcase. | |
| 139 NON_JS_TYPES = [ | |
| 140 "Code", "Context", "FixedArray", "FunctionTemplateInfo", | |
| 141 "JSFunctionResultCache", "JSMessageObject", "Map", "ScopeInfo", | |
| 142 "SharedFunctionInfo"] | |
| 143 | |
| 144 | |
| 145 # Maps argument types to concrete example inputs of that type. | |
| 146 JS_TYPE_GENERATORS = { | |
| 147 "Boolean": "true", | |
| 148 "HeapObject": "new Object()", | |
| 149 "Int32": "32", | |
| 150 "JSArray": "new Array()", | |
| 151 "JSArrayBuffer": "new ArrayBuffer(8)", | |
| 152 "JSDataView": "new DataView(new ArrayBuffer(8))", | |
| 153 "JSDate": "new Date()", | |
| 154 "JSFunction": "function() {}", | |
| 155 "JSFunctionProxy": "Proxy.createFunction({}, function() {})", | |
| 156 "JSGeneratorObject": "(function*(){ yield 1; })()", | |
| 157 "JSMap": "new Map()", | |
| 158 "JSMapIterator": "%MapCreateIterator(new Map(), 3)", | |
| 159 "JSObject": "new Object()", | |
| 160 "JSProxy": "Proxy.create({})", | |
| 161 "JSReceiver": "new Object()", | |
| 162 "JSRegExp": "/ab/g", | |
| 163 "JSSet": "new Set()", | |
| 164 "JSSetIterator": "%SetCreateIterator(new Set(), 2)", | |
| 165 "JSTypedArray": "new Int32Array(2)", | |
| 166 "JSValue": "new String('foo')", | |
| 167 "JSWeakCollection": "new WeakMap()", | |
| 168 "Name": "\"name\"", | |
| 169 "Number": "1.5", | |
| 170 "Object": "new Object()", | |
| 171 "PropertyDetails": "513", | |
| 172 "SeqString": "\"seqstring\"", | |
| 173 "Smi": 1, | |
| 174 "StrictMode": "1", | |
| 175 "String": "\"foo\"", | |
| 176 "Symbol": "Symbol(\"symbol\")", | |
| 177 "Uint32": "32", | |
| 178 } | |
| 179 | |
| 180 | |
| 181 class ArgParser(object): | |
| 182 def __init__(self, regex, ctor): | |
| 183 self.regex = regex | |
| 184 self.ArgCtor = ctor | |
| 185 | |
| 186 | |
| 187 class Arg(object): | |
| 188 def __init__(self, typename, varname, index): | |
| 189 self.type = typename | |
| 190 self.name = "_%s" % varname | |
| 191 self.index = index | |
| 192 | |
| 193 | |
| 194 class Function(object): | |
| 195 def __init__(self, match): | |
| 196 self.name = match.group(1) | |
| 197 self.argslength = -1 | |
| 198 self.args = {} | |
| 199 self.inline = "" | |
| 200 | |
| 201 handle_arg_parser = ArgParser( | |
| 202 re.compile("^\s*CONVERT_ARG_HANDLE_CHECKED\((\w+), (\w+), (\d+)\)"), | |
| 203 lambda match: Arg(match.group(1), match.group(2), int(match.group(3)))) | |
| 204 | |
| 205 plain_arg_parser = ArgParser( | |
| 206 re.compile("^\s*CONVERT_ARG_CHECKED\((\w+), (\w+), (\d+)\)"), | |
| 207 lambda match: Arg(match.group(1), match.group(2), int(match.group(3)))) | |
| 208 | |
| 209 number_handle_arg_parser = ArgParser( | |
| 210 re.compile("^\s*CONVERT_NUMBER_ARG_HANDLE_CHECKED\((\w+), (\d+)\)"), | |
| 211 lambda match: Arg("Number", match.group(1), int(match.group(2)))) | |
| 212 | |
| 213 smi_arg_parser = ArgParser( | |
| 214 re.compile("^\s*CONVERT_SMI_ARG_CHECKED\((\w+), (\d+)\)"), | |
| 215 lambda match: Arg("Smi", match.group(1), int(match.group(2)))) | |
| 216 | |
| 217 double_arg_parser = ArgParser( | |
| 218 re.compile("^\s*CONVERT_DOUBLE_ARG_CHECKED\((\w+), (\d+)\)"), | |
| 219 lambda match: Arg("Number", match.group(1), int(match.group(2)))) | |
| 220 | |
| 221 number_arg_parser = ArgParser( | |
| 222 re.compile( | |
| 223 "^\s*CONVERT_NUMBER_CHECKED\(\w+, (\w+), (\w+), args\[(\d+)\]\)"), | |
| 224 lambda match: Arg(match.group(2), match.group(1), int(match.group(3)))) | |
| 225 | |
| 226 strict_mode_arg_parser = ArgParser( | |
| 227 re.compile("^\s*CONVERT_STRICT_MODE_ARG_CHECKED\((\w+), (\d+)\)"), | |
| 228 lambda match: Arg("StrictMode", match.group(1), int(match.group(2)))) | |
| 229 | |
| 230 boolean_arg_parser = ArgParser( | |
| 231 re.compile("^\s*CONVERT_BOOLEAN_ARG_CHECKED\((\w+), (\d+)\)"), | |
| 232 lambda match: Arg("Boolean", match.group(1), int(match.group(2)))) | |
| 233 | |
| 234 property_details_parser = ArgParser( | |
| 235 re.compile("^\s*CONVERT_PROPERTY_DETAILS_CHECKED\((\w+), (\d+)\)"), | |
| 236 lambda match: Arg("PropertyDetails", match.group(1), int(match.group(2)))) | |
| 237 | |
| 238 arg_parsers = [handle_arg_parser, plain_arg_parser, number_handle_arg_parser, | |
| 239 smi_arg_parser, | |
| 240 double_arg_parser, number_arg_parser, strict_mode_arg_parser, | |
| 241 boolean_arg_parser, property_details_parser] | |
| 242 | |
| 243 | |
| 244 def SetArgsLength(self, match): | |
| 245 self.argslength = int(match.group(1)) | |
| 246 | |
| 247 def TryParseArg(self, line): | |
| 248 for parser in Function.arg_parsers: | |
| 249 match = parser.regex.match(line) | |
| 250 if match: | |
| 251 arg = parser.ArgCtor(match) | |
| 252 self.args[arg.index] = arg | |
| 253 return True | |
| 254 return False | |
| 255 | |
| 256 def Filename(self): | |
| 257 return "%s.js" % self.name.lower() | |
|
Dmitry Lomov (no reviews)
2014/04/25 12:54:34
Suggestion: why lowercase names for tests? It's ok
Jakob Kummerow
2014/05/07 13:07:54
I don't care much either way, but http://google-st
| |
| 258 | |
| 259 def __str__(self): | |
| 260 s = [self.name, "("] | |
| 261 argcount = self.argslength | |
| 262 if argcount < 0: | |
| 263 print("WARNING: unknown argslength for function %s" % self.name) | |
| 264 if self.args: | |
| 265 argcount = max([self.args[i].index + 1 for i in self.args]) | |
| 266 else: | |
| 267 argcount = 0 | |
| 268 for i in range(argcount): | |
| 269 if i > 0: s.append(", ") | |
| 270 s.append(self.args[i].type if i in self.args else "<unknown>") | |
| 271 s.append(")") | |
| 272 return "".join(s) | |
| 273 | |
| 274 # Parses HEADERFILENAME to find out which runtime functions are "inline". | |
| 275 def FindInlineRuntimeFunctions(): | |
| 276 inline_functions = [] | |
| 277 with open(HEADERFILENAME, "r") as f: | |
| 278 inline_list = "#define INLINE_FUNCTION_LIST(F) \\\n" | |
| 279 inline_opt_list = "#define INLINE_OPTIMIZED_FUNCTION_LIST(F) \\\n" | |
| 280 inline_function = re.compile(r"^\s*F\((\w+), \d+, \d+\)\s*\\") | |
| 281 mode = "SEARCHING" | |
| 282 for line in f: | |
| 283 if mode == "ACTIVE": | |
| 284 match = inline_function.match(line) | |
| 285 if match: | |
| 286 inline_functions.append(match.group(1)) | |
| 287 if not line.endswith("\\\n"): | |
| 288 mode = "SEARCHING" | |
| 289 elif mode == "SEARCHING": | |
| 290 if line == inline_list or line == inline_opt_list: | |
| 291 mode = "ACTIVE" | |
| 292 return inline_functions | |
| 293 | |
| 294 | |
| 295 # Detects runtime functions by parsing FILENAME. | |
| 296 def FindRuntimeFunctions(): | |
| 297 inline_functions = FindInlineRuntimeFunctions() | |
| 298 functions = [] | |
| 299 with open(FILENAME, "r") as f: | |
| 300 function = None | |
| 301 partial_line = "" | |
| 302 for line in f: | |
| 303 # Multi-line definition support, ignoring macros. | |
| 304 if line.startswith("RUNTIME_FUNCTION") and not line.endswith("{\n"): | |
| 305 if line.endswith("\\\n"): continue | |
| 306 partial_line = line.rstrip() | |
| 307 continue | |
| 308 if partial_line: | |
| 309 partial_line += " " + line.strip() | |
| 310 if partial_line.endswith("{"): | |
| 311 line = partial_line | |
| 312 partial_line = "" | |
| 313 else: | |
| 314 continue | |
| 315 | |
| 316 match = FUNCTION.match(line) | |
| 317 if match: | |
| 318 function = Function(match) | |
| 319 if function.name in inline_functions: | |
| 320 function.inline = "_" | |
| 321 continue | |
| 322 if function is None: continue | |
| 323 | |
| 324 match = ARGSLENGTH.match(line) | |
| 325 if match: | |
| 326 function.SetArgsLength(match) | |
| 327 continue | |
| 328 | |
| 329 if function.TryParseArg(line): | |
| 330 continue | |
| 331 | |
| 332 if line == FUNCTIONEND: | |
| 333 if function is not None: | |
| 334 functions.append(function) | |
| 335 function = None | |
| 336 return functions | |
| 337 | |
| 338 # Classifies runtime functions. | |
| 339 def ClassifyFunctions(functions): | |
| 340 # Can be fuzzed with a JavaScript testcase. | |
| 341 js_fuzzable_functions = [] | |
| 342 # We have enough information to fuzz these, but they need inputs that | |
| 343 # cannot be created or passed around in JavaScript. | |
| 344 cctest_fuzzable_functions = [] | |
| 345 # This script does not have enough information about these. | |
| 346 unknown_functions = [] | |
| 347 | |
| 348 types = {} | |
| 349 for f in functions: | |
| 350 if f.name in BLACKLISTED: | |
| 351 continue | |
| 352 decision = js_fuzzable_functions | |
| 353 custom = CUSTOM_KNOWN_GOOD_INPUT.get(f.name, None) | |
| 354 if f.argslength < 0: | |
| 355 # Unknown length -> give up unless there's a custom definition. | |
| 356 if custom and custom[-1] is not None: | |
| 357 f.argslength = custom[-1] | |
| 358 assert len(custom) == f.argslength + 1, \ | |
| 359 ("%s: last custom definition must be argslength" % f.name) | |
| 360 else: | |
| 361 decision = unknown_functions | |
| 362 else: | |
| 363 if custom: | |
| 364 # Any custom definitions must match the known argslength. | |
| 365 assert len(custom) == f.argslength + 1, \ | |
| 366 ("%s should have %d custom definitions but has %d" % | |
| 367 (f.name, f.argslength + 1, len(custom))) | |
| 368 for i in range(f.argslength): | |
| 369 if custom and custom[i] is not None: | |
| 370 # All good, there's a custom definition. | |
| 371 pass | |
| 372 elif not i in f.args: | |
| 373 # No custom definition and no parse result -> give up. | |
| 374 decision = unknown_functions | |
| 375 else: | |
| 376 t = f.args[i].type | |
| 377 if t in NON_JS_TYPES: | |
| 378 decision = cctest_fuzzable_functions | |
| 379 else: | |
| 380 assert t in JS_TYPE_GENERATORS, \ | |
| 381 ("type generator not found for %s, function: %s" % (t, f)) | |
| 382 decision.append(f) | |
| 383 return (js_fuzzable_functions, cctest_fuzzable_functions, unknown_functions) | |
| 384 | |
| 385 | |
| 386 def GenerateJSTestcaseForFunction(f): | |
| 387 s = ["// Copyright 2014 the V8 project authors. All rights reserved.", | |
| 388 "// AUTO-GENERATED BY tools/generate-runtime-tests.py, DO NOT MODIFY", | |
| 389 "// Flags: --allow-natives-syntax --harmony"] | |
| 390 call = "%%%s%s(" % (f.inline, f.name) | |
| 391 custom = CUSTOM_KNOWN_GOOD_INPUT.get(f.name, None) | |
| 392 for i in range(f.argslength): | |
| 393 if custom and custom[i] is not None: | |
| 394 (name, value) = ("arg%d" % i, custom[i]) | |
| 395 else: | |
| 396 arg = f.args[i] | |
| 397 (name, value) = (arg.name, JS_TYPE_GENERATORS[arg.type]) | |
| 398 s.append("var %s = %s;" % (name, value)) | |
| 399 if i > 0: call += ", " | |
| 400 call += name | |
| 401 call += ");" | |
| 402 if f.name in THROWS: | |
| 403 s.append("try {") | |
| 404 s.append(call); | |
| 405 s.append("} catch(e) {}") | |
| 406 else: | |
| 407 s.append(call) | |
| 408 testcase = "\n".join(s) | |
| 409 path = os.path.join(BASEPATH, f.Filename()) | |
| 410 with open(path, "w") as f: | |
| 411 f.write("%s\n" % testcase) | |
| 412 | |
| 413 def GenerateTestcases(functions): | |
| 414 shutil.rmtree(BASEPATH) # Re-generate everything. | |
| 415 os.makedirs(BASEPATH) | |
| 416 for f in functions: | |
| 417 GenerateJSTestcaseForFunction(f) | |
| 418 | |
| 419 def PrintUsage(): | |
| 420 print """Usage: %(this_script)s ACTION | |
| 421 | |
| 422 where ACTION can be: | |
| 423 | |
| 424 info Print diagnostic info. | |
| 425 check Check that runtime functions can be parsed as expected, and that | |
| 426 test cases exist. | |
| 427 generate Parse source code for runtime functions, and auto-generate | |
| 428 test cases for them. Warning: this will nuke and re-create | |
| 429 %(path)s. | |
| 430 """ % {"path": os.path.relpath(BASEPATH), "this_script": THIS_SCRIPT} | |
| 431 | |
| 432 if __name__ == "__main__": | |
| 433 if len(sys.argv) != 2: | |
| 434 PrintUsage() | |
| 435 sys.exit(1) | |
| 436 action = sys.argv[1] | |
| 437 if action in ["-h", "--help", "help"]: | |
| 438 PrintUsage() | |
| 439 sys.exit(0) | |
| 440 | |
| 441 functions = FindRuntimeFunctions() | |
| 442 (js_fuzzable_functions, cctest_fuzzable_functions, unknown_functions) = \ | |
| 443 ClassifyFunctions(functions) | |
| 444 | |
| 445 if action == "info": | |
| 446 print("%d functions total; js_fuzzable_functions: %d, " | |
| 447 "cctest_fuzzable_functions: %d, unknown_functions: %d" | |
| 448 % (len(functions), len(js_fuzzable_functions), | |
| 449 len(cctest_fuzzable_functions), len(unknown_functions))) | |
| 450 print("unknown functions:") | |
| 451 for f in unknown_functions: | |
| 452 print(f) | |
| 453 sys.exit(0) | |
| 454 | |
| 455 if action == "check": | |
| 456 error = False | |
| 457 def CheckCount(actual, expected, description): | |
| 458 global error | |
| 459 if len(actual) != expected: | |
| 460 print("Expected to detect %d %s, but found %d." % ( | |
| 461 expected, description, len(actual))) | |
| 462 error = True | |
| 463 CheckCount(functions, EXPECTED_FUNCTION_COUNT, "functions in total") | |
| 464 CheckCount(js_fuzzable_functions, EXPECTED_FUZZABLE_COUNT, | |
| 465 "JavaScript-fuzzable functions") | |
| 466 CheckCount(cctest_fuzzable_functions, EXPECTED_CCTEST_COUNT, | |
| 467 "cctest-fuzzable functions") | |
| 468 CheckCount(unknown_functions, EXPECTED_UNKNOWN_COUNT, | |
| 469 "functions with incomplete type information") | |
| 470 | |
| 471 def CheckTestcasesExisting(functions): | |
| 472 global error | |
| 473 for f in functions: | |
| 474 if not os.path.isfile(os.path.join(BASEPATH, f.Filename())): | |
| 475 print("Missing testcase for %s, please run '%s generate'" % | |
|
Dmitry Lomov (no reviews)
2014/04/25 12:54:34
Suggestion: would also be awesome to check if file
Jakob Kummerow
2014/05/07 13:07:54
Yeah, that's a possible follow-up change.
| |
| 476 (f.name, THIS_SCRIPT)) | |
| 477 error = True | |
| 478 files = os.listdir(BASEPATH) | |
| 479 if (len(files) != len(functions)): | |
| 480 unexpected_files = set(files) - set([f.Filename() for f in functions]) | |
| 481 for f in unexpected_files: | |
| 482 print("Unexpected testcase: %s" % os.path.join(BASEPATH, f)) | |
| 483 error = True | |
| 484 CheckTestcasesExisting(js_fuzzable_functions) | |
| 485 | |
| 486 if error: | |
| 487 sys.exit(1) | |
| 488 print("All good.") | |
| 489 sys.exit(0) | |
| 490 | |
| 491 if action == "generate": | |
| 492 GenerateTestcases(js_fuzzable_functions) | |
| 493 sys.exit(0) | |
| OLD | NEW |