skulpt.py 49 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464
  1. #!/usr/bin/env python2.7
  2. #
  3. # Note: python2.6 is specified because that is what the skulpt parser
  4. # used as a reference. This is only important when you are doing
  5. # things like regenerating tests and/or regenerating symtabs
  6. # If you do not have python 2.6 and you ARE NOT creating new tests
  7. # then all should be well for you to use 2.7 or whatever you have around
  8. from optparse import OptionParser
  9. from subprocess import Popen, PIPE
  10. import subprocess
  11. import os
  12. import sys
  13. import glob
  14. import py_compile
  15. import symtable
  16. import shutil
  17. import re
  18. import pprint
  19. import json
  20. import shutil
  21. import time
  22. # Assume that the GitPython module is available until proven otherwise.
  23. GIT_MODULE_AVAILABLE = False
  24. try:
  25. from git import *
  26. except:
  27. GIT_MODULE_AVAILABLE = False
  28. def bowerFileName():
  29. file = open(".bowerrc")
  30. data = json.load(file)
  31. fileName = data["json"]
  32. file.close()
  33. return fileName
  34. def bowerProperty(name):
  35. file = open(bowerFileName())
  36. data = json.load(file)
  37. value = data[name]
  38. file.close()
  39. return value
  40. # Symbolic constants for the project structure.
  41. DIST_DIR = 'dist'
  42. TEST_DIR = 'test'
  43. RUN_DIR = 'support/tmp'
  44. # Symbolic constants for the naming of distribution files.
  45. STANDARD_NAMING = True
  46. PRODUCT_NAME = bowerProperty("name")
  47. OUTFILE_REG = "{0}.js".format(PRODUCT_NAME) if STANDARD_NAMING else "skulpt-uncomp.js"
  48. OUTFILE_MIN = "{0}.min.js".format(PRODUCT_NAME) if STANDARD_NAMING else "skulpt.js"
  49. OUTFILE_LIB = "{0}-stdlib.js".format(PRODUCT_NAME) if STANDARD_NAMING else "builtin.js"
  50. OUTFILE_MAP = "{0}-linemap.txt".format(PRODUCT_NAME) if STANDARD_NAMING else "linemap.txt"
  51. OUTFILE_DEBUGGER = "debugger.js"
  52. # Symbolic constants for file types.
  53. FILE_TYPE_DIST = 'dist'
  54. FILE_TYPE_TEST = 'test'
  55. # Order is important!
  56. Files = [
  57. 'support/closure-library/closure/goog/base.js',
  58. 'support/closure-library/closure/goog/deps.js',
  59. ('support/closure-library/closure/goog/string/string.js', FILE_TYPE_DIST),
  60. ('support/closure-library/closure/goog/debug/error.js', FILE_TYPE_DIST),
  61. ('support/closure-library/closure/goog/asserts/asserts.js', FILE_TYPE_DIST),
  62. ('support/es6-promise-polyfill/promise-1.0.0.hacked.js', FILE_TYPE_DIST),
  63. 'src/env.js',
  64. 'src/type.js',
  65. 'src/abstract.js',
  66. 'src/object.js',
  67. 'src/function.js',
  68. 'src/builtin.js',
  69. 'src/fromcodepoint.js', # should become unnecessary, eventually
  70. 'src/errors.js',
  71. 'src/native.js',
  72. 'src/method.js',
  73. 'src/misceval.js',
  74. 'src/seqtype.js',
  75. 'src/list.js',
  76. 'src/str.js',
  77. 'src/formatting.js',
  78. 'src/tuple.js',
  79. 'src/dict.js',
  80. 'src/numtype.js',
  81. 'src/biginteger.js',
  82. 'src/int.js',
  83. 'src/bool.js',
  84. 'src/float.js',
  85. 'src/number.js',
  86. 'src/long.js',
  87. 'src/complex.js',
  88. 'src/slice.js',
  89. 'src/set.js',
  90. 'src/print.js',
  91. 'src/module.js',
  92. 'src/structseq.js',
  93. 'src/generator.js',
  94. 'src/file.js',
  95. 'src/ffi.js',
  96. 'src/iterator.js',
  97. 'src/enumerate.js',
  98. 'src/tokenize.js',
  99. 'gen/parse_tables.js',
  100. 'src/parser.js',
  101. 'gen/astnodes.js',
  102. 'src/ast.js',
  103. 'src/symtable.js',
  104. 'src/compile.js',
  105. 'src/import.js',
  106. 'src/timsort.js',
  107. 'src/sorted.js',
  108. 'src/builtindict.js',
  109. 'src/constants.js',
  110. 'src/internalpython.js',
  111. ("support/jsbeautify/beautify.js", FILE_TYPE_TEST),
  112. ]
  113. ExtLibs = [
  114. 'support/time-helpers/strftime-min.js',
  115. 'support/time-helpers/strptime.min.js'
  116. ]
  117. TestFiles = [
  118. 'support/closure-library/closure/goog/base.js',
  119. 'support/closure-library/closure/goog/deps.js',
  120. 'support/closure-library/closure/goog/math/math.js',
  121. 'support/closure-library/closure/goog/math/coordinate.js',
  122. 'support/closure-library/closure/goog/math/vec2.js',
  123. 'support/closure-library/closure/goog/json/json.js',
  124. 'support/jsbeautify/beautify.js',
  125. "{0}/namedtests.js".format(TEST_DIR),
  126. "{0}/sprintf.js".format(TEST_DIR),
  127. "{0}/json2.js".format(TEST_DIR),
  128. "{0}/test.js".format(TEST_DIR)
  129. ]
  130. def buildNamedTestsFile():
  131. testFiles = ['test/run/'+f.replace(".py","") for f in os.listdir('test/run') if re.match(r"test_.*\.py$",f)]
  132. nt = open("{0}/namedtests.js".format(TEST_DIR),'w')
  133. nt.write("namedtfiles = [")
  134. for f in testFiles:
  135. nt.write("'%s',\n" % f)
  136. nt.write("];")
  137. nt.close()
  138. def isClean():
  139. repo = Repo(".")
  140. return not repo.is_dirty()
  141. def getTip():
  142. repo = Repo(".")
  143. return repo.head.commit.hexsha
  144. def getFileList(type, include_ext_libs=True):
  145. ret = list(ExtLibs) if include_ext_libs else []
  146. for f in Files:
  147. if isinstance(f, tuple):
  148. if f[1] == type:
  149. ret.append(f[0])
  150. else:
  151. if "*" in f:
  152. for g in glob.glob(f):
  153. ret.append(f)
  154. else:
  155. ret.append(f)
  156. return ret
  157. def is64bit():
  158. return sys.maxsize > 2**32
  159. if sys.platform == "win32":
  160. winbase = ".\\support\\d8"
  161. os.environ["D8_PATH"] = winbase
  162. jsengine = winbase + "\\d8.exe --debugger --harmony"
  163. nul = "nul"
  164. crlfprog = os.path.join(os.path.split(sys.executable)[0], "Tools/Scripts/crlf.py")
  165. elif sys.platform == "darwin":
  166. os.environ["D8_PATH"] = "./support/d8/mac"
  167. jsengine = "./support/d8/mac/d8 --debugger"
  168. nul = "/dev/null"
  169. crlfprog = None
  170. elif sys.platform == "linux2":
  171. if is64bit():
  172. os.environ["D8_PATH"] = "support/d8/x64"
  173. jsengine = "support/d8/x64/d8 --debugger --harmony_promises"
  174. else:
  175. os.environ["D8_PATH"] = "support/d8/x32"
  176. jsengine = "support/d8/x32/d8 --debugger --harmony_promises"
  177. nul = "/dev/null"
  178. crlfprog = None
  179. else:
  180. # You're on your own...
  181. os.environ["D8_PATH"] = "support/d8/x32"
  182. jsengine = "support/d8/x32/d8 --debugger --harmony_promises"
  183. nul = "/dev/null"
  184. crlfprog = None
  185. if os.environ.get("CI",False):
  186. os.environ["D8_PATH"] = "support/d8/x64"
  187. jsengine = "support/d8/x64/d8 --harmony_promises"
  188. nul = "/dev/null"
  189. #jsengine = "rhino"
  190. def test(debug_mode=False):
  191. """runs the unit tests."""
  192. if debug_mode:
  193. debugon = "--debug-mode"
  194. else:
  195. debugon = ""
  196. buildNamedTestsFile()
  197. ret1 = os.system("{0} {1} {2} -- {3}".format(jsengine, ' '.join(getFileList(FILE_TYPE_TEST)), ' '.join(TestFiles), debugon))
  198. ret2 = 0
  199. ret3 = 0
  200. ret4 = 0
  201. if ret1 == 0:
  202. print("Running jshint")
  203. base_dirs = ["src", "debugger"]
  204. for base_dir in base_dirs:
  205. if sys.platform == "win32":
  206. jshintcmd = "{0} {1}".format("jshint", ' '.join(f for f in glob.glob(base_dir + "/*.js")))
  207. jscscmd = "{0} {1} --reporter=inline".format("jscs", ' '.join(f for f in glob.glob(base_dir + "/*.js")))
  208. else:
  209. jshintcmd = "jshint " + base_dir + "/*.js"
  210. jscscmd = "jscs " + base_dir + "/*.js --reporter=inline"
  211. ret2 = os.system(jshintcmd)
  212. print("Running JSCS")
  213. ret3 = os.system(jscscmd)
  214. #ret3 = os.system(jscscmd)
  215. print("Now running new unit tests")
  216. ret4 = rununits()
  217. return ret1 | ret2 | ret3 | ret4
  218. def parse_time_args(argv):
  219. usageString = """
  220. {program} time [filename.py] [iter=1]
  221. Computes the average runtime of a Python file (or test suite, if none specified)
  222. over iter number of trials.
  223. """.format(program=argv[0])
  224. fn = ""
  225. iter = 0
  226. if len(sys.argv) > 4:
  227. print(usageString)
  228. sys.exit(2)
  229. for arg in argv[2:]:
  230. if arg.isdigit():
  231. if iter:
  232. print(usageString)
  233. sys.exit(2)
  234. else:
  235. iter = int(arg)
  236. if iter <= 0:
  237. print("Number of trials must be 1 or greater.")
  238. sys.exit(2)
  239. elif ".py" in arg:
  240. if fn:
  241. print(usageString)
  242. sys.exit(2)
  243. else:
  244. fn = arg
  245. else:
  246. print(usageString)
  247. sys.exit(2)
  248. iter = iter if iter else 1
  249. time_suite(iter=iter, fn=fn)
  250. def time_suite(iter=1, fn=""):
  251. jsprofengine = jsengine.replace('--debugger', '--prof --log-internal-timer-events')
  252. if not os.path.exists("support/tmp"):
  253. os.mkdir("support/tmp")
  254. f = open("support/tmp/run.js", "w")
  255. additional_files = ""
  256. # Profile single file
  257. if fn:
  258. if not os.path.exists(fn):
  259. print("%s doesn't exist" % fn)
  260. raise SystemExit()
  261. modname = os.path.splitext(os.path.basename(fn))[0]
  262. f.write("""
  263. var input = read('%s');
  264. print("-----");
  265. print(input);
  266. print("-----");
  267. Sk.configure({syspath:["%s"], read:read, python3:false, debugging:false});
  268. Sk.misceval.asyncToPromise(function() {
  269. return Sk.importMain("%s", true, true);
  270. }).then(function () {
  271. print("-----");
  272. }, function(e) {
  273. print("UNCAUGHT EXCEPTION: " + e);
  274. print(e.stack);
  275. });
  276. """ % (fn, os.path.split(fn)[0], modname))
  277. # Profile test suite
  278. else:
  279. # Prepare named tests
  280. buildNamedTestsFile()
  281. # Prepare unit tests
  282. testFiles = ['test/unit/'+fn for fn in os.listdir('test/unit') if '.py' in fn]
  283. if not os.path.exists("support/tmp"):
  284. os.mkdir("support/tmp")
  285. f.write("var input;\n")
  286. for fn in testFiles:
  287. modname = os.path.splitext(os.path.basename(fn))[0]
  288. p3on = 'false'
  289. f.write("""
  290. input = read('%s');
  291. print('%s');
  292. Sk.configure({syspath:["%s"], read:read, python3:%s});
  293. Sk.importMain("%s", false);
  294. """ % (fn, fn, os.path.split(fn)[0], p3on, modname))
  295. fn = "test suite"
  296. additional_files = ' '.join(TestFiles)
  297. f.close()
  298. print("Timing %s...\n" % fn)
  299. times = []
  300. # Run profile
  301. for i in range(iter):
  302. if iter > 1:
  303. print("Iteration %d of %d..." % (i + 1, iter))
  304. startTime = time.time()
  305. p = Popen("{0} {1} {2} support/tmp/run.js".format(jsprofengine,
  306. ' '.join(getFileList(FILE_TYPE_TEST)),
  307. additional_files),
  308. shell=True, stdout=PIPE, stderr=PIPE)
  309. outs, errs = p.communicate()
  310. if p.returncode != 0:
  311. print("\n\nWARNING: Scripts returned with error code. Timing data may be inaccurate.\n\n")
  312. endTime = time.time()
  313. times.append(endTime - startTime)
  314. avg = sum(times) / len(times)
  315. if iter > 1:
  316. print("\nAverage time over %s iterations: %s seconds" % (iter, avg))
  317. else:
  318. print("%s seconds" % avg)
  319. def parse_profile_args(argv):
  320. usageString = """
  321. {program} profile [filename.py] [output]
  322. Runs profile on Python file (or test suite, if none specified)
  323. and outputs processed results to output file (or stdout if none specified)
  324. """.format(program=argv[0])
  325. fn = ""
  326. out = ""
  327. numArgs = len(sys.argv)
  328. if len(sys.argv) > 4:
  329. print(usageString)
  330. sys.exit(2)
  331. for arg in argv[2:]:
  332. if ".py" in arg:
  333. if fn:
  334. print(usageString)
  335. sys.exit(2)
  336. else:
  337. fn = arg
  338. else:
  339. if out:
  340. print(usageString)
  341. sys.exit(2)
  342. else:
  343. out = arg
  344. profile(fn=fn, output=out)
  345. def profile(fn="", process=True, output=""):
  346. """
  347. Runs v8 profiler, which outputs tick information to v8.log Use
  348. https://v8.googlecode.com/svn/branches/bleeding_edge/tools/profviz/profviz.html
  349. to analyze log.
  350. """
  351. jsprofengine = jsengine.replace('--debugger', '--prof --log-internal-timer-events')
  352. if not os.path.exists("support/tmp"):
  353. os.mkdir("support/tmp")
  354. f = open("support/tmp/run.js", "w")
  355. additional_files = ""
  356. # Profile single file
  357. if fn:
  358. if not os.path.exists(fn):
  359. print("%s doesn't exist" % fn)
  360. raise SystemExit()
  361. modname = os.path.splitext(os.path.basename(fn))[0]
  362. f.write("""
  363. var input = read('%s');
  364. print("-----");
  365. print(input);
  366. print("-----");
  367. Sk.configure({syspath:["%s"], read:read, python3:false, debugging:false});
  368. Sk.misceval.asyncToPromise(function() {
  369. return Sk.importMain("%s", true, true);
  370. }).then(function () {
  371. print("-----");
  372. }, function(e) {
  373. print("UNCAUGHT EXCEPTION: " + e);
  374. print(e.stack);
  375. });
  376. """ % (fn, os.path.split(fn)[0], modname))
  377. # Profile test suite
  378. else:
  379. # Prepare named tests
  380. buildNamedTestsFile()
  381. # Prepare unit tests
  382. testFiles = ['test/unit/'+fn for fn in os.listdir('test/unit') if '.py' in fn]
  383. if not os.path.exists("support/tmp"):
  384. os.mkdir("support/tmp")
  385. f.write("var input;\n")
  386. for fn in testFiles:
  387. modname = os.path.splitext(os.path.basename(fn))[0]
  388. p3on = 'false'
  389. f.write("""
  390. input = read('%s');
  391. print('%s');
  392. Sk.configure({syspath:["%s"], read:read, python3:%s});
  393. Sk.importMain("%s", false);
  394. """ % (fn, fn, os.path.split(fn)[0], p3on, modname))
  395. fn = "test suite"
  396. additional_files = ' '.join(TestFiles)
  397. f.close()
  398. # Run profile
  399. print("Running profile on %s..." % fn)
  400. startTime = time.time()
  401. p = Popen("{0} {1} {2} support/tmp/run.js".format(jsprofengine,
  402. ' '.join(getFileList(FILE_TYPE_TEST)),
  403. additional_files),
  404. shell=True, stdout=PIPE, stderr=PIPE)
  405. outs, errs = p.communicate()
  406. if p.returncode != 0:
  407. print("\n\nWARNING: Scripts returned with error code. Timing data may be inaccurate.\n\n")
  408. endTime = time.time()
  409. if errs:
  410. print(errs)
  411. print("\n\nRunning time: ", (endTime - startTime), " seconds\n\n")
  412. # Process and display results
  413. if process:
  414. if output:
  415. out_msg = " and saving in %s" % output
  416. output = " > " + output
  417. else:
  418. out_msg = ""
  419. print("Processing profile using d8 processor%s..." % out_msg)
  420. if sys.platform == "win32":
  421. os.system(".\\support\\d8\\tools\\windows-tick-processor.bat v8.log {0}".format(output))
  422. elif sys.platform == "darwin":
  423. os.system("./support/d8/tools/mac-tick-processor {0}".format(output))
  424. elif sys.platform == "linux2":
  425. os.system("./support/d8/tools/linux-tick-processor v8.log {0}".format(output))
  426. else:
  427. print("""d8 processor is unsupported on this platform.
  428. Try using https://v8.googlecode.com/svn/branches/bleeding_edge/tools/profviz/profviz.html.""")
  429. def debugbrowser():
  430. tmpl = """
  431. <!DOCTYPE HTML>
  432. <html>
  433. <head>
  434. <meta http-equiv="X-UA-Compatible" content="IE=edge" >
  435. <title>Skulpt test</title>
  436. <link rel="stylesheet" href="../closure-library/closure/goog/demos/css/demo.css">
  437. <link rel="stylesheet" href="../closure-library/closure/goog/css/menu.css">
  438. <link rel="stylesheet" href="../closure-library/closure/goog/css/menuitem.css">
  439. <link rel="stylesheet" href="../closure-library/closure/goog/css/menuseparator.css">
  440. <link rel="stylesheet" href="../closure-library/closure/goog/css/combobox.css">
  441. <style>
  442. .type { font-size:14px; font-weight:bold; font-family:arial; background-color:#f7f7f7; text-align:center }
  443. </style>
  444. %s
  445. </head>
  446. <body onload="testsMain()">
  447. <canvas id="__webglhelpercanvas" style="border: none;" width="500" height="500"></canvas>
  448. <table>
  449. <tr>
  450. <td>
  451. <div id="one-test" class="use-arrow"></div>
  452. </td>
  453. </tr>
  454. <tr>
  455. <td>
  456. <pre id="output"></pre>
  457. </td>
  458. <td>
  459. <span id="canv"></span>
  460. </td>
  461. </tr>
  462. </body>
  463. </html>
  464. """
  465. if not os.path.exists("support/tmp"):
  466. os.mkdir("support/tmp")
  467. buildVFS()
  468. scripts = []
  469. for f in getFileList(FILE_TYPE_TEST) + ["{0}/browser-stubs.js".format(TEST_DIR), "support/tmp/vfs.js" ] + TestFiles:
  470. scripts.append('<script type="text/javascript" src="%s"></script>' %
  471. os.path.join('../..', f))
  472. with open("support/tmp/test.html", "w") as f:
  473. print(tmpl % '\n'.join(scripts), file=f)
  474. if sys.platform == "win32":
  475. os.system("start support/tmp/test.html")
  476. elif sys.platform == "darwin":
  477. os.system("open support/tmp/test.html")
  478. else:
  479. os.system("gnome-open support/tmp/test.html")
  480. def buildVFS():
  481. """ build a silly virtual file system to support 'read'"""
  482. print(". Slurping test data")
  483. with open("support/tmp/vfs.js", "w") as out:
  484. print("VFSData = {", file=out)
  485. all = []
  486. for root in (TEST_DIR, "src/builtin", "src/lib"):
  487. for dirpath, dirnames, filenames in os.walk(root):
  488. for filename in filenames:
  489. f = os.path.join(dirpath, filename)
  490. if ".svn" in f: continue
  491. if ".swp" in f: continue
  492. if ".pyc" in f: continue
  493. data = open(f, "rb").read()
  494. data = data.replace("\r\n", "\n")
  495. all.append("'%s': '%s'" % (f.replace("\\", "/"), data.encode("hex")))
  496. print(",\n".join(all), file=out)
  497. print("};", file=out)
  498. print("""
  499. function readFromVFS(fn)
  500. {
  501. var hexToStr = function(str)
  502. {
  503. var ret = "";
  504. for (var i = 0; i < str.length; i += 2)
  505. ret += unescape("%" + str.substr(i, 2));
  506. return ret;
  507. }
  508. if (VFSData[fn] === undefined) throw "file not found: " + fn;
  509. return hexToStr(VFSData[fn]);
  510. }
  511. """, file=out)
  512. def buildBrowserTests():
  513. """combine all the tests data into something we can run from a browser
  514. page (so that it can be tested in the various crappy engines)
  515. we want to use the same code that the command line version of the tests
  516. uses so we stub the d8 functions to push to the browser."""
  517. outfn = "doc/static/browser-test.js"
  518. out = open(outfn, "w")
  519. print("""
  520. window.addevent('onload', function(){
  521. """, file=out)
  522. # stub the d8 functions we use
  523. print("""
  524. function read(fn)
  525. {
  526. var hexToStr = function(str)
  527. {
  528. var ret = "";
  529. for (var i = 0; i < str.length; i += 2)
  530. ret += unescape("%%" + str.substr(i, 2));
  531. return ret;
  532. }
  533. if (VFSData[fn] === undefined) throw "file not found: " + fn;
  534. return hexToStr(VFSData[fn]);
  535. }
  536. var SkulptTestRunOutput = '';
  537. function print()
  538. {
  539. var out = document.getElementById("output");
  540. for (var i = 0; i < arguments.length; ++i)
  541. {
  542. out.innerHTML += arguments[i];
  543. SkulptTestRunOutput += arguments[i];
  544. out.innerHTML += " ";
  545. SkulptTestRunOutput += " ";
  546. }
  547. out.innerHTML += "<br/>"
  548. SkulptTestRunOutput += "\\n";
  549. }
  550. function quit(rc)
  551. {
  552. var out = document.getElementById("output");
  553. if (rc === 0)
  554. {
  555. out.innerHTML += "<font color='green'>OK</font>";
  556. }
  557. else
  558. {
  559. out.innerHTML += "<font color='red'>FAILED</font>";
  560. }
  561. out.innerHTML += "<br/>Saving results...";
  562. var sendData = JSON.encode({
  563. browsername: BrowserDetect.browser,
  564. browserversion: BrowserDetect.version,
  565. browseros: BrowserDetect.OS,
  566. version: '%s',
  567. rc: rc,
  568. results: SkulptTestRunOutput
  569. });
  570. var results = new Request.JSON({
  571. url: '/testresults',
  572. method: 'post',
  573. onSuccess: function() { out.innerHTML += "<br/>Results saved."; },
  574. onFailure: function() { out.innerHTML += "<br/>Couldn't save results."; }
  575. });
  576. results.send(sendData);
  577. }
  578. """ % getTip(), file=out)
  579. for f in ["{0}/browser-detect.js".format(TEST_DIR)] + getFileList(FILE_TYPE_TEST) + TestFiles:
  580. print(open(f).read(), file=out)
  581. print("""
  582. });
  583. """, file=out)
  584. out.close()
  585. print(". Built %s" % outfn)
  586. def getInternalCodeAsJson():
  587. ret = {}
  588. ret['files'] = {}
  589. for f in ["src/" + x for x in os.listdir("src") if os.path.splitext(x)[1] == ".py" if os.path.isfile("src/" + x)]:
  590. ext = os.path.splitext(f)[1]
  591. if ext == ".py":
  592. f = f.replace("\\", "/")
  593. ret['files'][f] = open(f).read()
  594. return "Sk.internalPy=" + json.dumps(ret)
  595. def getBuiltinsAsJson(options):
  596. ret = {}
  597. ret['files'] = {}
  598. for root in ["src/builtin", "src/lib"]:
  599. for dirpath, dirnames, filenames in os.walk(root):
  600. for filename in filenames:
  601. f = os.path.join(dirpath, filename)
  602. ext = os.path.splitext(f)[1]
  603. if ext == ".py" or ext == ".js":
  604. if options.verbose:
  605. print("reading", f)
  606. f = f.replace("\\", "/")
  607. ret['files'][f] = open(f).read()
  608. return "Sk.builtinFiles=" + json.dumps(ret)
  609. def dist(options):
  610. """builds a 'shippable' version of Skulpt.
  611. this is all combined into one file, tests run, jslint'd, compressed.
  612. """
  613. if GIT_MODULE_AVAILABLE:
  614. if not isClean():
  615. print("WARNING: working directory not clean (according to 'git status')")
  616. else:
  617. print("Working directory is clean (according to 'git status')")
  618. else:
  619. '''
  620. # We don't really use GitPython
  621. print("+----------------------------------------------------------------------------+")
  622. print("GitPython is not installed for Python 2.6")
  623. print("The 'dist' command will not work without it. Get it using pip or easy_install")
  624. print("or see: http://packages.python.org/GitPython/0.3.1/intro.html#getting-started")
  625. print("+----------------------------------------------------------------------------+")
  626. '''
  627. if options.verbose:
  628. print(". Removing distribution directory, '{0}/'.".format(DIST_DIR))
  629. shutil.rmtree(DIST_DIR, ignore_errors=True)
  630. if not os.path.exists(DIST_DIR): os.mkdir(DIST_DIR)
  631. if options.uncompressed:
  632. make_skulpt_js(options,DIST_DIR)
  633. # Make the compressed distribution.
  634. compfn = os.path.join(DIST_DIR, OUTFILE_MIN)
  635. builtinfn = os.path.join(DIST_DIR, OUTFILE_LIB)
  636. debuggerfn = os.path.join(DIST_DIR, OUTFILE_DEBUGGER)
  637. # Run tests on uncompressed.
  638. if options.verbose:
  639. print(". Running tests on uncompressed...")
  640. ret = 0 #test()
  641. if ret != 0:
  642. print("Tests failed on uncompressed version.")
  643. #sys.exit(1);
  644. # compress
  645. uncompfiles = ' '.join(['--js ' + x for x in getFileList(FILE_TYPE_DIST, include_ext_libs=False)])
  646. if options.verbose:
  647. print(". Compressing...")
  648. ret = os.system("java -jar support/closure-compiler/compiler.jar --define goog.DEBUG=false --output_wrapper \"(function(){%%output%%}());\" --compilation_level SIMPLE_OPTIMIZATIONS --jscomp_error accessControls --jscomp_error checkRegExp --jscomp_error checkTypes --jscomp_error checkVars --jscomp_error deprecated --jscomp_off fileoverviewTags --jscomp_error invalidCasts --jscomp_error missingProperties --jscomp_error nonStandardJsDocs --jscomp_error strictModuleDepCheck --jscomp_error undefinedVars --jscomp_error unknownDefines --jscomp_error visibility %s --externs support/es6-promise-polyfill/externs.js --js_output_file tmp.js" % (uncompfiles))
  649. # to disable asserts
  650. # --define goog.DEBUG=false
  651. #
  652. # to make a file that for ff plugin, not sure of format
  653. # --create_source_map <distribution-dir>/srcmap.txt
  654. #
  655. # --jscomp_error accessControls --jscomp_error checkRegExp --jscomp_error checkTypes --jscomp_error checkVars --jscomp_error deprecated --jscomp_error fileoverviewTags --jscomp_error invalidCasts --jscomp_error missingProperties --jscomp_error nonStandardJsDocs --jscomp_error strictModuleDepCheck --jscomp_error undefinedVars --jscomp_error unknownDefines --jscomp_error visibility
  656. #
  657. if ret != 0:
  658. print("closure-compiler failed.")
  659. sys.exit(1)
  660. # Copy the debugger file to the output dir
  661. if options.verbose:
  662. print(". Bundling external libraries...")
  663. bundle = ""
  664. for fn in ExtLibs + ["tmp.js"]:
  665. with open(fn, "r") as f:
  666. bundle += f.read()
  667. with open(compfn, "w") as f:
  668. f.write(bundle)
  669. print(". Wrote bundled file")
  670. # Run tests on compressed.
  671. if options.verbose:
  672. print(". Running tests on compressed...")
  673. buildNamedTestsFile()
  674. ret = 0 #os.system("{0} {1} {2}".format(jsengine, compfn, ' '.join(TestFiles)))
  675. if ret != 0:
  676. print("Tests failed on compressed version.")
  677. sys.exit(1)
  678. ret = 0 #rununits(opt=True)
  679. if ret != 0:
  680. print("Tests failed on compressed unit tests")
  681. sys.exit(1)
  682. #doc()
  683. try:
  684. shutil.copy(compfn, os.path.join(DIST_DIR, "tmp.js"))
  685. shutil.copy("debugger/debugger.js", DIST_DIR)
  686. except Exception as e:
  687. print("Couldn't copy debugger to output folder: %s" % e.message)
  688. sys.exit(1)
  689. path_list = os.environ.get('PATH','').split(':')
  690. has_gzip = False
  691. for p in path_list:
  692. has_gzip = os.access(os.path.join(p,"gzip"), os.X_OK)
  693. if has_gzip:
  694. break
  695. if has_gzip:
  696. ret = os.system("gzip -9 {0}/tmp.js".format(DIST_DIR))
  697. if ret != 0:
  698. print("Couldn't gzip to get final size.")
  699. has_gzip = False
  700. os.unlink("{0}/tmp.js".format(DIST_DIR))
  701. size = os.path.getsize("{0}/tmp.js.gz".format(DIST_DIR))
  702. os.unlink("{0}/tmp.js.gz".format(DIST_DIR))
  703. else:
  704. os.unlink("{0}/tmp.js".format(DIST_DIR))
  705. print("No gzip executable, can't get final size")
  706. with open(builtinfn, "w") as f:
  707. f.write(getBuiltinsAsJson(options))
  708. if options.verbose:
  709. print(". Wrote {0}".format(builtinfn))
  710. # Update documentation folder copies of the distribution.
  711. try:
  712. shutil.copy(compfn, os.path.join("doc", "static", OUTFILE_MIN))
  713. shutil.copy(builtinfn, os.path.join("doc", "static", OUTFILE_LIB))
  714. shutil.copy(debuggerfn, os.path.join("doc", "static", "debugger", OUTFILE_DEBUGGER))
  715. except:
  716. print("Couldn't copy to docs dir.")
  717. sys.exit(1)
  718. if options.verbose:
  719. print(". Updated doc dir")
  720. # All good!
  721. if options.verbose:
  722. print(". Wrote {0}.".format(compfn))
  723. if has_gzip:
  724. print(". gzip of compressed: %d bytes" % size)
  725. def make_skulpt_js(options,dest):
  726. if options.verbose:
  727. print(". Writing combined version...")
  728. combined = ''
  729. linemap = open(os.path.join(dest, OUTFILE_MAP), "w")
  730. curline = 1
  731. for file in getFileList(FILE_TYPE_DIST):
  732. curfiledata = open(file).read()
  733. combined += curfiledata
  734. print("%d:%s" % (curline, file), file=linemap)
  735. curline += len(curfiledata.split("\n")) - 1
  736. linemap.close()
  737. uncompfn = os.path.join(dest, OUTFILE_REG)
  738. open(uncompfn, "w").write(combined)
  739. # Prevent accidental editing of the uncompressed distribution file.
  740. if sys.platform != "win32":
  741. os.chmod(os.path.join(dest, OUTFILE_REG), 0o444)
  742. def run_in_browser(fn, options):
  743. shutil.rmtree(RUN_DIR, ignore_errors=True)
  744. if not os.path.exists(RUN_DIR): os.mkdir(RUN_DIR)
  745. docbi(options,RUN_DIR)
  746. scripts = []
  747. for f in getFileList(FILE_TYPE_TEST):
  748. scripts.append('<script type="text/javascript" src="%s"></script>' %
  749. os.path.join('../..', f))
  750. scripts = "\n".join(scripts)
  751. with open (fn,'r') as runfile:
  752. prog = runfile.read()
  753. with open('support/run_template.html') as tpfile:
  754. page = tpfile.read()
  755. page = page % dict(code=prog,scripts=scripts, root='', debug_mode='true')
  756. with open("{0}/run.html".format(RUN_DIR),"w") as htmlfile:
  757. htmlfile.write(page)
  758. if sys.platform == "darwin":
  759. os.system("open {0}/run.html".format(RUN_DIR))
  760. elif sys.platform == "linux2":
  761. os.system("xdg-open {0}/run.html".format(RUN_DIR))
  762. elif sys.platform == "win32":
  763. os.system("start {0}/run.html".format(RUN_DIR))
  764. else:
  765. print("open or refresh {0}/run.html in your browser to test/debug".format(RUN_DIR))
  766. def regenparser():
  767. """regenerate the parser/ast source code"""
  768. if not os.path.exists("gen"): os.mkdir("gen")
  769. os.chdir("src/pgen/parser")
  770. os.system("python main.py ../../../gen/parse_tables.js")
  771. os.chdir("../ast")
  772. os.system("python asdl_js.py Python.asdl ../../../gen/astnodes.js")
  773. os.chdir("../../..")
  774. # sanity check that they at least parse
  775. #os.system(jsengine + " support/closure-library/closure/goog/base.js src/env.js src/tokenize.js gen/parse_tables.js gen/astnodes.js")
  776. def regenasttests(togen="{0}/run/*.py".format(TEST_DIR)):
  777. """regenerate the ast test files by running our helper script via real python"""
  778. for f in glob.glob(togen):
  779. transname = f.replace(".py", ".trans")
  780. os.system("python {0}/astppdump.py {1} > {2}".format(TEST_DIR, f, transname))
  781. forcename = f.replace(".py", ".trans.force")
  782. if os.path.exists(forcename):
  783. shutil.copy(forcename, transname)
  784. if crlfprog:
  785. os.system("python {0} {1}".format(crlfprog, transname))
  786. def regenruntests(togen="{0}/run/*.py".format(TEST_DIR)):
  787. """regenerate the test data by running the tests on real python"""
  788. for f in glob.glob(togen):
  789. os.system("python {0} > {1}.real 2>&1".format(f, f))
  790. forcename = f + ".real.force"
  791. if os.path.exists(forcename):
  792. shutil.copy(forcename, "%s.real" % f)
  793. if crlfprog:
  794. os.system("python %s %s.real" % (crlfprog, f))
  795. for f in glob.glob("{0}/interactive/*.py".format(TEST_DIR)):
  796. p = Popen("python -i > %s.real 2>%s" % (f, nul), shell=True, stdin=PIPE)
  797. p.communicate(open(f).read() + "\004")
  798. forcename = f + ".real.force"
  799. if os.path.exists(forcename):
  800. shutil.copy(forcename, "%s.real" % f)
  801. if crlfprog:
  802. os.system("python %s %s.real" % (crlfprog, f))
  803. def doc():
  804. print("Building Documentation in docs/ProgMan")
  805. ret = os.system("jsdoc -c jsdoc.json HACKING.md")
  806. if ret != 0:
  807. print("Build of docs failed. Is jsdoc installed?")
  808. def symtabdump(fn):
  809. if not os.path.exists(fn):
  810. print("%s doesn't exist" % fn)
  811. raise SystemExit()
  812. text = open(fn).read()
  813. mod = symtable.symtable(text, os.path.split(fn)[1], "exec")
  814. def getidents(obj, indent=""):
  815. ret = ""
  816. ret += """%sSym_type: %s
  817. %sSym_name: %s
  818. %sSym_lineno: %s
  819. %sSym_nested: %s
  820. %sSym_haschildren: %s
  821. """ % (
  822. indent, obj.get_type(),
  823. indent, obj.get_name(),
  824. indent, obj.get_lineno(),
  825. indent, obj.is_nested(),
  826. indent, obj.has_children())
  827. if obj.get_type() == "function":
  828. ret += "%sFunc_params: %s\n%sFunc_locals: %s\n%sFunc_globals: %s\n%sFunc_frees: %s\n" % (
  829. indent, sorted(obj.get_parameters()),
  830. indent, sorted(obj.get_locals()),
  831. indent, sorted(obj.get_globals()),
  832. indent, sorted(obj.get_frees()))
  833. elif obj.get_type() == "class":
  834. ret += "%sClass_methods: %s\n" % (
  835. indent, sorted(obj.get_methods()))
  836. ret += "%s-- Identifiers --\n" % indent
  837. for ident in sorted(obj.get_identifiers()):
  838. info = obj.lookup(ident)
  839. ret += "%sname: %s\n %sis_referenced: %s\n %sis_imported: %s\n %sis_parameter: %s\n %sis_global: %s\n %sis_declared_global: %s\n %sis_local: %s\n %sis_free: %s\n %sis_assigned: %s\n %sis_namespace: %s\n %snamespaces: [\n%s %s]\n" % (
  840. indent, info.get_name(),
  841. indent, info.is_referenced(),
  842. indent, info.is_imported(),
  843. indent, info.is_parameter(),
  844. indent, info.is_global(),
  845. indent, info.is_declared_global(),
  846. indent, info.is_local(),
  847. indent, info.is_free(),
  848. indent, info.is_assigned(),
  849. indent, info.is_namespace(),
  850. indent, '\n'.join([getidents(x, indent + " ") for x in info.get_namespaces()]),
  851. indent
  852. )
  853. return ret
  854. return getidents(mod)
  855. def regensymtabtests(togen="{0}/run/*.py".format(TEST_DIR)):
  856. """regenerate the test data by running the symtab dump via real python"""
  857. for fn in glob.glob(togen):
  858. outfn = "%s.symtab" % fn
  859. f = open(outfn, "wb")
  860. f.write(symtabdump(fn))
  861. f.close()
  862. def upload():
  863. """uploads doc to GAE (stub app for static hosting, mostly)"""
  864. ret = os.system("python2.6 ~/Desktop/3rdparty/google_appengine/appcfg.py update doc")
  865. if ret != 0:
  866. print("Couldn't upload.")
  867. raise SystemExit()
  868. def doctest():
  869. ret = os.system("python2.6 ~/Desktop/3rdparty/google_appengine/dev_appserver.py -p 20710 doc")
  870. def docbi(options,dest="doc/static"):
  871. builtinfn = "{0}/{1}".format(dest,OUTFILE_LIB)
  872. with open(builtinfn, "w") as f:
  873. f.write(getBuiltinsAsJson(options))
  874. if options.verbose:
  875. print(". Wrote {fileName}".format(fileName=builtinfn))
  876. def assess(student_code, instructor_code):
  877. student_code = student_code.replace("\\", "/")
  878. instructor_code = instructor_code.replace("\\", "/")
  879. if not os.path.exists(student_code):
  880. print("%s doesn't exist" % student_code)
  881. raise SystemExit()
  882. if not os.path.exists(instructor_code):
  883. print("%s doesn't exist" % instructor_code)
  884. raise SystemExit()
  885. if not os.path.exists("support/tmp"):
  886. os.mkdir("support/tmp")
  887. student_module_name = os.path.splitext(os.path.basename(student_code))[0]
  888. instructor_module_name = os.path.splitext(os.path.basename(instructor_code))[0]
  889. f = open("support/tmp/run.js", "w")
  890. f.write("""
  891. Sk.console = [];
  892. Sk.skip_drawing = true;
  893. var printError = function(error) {{
  894. if (error.constructor == Sk.builtin.NameError
  895. && error.args.v.length > 0
  896. && error.args.v[0].v == "name '___' is not defined") {{
  897. print("EXCEPTION: "+error.tp$name);
  898. //print("EXCEPTION: DanglingBlocksError");
  899. }} else {{
  900. print("EXCEPTION: "+error.tp$name);
  901. }}
  902. }}
  903. var student_code = read('{student_code_filename}');
  904. var instructor_code = read('{instructor_code_filename}');
  905. var outputList = [];
  906. Sk.configure({{read:read, python3:true, debugging:false, output: function(text) {{ if (text !== "\\n") {{ outputList.push(text); }} }} }});
  907. Sk.console.printHtml = function(chart, lines) {{
  908. outputList.push(lines);
  909. }};
  910. // Run students' code
  911. Sk.misceval.asyncToPromise(function() {{
  912. return Sk.importMainWithBody("<student>", false, student_code, true);
  913. }}).then(function (data) {{
  914. // Trace table
  915. var traceTable = []; //JSON.stringify(data.$d);
  916. // Run instructor's code
  917. /*Sk.configure({{read:read, python3:true, debugging:false, output: function(text) {{ }} }});
  918. instructor_code += "\\nresult = on_run('''"+student_code+"''', "+
  919. JSON.stringify(outputList)+", "+
  920. JSON.stringify(traceTable)+")";
  921. Sk.misceval.asyncToPromise(function() {{
  922. return Sk.importMainWithBody("<instructor>", false, instructor_code, true);
  923. }}).then(function (data) {{
  924. var result = data.$d.result.v;
  925. print(result);
  926. }}, function(e) {{
  927. //printError(e);
  928. print("UNCAUGHT EXCEPTION: " + e);
  929. }});*/
  930. }}, function(e) {{
  931. //printError(e);
  932. print(e);
  933. }});""".format(student_code_filename=student_code,
  934. instructor_code_filename=instructor_code))
  935. f.close()
  936. command = jsengine.split(" ")+getFileList(FILE_TYPE_TEST)+["../libs/math.0.19.0.min.js", "../libs/crime_data.js", "support/tmp/run.js"]
  937. try:
  938. p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
  939. out, err = p.communicate()
  940. print(out)
  941. except OSError as e:
  942. print("Execution failed:", e, file=sys.stderr)
  943. def run(fn, shell="", opt=False, p3=True, debug_mode=False, dumpJS='true'):
  944. if not os.path.exists(fn):
  945. print("%s doesn't exist" % fn)
  946. raise SystemExit()
  947. if not os.path.exists("support/tmp"):
  948. os.mkdir("support/tmp")
  949. f = open("support/tmp/run.js", "w")
  950. modname = os.path.splitext(os.path.basename(fn))[0]
  951. if p3:
  952. p3on = 'true'
  953. else:
  954. p3on = 'false'
  955. if debug_mode:
  956. debugon = 'true'
  957. else:
  958. debugon = 'false'
  959. f.write("""
  960. var input = read('%s');
  961. var outputList = [];
  962. //print("-----");
  963. //print(input);
  964. //print("-----");
  965. Sk.configure({syspath:["%s"], read:read, python3:%s, debugging:%s
  966. , output: function(text) {if (text !== "\\n") {outputList.push(text); print(text); }}
  967. });
  968. Sk.misceval.asyncToPromise(function() {
  969. return Sk.importMain("%s", %s, true);
  970. }).then(function (data) {
  971. // Printed
  972. // outputList
  973. // Properties
  974. // JSON.stringify(data.$d);
  975. // Source code
  976. // input
  977. print("-----");
  978. //print(outputList)
  979. }, function(e) {
  980. print("UNCAUGHT EXCEPTION: " + e);
  981. print(e.stack);
  982. });
  983. """ % (fn, os.path.split(fn)[0], p3on, debugon, modname, dumpJS))
  984. f.close()
  985. if opt:
  986. os.system("{0} {1}/{2} support/tmp/run.js".format(jsengine, DIST_DIR, OUTFILE_MIN))
  987. else:
  988. os.system("{0} {1} {2} support/tmp/run.js".format(jsengine, shell, ' '.join(getFileList(FILE_TYPE_TEST))))
  989. def runopt(fn):
  990. run(fn, "", True)
  991. def run3(fn):
  992. run(fn,p3=True)
  993. def rundebug(fn):
  994. run(fn,debug_mode=True)
  995. def shell(fn):
  996. run(fn, "--shell")
  997. def rununits(opt=False, p3=False):
  998. testFiles = ['test/unit/'+f for f in os.listdir('test/unit') if '.py' in f]
  999. jstestengine = jsengine.replace('--debugger', '')
  1000. passTot = 0
  1001. failTot = 0
  1002. for fn in testFiles:
  1003. if not os.path.exists("support/tmp"):
  1004. os.mkdir("support/tmp")
  1005. f = open("support/tmp/run.js", "w")
  1006. modname = os.path.splitext(os.path.basename(fn))[0]
  1007. if p3:
  1008. p3on = 'true'
  1009. else:
  1010. p3on = 'false'
  1011. f.write("""
  1012. var input = read('%s');
  1013. print('%s');
  1014. Sk.configure({syspath:["%s"], read:read, python3:%s});
  1015. Sk.misceval.asyncToPromise(function() {
  1016. return Sk.importMain("%s", false, true);
  1017. }).then(function () {}, function(e) {
  1018. print("UNCAUGHT EXCEPTION: " + e);
  1019. print(e.stack);
  1020. quit(1);
  1021. });
  1022. """ % (fn, fn, os.path.split(fn)[0], p3on, modname))
  1023. f.close()
  1024. if opt:
  1025. p = Popen("{0} {1}/{2} support/tmp/run.js".format(jstestengine, DIST_DIR,
  1026. OUTFILE_MIN),shell=True,
  1027. stdout=PIPE, stderr=PIPE)
  1028. else:
  1029. p = Popen("{0} {1} support/tmp/run.js".format(jstestengine, ' '.join(
  1030. getFileList(FILE_TYPE_TEST))), shell=True, stdout=PIPE, stderr=PIPE)
  1031. outs, errs = p.communicate()
  1032. if p.returncode != 0:
  1033. failTot += 1
  1034. print("{} exited with error code {}".format(fn,p.returncode))
  1035. print(outs)
  1036. if errs:
  1037. print(errs)
  1038. outlines = outs.split('\n')
  1039. for ol in outlines:
  1040. g = re.match(r'Ran.*passed:\s+(\d+)\s+failed:\s+(\d+)',ol)
  1041. if g:
  1042. passTot += int(g.group(1))
  1043. failTot += int(g.group(2))
  1044. print("Summary")
  1045. print("Passed: %5d Failed %5d" % (passTot, failTot))
  1046. if failTot != 0:
  1047. return -1
  1048. else:
  1049. return 0
  1050. def repl():
  1051. os.system("{0} {1} repl/repl.js".format(jsengine, ' '.join(getFileList(FILE_TYPE_TEST))))
  1052. def nrt(newTest):
  1053. """open a new run test"""
  1054. fn = "{0}/run/test_{1}.py".format(TEST_DIR,newTest)
  1055. disfn = fn + ".disabled"
  1056. if not os.path.exists(fn) and not os.path.exists(disfn):
  1057. if 'EDITOR' in os.environ:
  1058. editor = os.environ['EDITOR']
  1059. else:
  1060. editor = 'vim'
  1061. os.system(editor + ' ' + fn)
  1062. if os.path.exists(fn):
  1063. print("Generating tests for %s" % fn)
  1064. regensymtabtests(fn)
  1065. regenasttests(fn)
  1066. regenruntests(fn)
  1067. else:
  1068. print("Test test_%s.py already exists." % newTest)
  1069. print("run ./m regentests test_%s.py" % newTest)
  1070. def vmwareregr(names):
  1071. """todo; not working yet.
  1072. run unit tests via vmware on a bunch of browsers"""
  1073. xp = "/data/VMs/xpsp3/xpsp3.vmx"
  1074. ubu = "/data/VMs/ubu910/ubu910.vmx"
  1075. # apparently osx isn't very vmware-able. stupid.
  1076. class Browser:
  1077. def __init__(self, name, vmx, guestloc):
  1078. self.name = name
  1079. self.vmx = vmx
  1080. self.guestloc = guestloc
  1081. browsers = [
  1082. Browser("ie7-win", xp, "C:\\Program Files\\Internet Explorer\\iexplore.exe"),
  1083. Browser("ie8-win", xp, "C:\\Program Files\\Internet Explorer\\iexplore.exe"),
  1084. Browser("chrome3-win", xp, "C:\\Documents and Settings\\Administrator\\Local Settings\\Application Data\\Google\\Chrome\\Application\\chrome.exe"),
  1085. Browser("chrome4-win", xp, "C:\\Documents and Settings\\Administrator\\Local Settings\\Application Data\\Google\\Chrome\\Application\\chrome.exe"),
  1086. Browser("ff3-win", xp, "C:\\Program Files\\Mozilla Firefox\\firefox.exe"),
  1087. Browser("ff35-win", xp, "C:\\Program Files\\Mozilla Firefox\\firefox.exe"),
  1088. #Browser("safari3-win", xp,
  1089. #Browser("safari4-win", xp,
  1090. #"ff3-osx": osx,
  1091. #"ff35-osx": osx,
  1092. #"safari3-osx": osx,
  1093. #"safari4-osx": osx,
  1094. #"ff3-ubu": ubu,
  1095. #"chromed-ubu": ubu,
  1096. ]
  1097. def regengooglocs():
  1098. """scans the closure library and builds an import-everything file to be
  1099. used during dev. """
  1100. # from calcdeps.py
  1101. prov_regex = re.compile('goog\.provide\s*\(\s*[\'\"]([^\)]+)[\'\"]\s*\)')
  1102. # walk whole tree, find all the 'provide's in a file, and note the location
  1103. root = "support/closure-library/closure"
  1104. modToFile = {}
  1105. for dirpath, dirnames, filenames in os.walk(root):
  1106. for filename in filenames:
  1107. f = os.path.join(dirpath, filename)
  1108. if ".svn" in f: continue
  1109. if os.path.splitext(f)[1] == ".js":
  1110. contents = open(f).read()
  1111. for prov in prov_regex.findall(contents):
  1112. modToFile[prov] = f.lstrip(root)
  1113. with open("gen/debug_import_all_closure.js", "w") as glf:
  1114. keys = modToFile.keys()
  1115. keys.sort()
  1116. for m in keys:
  1117. if "demos." in m: continue
  1118. if not m.startswith("goog."): continue
  1119. print("goog.require('%s');" % m, file=glf)
  1120. import http.server
  1121. from urllib.parse import urlparse
  1122. class HttpHandler(http.server.SimpleHTTPRequestHandler):
  1123. """allow grabbing any file for testing, and support /import
  1124. which grabs all builtin and lib modules in a json request.
  1125. see notes on import for why we can't just grab one at a time.
  1126. on real hosting, we'll just prebuild/gzip the stdlib into somewhere on
  1127. upload. this is more convenient during dev on localhost though.
  1128. """
  1129. def do_GET(self):
  1130. prefix = "/import"
  1131. if self.path == prefix:
  1132. self.send_response(200)
  1133. self.send_header("Content-type", "application/json")
  1134. self.end_headers()
  1135. self.wfile.write(getBuiltinsAsJson(None))
  1136. else:
  1137. SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
  1138. def host(PORT = 20710):
  1139. """simple http host from root of dir for testing"""
  1140. import SocketServer
  1141. httpd = SocketServer.TCPServer(("", PORT), HttpHandler)
  1142. print("serving at port", PORT)
  1143. httpd.serve_forever()
  1144. def usageString(program):
  1145. return '''
  1146. {program} <command> [<options>] [script.py]
  1147. Commands:
  1148. run Run a Python file using Skulpt
  1149. brun Run a Python file using Skulpt but in your browser
  1150. test Run all test cases
  1151. rununits Run only the new-style unit tests
  1152. dist Build core and library distribution files
  1153. docbi Build library distribution file only and copy to doc/static
  1154. profile [fn] [out] Profile Skulpt using d8 and show processed results
  1155. time [iter] Average runtime of the test suite over [iter] iterations.
  1156. regenparser Regenerate parser tests
  1157. regenasttests Regen abstract symbol table tests
  1158. regenruntests Regenerate runtime unit tests
  1159. regensymtabtests Regenerate symbol table tests
  1160. regentests Regenerate all of the above
  1161. help Display help information about Skulpt
  1162. host [PORT] Start a simple HTTP server for testing. Default port: 20710
  1163. upload Run appcfg.py to upload doc to live GAE site
  1164. doctest Run the GAE development server for doc testing
  1165. nrt Generate a file for a new test case
  1166. runopt Run a Python file optimized
  1167. browser Run all tests in the browser
  1168. shell Run a Python program but keep a shell open (like python -i)
  1169. vfs Build a virtual file system to support Skulpt read tests
  1170. debugbrowser Debug in the browser -- open your javascript console
  1171. Options:
  1172. -q, --quiet Only output important information
  1173. -s, --silent Do not output anything, besides errors
  1174. -u, --uncompressed Makes uncompressed core distribution file for debugging
  1175. -v, --verbose Make output more verbose [default]
  1176. --version Returns the version string in Bower configuration file.
  1177. '''.format(program=program)
  1178. def main():
  1179. parser = OptionParser(usageString("%prog"), version="%prog {0}".format(bowerProperty("version")))
  1180. parser.add_option("-q", "--quiet", action="store_false", dest="verbose")
  1181. parser.add_option("-s", "--silent", action="store_true", dest="silent", default=False)
  1182. parser.add_option("-u", "--uncompressed", action="store_true", dest="uncompressed", default=False)
  1183. parser.add_option("-v", "--verbose",
  1184. action="store_true",
  1185. dest="verbose",
  1186. default=False,
  1187. help="Make output more verbose [default]")
  1188. (options, args) = parser.parse_args()
  1189. # This is rather aggressive. Do we really want it?
  1190. if options.verbose:
  1191. if sys.platform == 'win32':
  1192. os.system("cls")
  1193. else:
  1194. os.system("clear")
  1195. if len(sys.argv) < 2:
  1196. cmd = "help"
  1197. else:
  1198. cmd = sys.argv[1]
  1199. with open("src/internalpython.js", "w") as f:
  1200. f.write(getInternalCodeAsJson() + ";")
  1201. if cmd == "test":
  1202. test()
  1203. elif cmd == "testdebug":
  1204. test(True)
  1205. elif cmd == "dist":
  1206. dist(options)
  1207. elif cmd == "regengooglocs":
  1208. regengooglocs()
  1209. elif cmd == "regentests":
  1210. if len(sys.argv) > 2:
  1211. togen = "{0}/run/".format(TEST_DIR) + sys.argv[2]
  1212. else:
  1213. togen = "{0}/run/*.py".format(TEST_DIR)
  1214. print("generating tests for ", togen)
  1215. regensymtabtests(togen)
  1216. regenasttests(togen)
  1217. regenruntests(togen)
  1218. elif cmd == "regensymtabtests":
  1219. regensymtabtests()
  1220. elif cmd == "run":
  1221. run(sys.argv[2])
  1222. elif cmd == "assess":
  1223. assess(sys.argv[2], sys.argv[3])
  1224. elif cmd == "brun":
  1225. run_in_browser(sys.argv[2],options)
  1226. elif cmd == 'rununits':
  1227. rununits()
  1228. elif cmd == "runopt":
  1229. runopt(sys.argv[2])
  1230. elif cmd == "run3":
  1231. run3(sys.argv[2])
  1232. elif cmd == "rundebug":
  1233. rundebug(sys.argv[2])
  1234. elif cmd == "vmwareregr":
  1235. vmwareregr()
  1236. elif cmd == "regenparser":
  1237. regenparser()
  1238. elif cmd == "regenasttests":
  1239. regenasttests()
  1240. elif cmd == "regenruntests":
  1241. regenruntests()
  1242. elif cmd == "upload":
  1243. upload()
  1244. elif cmd == "doctest":
  1245. doctest()
  1246. elif cmd == "docbi":
  1247. docbi(options)
  1248. elif cmd == "doc":
  1249. doc()
  1250. elif cmd == "nrt":
  1251. print("Warning: nrt is deprectated.")
  1252. print("It is preferred that you enhance one of the unit tests in test/unit")
  1253. print("Or, create a new unit test file in test/unit using the template in test/unit_tmpl.py")
  1254. if len(sys.argv) < 3:
  1255. print("Need a name for the new test")
  1256. print(usageString(os.path.basename(sys.argv[0])))
  1257. sys.exit(2)
  1258. nrt(sys.argv[2])
  1259. elif cmd == "browser":
  1260. buildBrowserTests()
  1261. elif cmd == "debugbrowser":
  1262. debugbrowser()
  1263. elif cmd == "vfs":
  1264. buildVFS()
  1265. elif cmd == "host":
  1266. if len(sys.argv) < 3:
  1267. host()
  1268. else:
  1269. try:
  1270. host(int(sys.argv[2]))
  1271. except ValueError:
  1272. print("Port must be an integer")
  1273. sys.exit(2)
  1274. elif cmd == "shell":
  1275. shell(sys.argv[2]);
  1276. elif cmd == "repl":
  1277. repl()
  1278. elif cmd == "profile":
  1279. parse_profile_args(sys.argv)
  1280. elif cmd == "time":
  1281. parse_time_args(sys.argv)
  1282. else:
  1283. print(usageString(os.path.basename(sys.argv[0])))
  1284. sys.exit(2)
  1285. if __name__ == "__main__":
  1286. main()