1# Copyright (c) 2014 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import json 6import os 7import re 8import sys 9import shutil 10import tempfile 11import unittest 12 13sys.path.append( 14 os.path.join(os.path.dirname(__file__), '..', '..', 'mock')) 15import mock 16 17import vinn 18 19def _EscapeJsString(s): 20 return json.dumps(s) 21 22 23class VinnUnittest(unittest.TestCase): 24 25 @classmethod 26 def setUpClass(cls): 27 cls.test_data_dir = os.path.abspath( 28 os.path.join(os.path.dirname(__file__), 'test_data')) 29 30 def GetTestFilePath(self, file_name): 31 return os.path.join(self.test_data_dir, file_name) 32 33 def AssertHasNamedFrame(self, func_name, file_and_linum, exception_message): 34 m = re.search('at %s.+\(.*%s.*\)' % (func_name, file_and_linum), 35 exception_message) 36 if not m: 37 sys.stderr.write('\n=============================================\n') 38 msg = "Expected to find %s and %s" % (func_name, file_and_linum) 39 sys.stderr.write('%s\n' % msg) 40 sys.stderr.write('=========== Begin Exception Message =========\n') 41 sys.stderr.write(exception_message) 42 sys.stderr.write('=========== End Exception Message =========\n\n') 43 self.fail(msg) 44 45 def AssertHasFrame(self, file_and_linum, exception_message): 46 m = re.search('at .*%s.*' % file_and_linum, exception_message) 47 if not m: 48 sys.stderr.write('\n=============================================\n') 49 msg = "Expected to find %s" % file_and_linum 50 sys.stderr.write('%s\n' % msg) 51 sys.stderr.write('=========== Begin Exception Message =========\n') 52 sys.stderr.write(exception_message) 53 sys.stderr.write('=========== End Exception Message =========\n\n') 54 self.fail(msg) 55 56 def testExecuteJsStringStdoutPiping(self): 57 tmp_dir = tempfile.mkdtemp() 58 try: 59 temp_file_name = os.path.join(tmp_dir, 'out_file') 60 with open(temp_file_name, 'w') as f: 61 vinn.ExecuteJsString( 62 'print("Hello w0rld");\n', stdout=f) 63 with open(temp_file_name, 'r') as f: 64 self.assertEquals(f.read(), 'Hello w0rld\n') 65 finally: 66 shutil.rmtree(tmp_dir) 67 68 def testRunJsStringStdoutPiping(self): 69 tmp_dir = tempfile.mkdtemp() 70 try: 71 temp_file_name = os.path.join(tmp_dir, 'out_file') 72 with open(temp_file_name, 'w') as f: 73 vinn.RunJsString( 74 'print("Hello w0rld");\n', stdout=f) 75 with open(temp_file_name, 'r') as f: 76 self.assertEquals(f.read(), 'Hello w0rld\n') 77 finally: 78 shutil.rmtree(tmp_dir) 79 80 def testExecuteFileStdoutPiping(self): 81 file_path = self.GetTestFilePath('simple.js') 82 tmp_dir = tempfile.mkdtemp() 83 try: 84 temp_file_name = os.path.join(tmp_dir, 'out_file') 85 with open(temp_file_name, 'w') as f: 86 vinn.ExecuteFile(file_path, stdout=f) 87 with open(temp_file_name, 'r') as f: 88 self.assertEquals(f.read(), 'Hello W0rld from simple.js\n') 89 finally: 90 shutil.rmtree(tmp_dir) 91 92 def testRunFileStdoutPiping(self): 93 file_path = self.GetTestFilePath('simple.js') 94 tmp_dir = tempfile.mkdtemp() 95 try: 96 temp_file_name = os.path.join(tmp_dir, 'out_file') 97 with open(temp_file_name, 'w') as f: 98 vinn.RunFile(file_path, stdout=f) 99 with open(temp_file_name, 'r') as f: 100 self.assertEquals(f.read(), 'Hello W0rld from simple.js\n') 101 finally: 102 shutil.rmtree(tmp_dir) 103 104 def testSimpleJsExecution(self): 105 file_path = self.GetTestFilePath('print_file_content.js') 106 dummy_test_path = self.GetTestFilePath('dummy_test_file') 107 output = vinn.ExecuteFile(file_path, source_paths=[self.test_data_dir], 108 js_args=[dummy_test_path]) 109 self.assertIn( 110 'This is file contains only data for testing.\n1 2 3 4', output) 111 112 def testDuplicateSourcePaths(self): 113 output = vinn.ExecuteJsString( 114 "HTMLImportsLoader.loadHTML('/load_simple_html.html');", 115 source_paths=[self.test_data_dir]*100) 116 self.assertIn( 117 'load_simple_html.html is loaded', output) 118 119 def testJsFileLoadHtmlFile(self): 120 file_path = self.GetTestFilePath('load_simple_html.js') 121 output = vinn.ExecuteFile(file_path, source_paths=[self.test_data_dir]) 122 expected_output = ('File foo.html is loaded\n' 123 'x = 1\n' 124 "File foo.html's second script is loaded\n" 125 'x = 2\n' 126 'load_simple_html.js is loaded\n') 127 self.assertEquals(output, expected_output) 128 129 def testJsFileLoadJsFile(self): 130 file_path = self.GetTestFilePath('load_simple_js.js') 131 output = vinn.ExecuteFile(file_path, source_paths=[self.test_data_dir]) 132 expected_output = ('bar.js is loaded\n' 133 'load_simple_js.js is loaded\n') 134 self.assertEquals(output, expected_output) 135 136 def testHTMLFileLoadHTMLFile(self): 137 file_path = self.GetTestFilePath('load_simple_html.html') 138 output = vinn.ExecuteFile( 139 file_path, source_paths=[self.test_data_dir]) 140 expected_output = ('File foo.html is loaded\n' 141 'x = 1\n' 142 "File foo.html's second script is loaded\n" 143 'x = 2\n' 144 'bar.js is loaded\n' 145 'File load_simple_html.html is loaded\n') 146 self.assertEquals(output, expected_output) 147 148 def testQuit0Handling(self): 149 file_path = self.GetTestFilePath('quit_0_test.js') 150 res = vinn.RunFile(file_path, source_paths=[self.test_data_dir]) 151 self.assertEquals(res.returncode, 0) 152 153 def testQuit1Handling(self): 154 file_path = self.GetTestFilePath('quit_1_test.js') 155 res = vinn.RunFile(file_path, source_paths=[self.test_data_dir]) 156 self.assertEquals(res.returncode, 1) 157 158 def testQuit42Handling(self): 159 file_path = self.GetTestFilePath('quit_42_test.js') 160 res = vinn.RunFile(file_path, source_paths=[self.test_data_dir]) 161 self.assertEquals(res.returncode, 42) 162 163 def testQuit274Handling(self): 164 file_path = self.GetTestFilePath('quit_274_test.js') 165 res = vinn.RunFile(file_path, source_paths=[self.test_data_dir]) 166 self.assertEquals(res.returncode, 238) 167 168 def testErrorStackTraceJs(self): 169 file_path = self.GetTestFilePath('error_stack_test.js') 170 # error_stack_test.js imports load_simple_html.html 171 # load_simple_html.html imports foo.html 172 # foo.html imports error.js 173 # error.js defines maybeRaiseException() method that can raise exception 174 # foo.html defines maybeRaiseExceptionInFoo() method that calls 175 # maybeRaiseException() 176 # Finally, we call maybeRaiseExceptionInFoo() error_stack_test.js 177 # Exception log should capture these method calls' stack trace. 178 with self.assertRaises(RuntimeError) as context: 179 vinn.ExecuteFile(file_path, source_paths=[self.test_data_dir]) 180 181 # Assert error stack trace contain src files' info. 182 exception_message = context.exception.message 183 self.assertIn( 184 ('error.js:7: Error: Throw ERROR\n' 185 " throw new Error('Throw ERROR');"), exception_message) 186 self.AssertHasNamedFrame('maybeRaiseException', 'error.js:7', 187 exception_message) 188 self.AssertHasNamedFrame('global.maybeRaiseExceptionInFoo', 'foo.html:34', 189 exception_message) 190 self.AssertHasFrame('error_stack_test.js:14', exception_message) 191 192 def testErrorStackTraceHTML(self): 193 file_path = self.GetTestFilePath('error_stack_test.html') 194 # error_stack_test.html imports error_stack_test.js 195 # error_stack_test.js imports load_simple_html.html 196 # load_simple_html.html imports foo.html 197 # foo.html imports error.js 198 # error.js defines maybeRaiseException() method that can raise exception 199 # foo.html defines maybeRaiseExceptionInFoo() method that calls 200 # maybeRaiseException() 201 # Finally, we call maybeRaiseExceptionInFoo() error_stack_test.js 202 # Exception log should capture these method calls' stack trace. 203 with self.assertRaises(RuntimeError) as context: 204 vinn.ExecuteFile(file_path, source_paths=[self.test_data_dir]) 205 206 # Assert error stack trace contain src files' info. 207 exception_message = context.exception.message 208 self.assertIn( 209 ('error.js:7: Error: Throw ERROR\n' 210 " throw new Error('Throw ERROR');"), exception_message) 211 212 self.AssertHasNamedFrame('maybeRaiseException', 'error.js:7', 213 exception_message) 214 self.AssertHasNamedFrame('global.maybeRaiseExceptionInFoo', 'foo.html:34', 215 exception_message) 216 self.AssertHasFrame('error_stack_test.js:14', exception_message) 217 self.AssertHasNamedFrame('eval', 'error_stack_test.html:22', 218 exception_message) 219 220 def testStackTraceOfErroWhenLoadingHTML(self): 221 file_path = self.GetTestFilePath('load_error.html') 222 with self.assertRaises(RuntimeError) as context: 223 vinn.ExecuteFile(file_path, source_paths=[self.test_data_dir]) 224 225 # Assert error stack trace contain src files' info. 226 exception_message = context.exception.message 227 228 self.assertIn('Error: /does_not_exist.html not found', exception_message) 229 self.AssertHasNamedFrame('eval', 'load_error_2.html:21', exception_message) 230 self.AssertHasNamedFrame('eval', 'load_error.html:23', exception_message) 231 232 def testStackTraceOfErroWhenSyntaxErrorOccurs(self): 233 file_path = self.GetTestFilePath('syntax_error.html') 234 with self.assertRaises(RuntimeError) as context: 235 vinn.ExecuteFile(file_path, source_paths=[self.test_data_dir]) 236 237 # Assert error stack trace contain src files' info. 238 exception_message = context.exception.message 239 240 self.assertIn('syntax_error.html:23: SyntaxError: Unexpected identifier', 241 exception_message) 242 243 def testStackTraceOfErroWhenLoadingJS(self): 244 file_path = self.GetTestFilePath('load_js_error.html') 245 with self.assertRaises(RuntimeError) as context: 246 vinn.ExecuteFile(file_path, source_paths=[self.test_data_dir]) 247 248 # Assert error stack trace contain src files' info. 249 exception_message = context.exception.message 250 251 self.assertIn('Error: /does_not_exist.js not found', exception_message) 252 self.AssertHasNamedFrame('eval', 'load_js_error_2.html:20', 253 exception_message) 254 self.AssertHasNamedFrame('eval', 'load_js_error.html:22', 255 exception_message) 256 257 def testStrictError(self): 258 file_path = self.GetTestFilePath('non_strict_error.html') 259 with self.assertRaises(RuntimeError) as context: 260 vinn.ExecuteFile(file_path, source_paths=[self.test_data_dir]) 261 262 # Assert error stack trace contain src files' info. 263 exception_message = context.exception.message 264 265 self.assertIn('non_defined_variable is not defined', exception_message) 266 self.AssertHasNamedFrame('eval', 'non_strict_error.html:17', 267 exception_message) 268 269 def testConsolePolyfill(self): 270 self.assertEquals( 271 vinn.ExecuteJsString('console.log("hello", "world");'), 272 'hello world\n') 273 self.assertEquals( 274 vinn.ExecuteJsString('console.info("hello", "world");'), 275 'Info: hello world\n') 276 self.assertEquals( 277 vinn.ExecuteJsString('console.warn("hello", "world");'), 278 'Warning: hello world\n') 279 self.assertEquals( 280 vinn.ExecuteJsString('console.error("hello", "world");'), 281 'Error: hello world\n') 282 283 def testConsoleTimeEndAssertion(self): 284 file_path = self.GetTestFilePath('console_time_test.js') 285 try: 286 vinn.ExecuteFile(file_path) 287 except RuntimeError: 288 self.fail() 289 290 def testConsoleTime(self): 291 self.assertEquals( 292 vinn.ExecuteJsString('console.time("AA")'), 293 '') 294 295 def testConsoleTimeEndOutput(self): 296 output = vinn.ExecuteJsString('console.time("AA");console.timeEnd("AA")') 297 m = re.search('\d+\.\d+', output) 298 if not m: 299 sys.stderr.write('\nExpected to find output of timer AA') 300 self.fail() 301 a_duration = float(m.group()) 302 self.assertTrue(a_duration > 0.0) 303 output = vinn.ExecuteJsString("""console.time("BB"); 304 console.time("CC"); 305 console.timeEnd("CC"); 306 console.timeEnd("BB")""") 307 m = re.findall('(\d+\.\d+)', output) 308 if not m: 309 sys.stderr.write('\nExpected to find output of timer\n') 310 self.fail() 311 c_duration = float(m[0]) 312 b_duration = float(m[1]) 313 self.assertTrue(b_duration > c_duration) 314 315 316class PathUtilUnittest(unittest.TestCase): 317 def testPathUtil(self): 318 path_util_js_test = os.path.abspath(os.path.join( 319 os.path.dirname(__file__), 'path_utils_test.js')) 320 path_utils_js_dir = os.path.abspath( 321 os.path.join(os.path.dirname(__file__), 'path_utils.js')) 322 323 test_loading_js = """ 324 load(%s); 325 load(%s); 326 runTests(); 327 """ % (_EscapeJsString(path_utils_js_dir), 328 _EscapeJsString(path_util_js_test)) 329 330 res = vinn.RunJsString(test_loading_js) 331 self.assertEquals(res.returncode, 0) 332 333 334def _GetLineNumberOfSubstring(content, substring): 335 """ Return the line number of |substring| in |content|.""" 336 index = content.index(substring) 337 return content[:index].count('\n') + 1 338 339 340def _GenerateLineByLineDiff(actual, expected): 341 results = [] 342 expected_lines = expected.split('\n') 343 actual_lines = actual.split('\n') 344 max_num_lines = max(len(expected_lines), len(actual_lines)) 345 results.append('**Actual : num lines = %i' % len(actual_lines)) 346 results.append('**Expected : num lines = %i' % len(expected_lines)) 347 348 for i in xrange(0, max_num_lines): 349 expected_current_line = expected_lines[i] if i < len(expected_lines) else '' 350 actual_current_line = actual_lines[i] if i < len(actual_lines) else '' 351 if actual_current_line == expected_current_line: 352 continue 353 results.append('================= Line %s ======================' % (i + 1)) 354 results.append('**Actual : %s' % repr(actual_current_line)) 355 results.append('**Expected : %s' % repr(expected_current_line)) 356 return '\n'.join(results) 357 358 359class HTMLGeneratorTest(unittest.TestCase): 360 361 def AssertStringEquals(self, actual, expected): 362 if actual != expected: 363 message = 'Expected %s but got %s.\n' % (repr(expected), repr(actual)) 364 message += _GenerateLineByLineDiff(actual, expected) 365 self.fail(message) 366 367 def GetGeneratedJs(self, html_text): 368 tmp_dir = tempfile.mkdtemp() 369 try: 370 temp_file_name = os.path.join(tmp_dir, 'test.html') 371 with open(temp_file_name, 'w') as f: 372 f.write(html_text) 373 return vinn.ExecuteJsString( 374 'write(generateJsFromHTML(read(%s)));' % 375 _EscapeJsString(temp_file_name)) 376 finally: 377 shutil.rmtree(tmp_dir) 378 379 def testGenerateJsForD8RunnerSimpleHTMLImport(self): 380 html = '<link rel="import" href="/base/math.html">' 381 expected_js = "global.HTMLImportsLoader.loadHTML('/base/math.html');" 382 self.AssertStringEquals(self.GetGeneratedJs(html), expected_js) 383 384 def testGenerateJSForD8RunnerImportMultilineHTMLImport(self): 385 html = """ 386 <link rel="import" 387 href="/base/math.html">""" 388 expected_js = "\nglobal.HTMLImportsLoader.loadHTML('/base/math.html');" 389 self.AssertStringEquals(self.GetGeneratedJs(html), 390 expected_js) 391 392 def testGenerateJsForD8RunnerImportSimpleScriptWithSrc(self): 393 html = '<script src="/base/math.js"></script>' 394 expected_js = "global.HTMLImportsLoader.loadScript('/base/math.js');" 395 self.AssertStringEquals(self.GetGeneratedJs(html), 396 expected_js) 397 398 def testGenerateJsForD8RunnerImportMultilineScriptWithSrc(self): 399 html = """<script 400 type="text/javascript" 401 src="/base/math.js"> 402 </script>""" 403 expected_js = """global.HTMLImportsLoader.loadScript('/base/math.js'); 404 405 406 """ 407 self.AssertStringEquals(self.GetGeneratedJs(html), 408 expected_js) 409 410 def testGenerateJsForD8RunnerWithMixedMultipleImport(self): 411 html = """ 412<link rel="import" href="/base.html"><link rel="import" href="/base64.html"> 413<link rel="import" 414 href="/base/math.html"><script 415 type="text/javascript" 416 417 src="/base/3d.js"> 418 </script> 419 420<script src="/base/math.js"></script> 421 422 <link rel="import" 423 href="/base/random.html"> 424""" 425 expected_js = (""" 426global.HTMLImportsLoader.loadHTML('/base.html');global.HTMLImportsLoader.loadHTML('/base64.html'); 427global.HTMLImportsLoader.loadHTML('/base/math.html'); 428global.HTMLImportsLoader.loadScript('/base/3d.js'); 429 430 431 432 """ + """ 433 434global.HTMLImportsLoader.loadScript('/base/math.js'); 435 436global.HTMLImportsLoader.loadHTML('/base/random.html');""") 437 self.AssertStringEquals(self.GetGeneratedJs(html), 438 expected_js) 439 440 def testGenerateJsForD8RunnerImportWithSimpleContent(self): 441 html = """<script> 442 var html_lines = [ 443 '<script>', 444 '< /script>', 445 ]; 446 </script> 447 """ 448 expected_js = """ 449 var html_lines = [ 450 '<script>', 451 '< /script>', 452 ]; 453 """ 454 self.AssertStringEquals(self.GetGeneratedJs(html), 455 expected_js) 456 457 def testGenerateJsForD8RunnerImportWithEscapedScriptTag(self): 458 html = """<script> 459var s = ("<") + "<\/script>"; 460var x = 100; 461 </script> 462 """ 463 expected_js = """ 464var s = ("<") + "<\/script>"; 465var x = 100; 466 """ 467 self.AssertStringEquals(self.GetGeneratedJs(html), 468 expected_js) 469 470 def testGenerateJsForD8RunnerImportWithSrcAndSimpleContent(self): 471 html = """<script 472 src="/base.js">var html_lines = [ 473 '<script>', 474 '< /script>', 475 ]; 476 </script> 477 """ 478 expected_js = """global.HTMLImportsLoader.loadScript('/base.js'); 479var html_lines = [ 480 '<script>', 481 '< /script>', 482 ]; 483 """ 484 self.AssertStringEquals(self.GetGeneratedJs(html), 485 expected_js) 486 487 def testGenerateJsForD8RunnerImportComplex(self): 488 html = """<!DOCTYPE html> 489<!-- 490Copyright (c) 2014 The Chromium Authors. All rights reserved. 491Use of this source code is governed by a BSD-style license that can be 492found in the LICENSE file. 493--> 494 <link rel="import" href="/base/math.html"><script>var x = 1;</script> 495 <script src="/base/computer.js"> 496 var linux = os.system; // line number of this is 9 497 </script> 498 <link rel="import" href="/base/physics.html"> 499 500 <script> 501 var html_lines = [ 502 '<script>', 503 '< /script>', 504 ]; 505 function foo() { 506 var y = [ 507 1, 508 2, // line number of this is 21 509 3, 510 4 511 ]; 512 } 513 </script> 514 515 <link rel="import" href="/base/this_is_line_28.html"> 516 <script> 517 var i = '<link rel="import" href="/base/math.html">'; 518 </script> 519 """ 520 expected_js = """ 521 522 523 524 525 526global.HTMLImportsLoader.loadHTML('/base/math.html');var x = 1; 527global.HTMLImportsLoader.loadScript('/base/computer.js'); 528 var linux = os.system; // line number of this is 9 529 """ + """ 530global.HTMLImportsLoader.loadHTML('/base/physics.html'); 531 532 533 var html_lines = [ 534 '<script>', 535 '< /script>', 536 ]; 537 function foo() { 538 var y = [ 539 1, 540 2, // line number of this is 21 541 3, 542 4 543 ]; 544 } 545 """ + """ 546 547global.HTMLImportsLoader.loadHTML('/base/this_is_line_28.html'); 548 549 var i = '<link rel="import" href="/base/math.html">'; 550 """ 551 552 generated_js = self.GetGeneratedJs(html) 553 self.AssertStringEquals( 554 _GetLineNumberOfSubstring(generated_js, '// line number of this is 9'), 555 9) 556 self.AssertStringEquals( 557 _GetLineNumberOfSubstring(generated_js, '// line number of this is 21'), 558 21) 559 self.AssertStringEquals( 560 _GetLineNumberOfSubstring(generated_js, 'this_is_line_28.html'), 561 28) 562 self.AssertStringEquals(generated_js, expected_js) 563 564 565class VinnV8ArgsTest(unittest.TestCase): 566 567 @classmethod 568 def setUpClass(cls): 569 cls.test_data_dir = os.path.abspath( 570 os.path.join(os.path.dirname(__file__), 'test_data')) 571 572 def GetTestFilePath(self, file_name): 573 return os.path.join(self.test_data_dir, file_name) 574 575 def setUp(self): 576 self.patcher = mock.patch('_vinn.subprocess.Popen') 577 self.mock_popen = self.patcher.start() 578 mock_rv = mock.Mock() 579 mock_rv.returncode = 0 580 # Communicate() returns [stdout, stderr] 581 mock_rv.communicate.return_value = ['', None] 582 self.mock_popen.return_value = mock_rv 583 584 def tearDown(self): 585 mock.patch.stopall() 586 587 def testRunJsStringWithV8Args(self): 588 vinn.RunJsString('var x = 1;', v8_args=['--foo', '--bar=True']) 589 v8_args = self.mock_popen.call_args[0][0] 590 self.assertIn('--foo', v8_args) 591 self.assertIn('--bar=True', v8_args) 592 593 def testExecuteJsStringWithV8Args(self): 594 vinn.ExecuteJsString('var x = 1;', v8_args=['--foo', '--bar=True']) 595 v8_args = self.mock_popen.call_args[0][0] 596 self.assertIn('--foo', v8_args) 597 self.assertIn('--bar=True', v8_args) 598 599 def testRunFileWithV8Args(self): 600 file_path = self.GetTestFilePath('simple.js') 601 vinn.RunFile(file_path, v8_args=['--foo', '--bar=True']) 602 v8_args = self.mock_popen.call_args[0][0] 603 self.assertIn('--foo', v8_args) 604 605 def testExecuteFileWithV8Args(self): 606 file_path = self.GetTestFilePath('simple.js') 607 vinn.ExecuteFile(file_path, v8_args=['--foo', '--bar=True']) 608 v8_args = self.mock_popen.call_args[0][0] 609 self.assertIn('--foo', v8_args) 610 self.assertIn('--bar=True', v8_args) 611