| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent a1719a9 commit 01b314d
7 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -111,7 +111,7 @@ v8: | |||
| 111 | 111 | $(MAKE) -C deps/v8 $(V8_ARCH) $(V8_BUILD_OPTIONS) | |
| 112 | 112 | ||
| 113 | 113 | test: | cctest # Depends on 'all'. | |
| 114 | - $(PYTHON) tools/test.py --mode=release doctool message parallel sequential -J | ||
| 114 | + $(PYTHON) tools/test.py --mode=release doctool message pseudo-tty parallel sequential -J | ||
| 115 | 115 | $(MAKE) jslint | |
| 116 | 116 | $(MAKE) cpplint | |
| 117 | 117 | ||
@@ -167,7 +167,7 @@ test-all-valgrind: test-build | |||
| 167 | 167 | $(PYTHON) tools/test.py --mode=debug,release --valgrind | |
| 168 | 168 | ||
| 169 | 169 | CI_NATIVE_SUITES := addons | |
| 170 | - CI_JS_SUITES := doctool message parallel sequential | ||
| 170 | + CI_JS_SUITES := doctool message parallel pseudo-tty sequential | ||
| 171 | 171 | ||
| 172 | 172 | # Build and test addons without building anything else | |
| 173 | 173 | test-ci-native: | test/addons/.buildstamp | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,15 @@ | |||
| 1 | + // https://github.com/nodejs/node/issues/6456#issuecomment-219320599 | ||
| 2 | + // https://gist.github.com/isaacs/1495b91ec66b21d30b10572d72ad2cdd | ||
| 3 | + 'use strict'; | ||
| 4 | + require('../common'); | ||
| 5 | + | ||
| 6 | + // 1000 bytes wrapped at 50 columns | ||
| 7 | + // \n turns into a double-byte character | ||
| 8 | + // (48 + {2}) * 20 = 1000 | ||
| 9 | + var out = ('o'.repeat(48) + '\n').repeat(20); | ||
| 10 | + // Add the remaining 24 bytes and terminate with an 'O'. | ||
| 11 | + // This results in 1025 bytes, just enough to overflow the 1kb OS X TTY buffer. | ||
| 12 | + out += 'o'.repeat(24) + 'O'; | ||
| 13 | + | ||
| 14 | + process.stdout.write(out); | ||
| 15 | + process.exit(0); | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,21 @@ | |||
| 1 | + oooooooooooooooooooooooooooooooooooooooooooooooo | ||
| 2 | + oooooooooooooooooooooooooooooooooooooooooooooooo | ||
| 3 | + oooooooooooooooooooooooooooooooooooooooooooooooo | ||
| 4 | + oooooooooooooooooooooooooooooooooooooooooooooooo | ||
| 5 | + oooooooooooooooooooooooooooooooooooooooooooooooo | ||
| 6 | + oooooooooooooooooooooooooooooooooooooooooooooooo | ||
| 7 | + oooooooooooooooooooooooooooooooooooooooooooooooo | ||
| 8 | + oooooooooooooooooooooooooooooooooooooooooooooooo | ||
| 9 | + oooooooooooooooooooooooooooooooooooooooooooooooo | ||
| 10 | + oooooooooooooooooooooooooooooooooooooooooooooooo | ||
| 11 | + oooooooooooooooooooooooooooooooooooooooooooooooo | ||
| 12 | + oooooooooooooooooooooooooooooooooooooooooooooooo | ||
| 13 | + oooooooooooooooooooooooooooooooooooooooooooooooo | ||
| 14 | + oooooooooooooooooooooooooooooooooooooooooooooooo | ||
| 15 | + oooooooooooooooooooooooooooooooooooooooooooooooo | ||
| 16 | + oooooooooooooooooooooooooooooooooooooooooooooooo | ||
| 17 | + oooooooooooooooooooooooooooooooooooooooooooooooo | ||
| 18 | + oooooooooooooooooooooooooooooooooooooooooooooooo | ||
| 19 | + oooooooooooooooooooooooooooooooooooooooooooooooo | ||
| 20 | + oooooooooooooooooooooooooooooooooooooooooooooooo | ||
| 21 | + ooooooooooooooooooooooooO | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,17 @@ | |||
| 1 | + // https://github.com/nodejs/node/issues/6456#issuecomment-219320599 | ||
| 2 | + // https://gist.github.com/isaacs/1495b91ec66b21d30b10572d72ad2cdd | ||
| 3 | + 'use strict'; | ||
| 4 | + require('../common'); | ||
| 5 | + | ||
| 6 | + // 1000 bytes wrapped at 50 columns | ||
| 7 | + // \n turns into a double-byte character | ||
| 8 | + // (48 + {2}) * 20 = 1000 | ||
| 9 | + var out = ('o'.repeat(48) + '\n').repeat(20); | ||
| 10 | + // Add the remaining 24 bytes and terminate with an 'O'. | ||
| 11 | + // This results in 1025 bytes, just enough to overflow the 1kb OS X TTY buffer. | ||
| 12 | + out += 'o'.repeat(24) + 'O'; | ||
| 13 | + | ||
| 14 | + const err = '__This is some stderr__'; | ||
| 15 | + | ||
| 16 | + process.stdout.write(out); | ||
| 17 | + process.stderr.write(err); | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,21 @@ | |||
| 1 | + oooooooooooooooooooooooooooooooooooooooooooooooo | ||
| 2 | + oooooooooooooooooooooooooooooooooooooooooooooooo | ||
| 3 | + oooooooooooooooooooooooooooooooooooooooooooooooo | ||
| 4 | + oooooooooooooooooooooooooooooooooooooooooooooooo | ||
| 5 | + oooooooooooooooooooooooooooooooooooooooooooooooo | ||
| 6 | + oooooooooooooooooooooooooooooooooooooooooooooooo | ||
| 7 | + oooooooooooooooooooooooooooooooooooooooooooooooo | ||
| 8 | + oooooooooooooooooooooooooooooooooooooooooooooooo | ||
| 9 | + oooooooooooooooooooooooooooooooooooooooooooooooo | ||
| 10 | + oooooooooooooooooooooooooooooooooooooooooooooooo | ||
| 11 | + oooooooooooooooooooooooooooooooooooooooooooooooo | ||
| 12 | + oooooooooooooooooooooooooooooooooooooooooooooooo | ||
| 13 | + oooooooooooooooooooooooooooooooooooooooooooooooo | ||
| 14 | + oooooooooooooooooooooooooooooooooooooooooooooooo | ||
| 15 | + oooooooooooooooooooooooooooooooooooooooooooooooo | ||
| 16 | + oooooooooooooooooooooooooooooooooooooooooooooooo | ||
| 17 | + oooooooooooooooooooooooooooooooooooooooooooooooo | ||
| 18 | + oooooooooooooooooooooooooooooooooooooooooooooooo | ||
| 19 | + oooooooooooooooooooooooooooooooooooooooooooooooo | ||
| 20 | + oooooooooooooooooooooooooooooooooooooooooooooooo | ||
| 21 | + ooooooooooooooooooooooooO__This is some stderr__ | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,161 @@ | |||
| 1 | + # Copyright 2008 the V8 project authors. All rights reserved. | ||
| 2 | + # Redistribution and use in source and binary forms, with or without | ||
| 3 | + # modification, are permitted provided that the following conditions are | ||
| 4 | + # met: | ||
| 5 | + # | ||
| 6 | + # * Redistributions of source code must retain the above copyright | ||
| 7 | + # notice, this list of conditions and the following disclaimer. | ||
| 8 | + # * Redistributions in binary form must reproduce the above | ||
| 9 | + # copyright notice, this list of conditions and the following | ||
| 10 | + # disclaimer in the documentation and/or other materials provided | ||
| 11 | + # with the distribution. | ||
| 12 | + # * Neither the name of Google Inc. nor the names of its | ||
| 13 | + # contributors may be used to endorse or promote products derived | ||
| 14 | + # from this software without specific prior written permission. | ||
| 15 | + # | ||
| 16 | + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| 17 | + # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| 18 | + # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| 19 | + # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| 20 | + # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| 21 | + # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
| 22 | + # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
| 23 | + # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
| 24 | + # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| 25 | + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| 26 | + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 27 | + | ||
| 28 | + import test | ||
| 29 | + import os | ||
| 30 | + from os.path import join, exists, basename, isdir | ||
| 31 | + import re | ||
| 32 | + import utils | ||
| 33 | + | ||
| 34 | + FLAGS_PATTERN = re.compile(r"//\s+Flags:(.*)") | ||
| 35 | + | ||
| 36 | + class TTYTestCase(test.TestCase): | ||
| 37 | + | ||
| 38 | + def __init__(self, path, file, expected, arch, mode, context, config): | ||
| 39 | + super(TTYTestCase, self).__init__(context, path, arch, mode) | ||
| 40 | + self.file = file | ||
| 41 | + self.expected = expected | ||
| 42 | + self.config = config | ||
| 43 | + self.arch = arch | ||
| 44 | + self.mode = mode | ||
| 45 | + | ||
| 46 | + def IgnoreLine(self, str): | ||
| 47 | + """Ignore empty lines and valgrind output.""" | ||
| 48 | + if not str.strip(): return True | ||
| 49 | + else: return str.startswith('==') or str.startswith('**') | ||
| 50 | + | ||
| 51 | + def IsFailureOutput(self, output): | ||
| 52 | + f = file(self.expected) | ||
| 53 | + # Convert output lines to regexps that we can match | ||
| 54 | + env = { 'basename': basename(self.file) } | ||
| 55 | + patterns = [ ] | ||
| 56 | + for line in f: | ||
| 57 | + if not line.strip(): | ||
| 58 | + continue | ||
| 59 | + pattern = re.escape(line.rstrip() % env) | ||
| 60 | + pattern = pattern.replace('\\*', '.*') | ||
| 61 | + pattern = '^%s$' % pattern | ||
| 62 | + patterns.append(pattern) | ||
| 63 | + # Compare actual output with the expected | ||
| 64 | + raw_lines = (output.stdout + output.stderr).split('\n') | ||
| 65 | + outlines = [ s.strip() for s in raw_lines if not self.IgnoreLine(s) ] | ||
| 66 | + if len(outlines) != len(patterns): | ||
| 67 | + print "length differs." | ||
| 68 | + print "expect=%d" % len(patterns) | ||
| 69 | + print "actual=%d" % len(outlines) | ||
| 70 | + print "patterns:" | ||
| 71 | + for i in xrange(len(patterns)): | ||
| 72 | + print "pattern = %s" % patterns[i] | ||
| 73 | + print "outlines:" | ||
| 74 | + for i in xrange(len(outlines)): | ||
| 75 | + print "outline = %s" % outlines[i] | ||
| 76 | + return True | ||
| 77 | + for i in xrange(len(patterns)): | ||
| 78 | + if not re.match(patterns[i], outlines[i]): | ||
| 79 | + print "match failed" | ||
| 80 | + print "line=%d" % i | ||
| 81 | + print "expect=%s" % patterns[i] | ||
| 82 | + print "actual=%s" % outlines[i] | ||
| 83 | + return True | ||
| 84 | + return False | ||
| 85 | + | ||
| 86 | + def GetLabel(self): | ||
| 87 | + return "%s %s" % (self.mode, self.GetName()) | ||
| 88 | + | ||
| 89 | + def GetName(self): | ||
| 90 | + return self.path[-1] | ||
| 91 | + | ||
| 92 | + def GetCommand(self): | ||
| 93 | + result = [self.config.context.GetVm(self.arch, self.mode)] | ||
| 94 | + source = open(self.file).read() | ||
| 95 | + flags_match = FLAGS_PATTERN.search(source) | ||
| 96 | + if flags_match: | ||
| 97 | + result += flags_match.group(1).strip().split() | ||
| 98 | + result.append(self.file) | ||
| 99 | + return result | ||
| 100 | + | ||
| 101 | + def GetSource(self): | ||
| 102 | + return (open(self.file).read() | ||
| 103 | + + "\n--- expected output ---\n" | ||
| 104 | + + open(self.expected).read()) | ||
| 105 | + | ||
| 106 | + def RunCommand(self, command, env): | ||
| 107 | + full_command = self.context.processor(command) | ||
| 108 | + output = test.Execute(full_command, | ||
| 109 | + self.context, | ||
| 110 | + self.context.GetTimeout(self.mode), | ||
| 111 | + env, | ||
| 112 | + True) | ||
| 113 | + self.Cleanup() | ||
| 114 | + return test.TestOutput(self, | ||
| 115 | + full_command, | ||
| 116 | + output, | ||
| 117 | + self.context.store_unexpected_output) | ||
| 118 | + | ||
| 119 | + | ||
| 120 | + class TTYTestConfiguration(test.TestConfiguration): | ||
| 121 | + | ||
| 122 | + def __init__(self, context, root): | ||
| 123 | + super(TTYTestConfiguration, self).__init__(context, root) | ||
| 124 | + | ||
| 125 | + def Ls(self, path): | ||
| 126 | + if isdir(path): | ||
| 127 | + return [f[:-3] for f in os.listdir(path) if f.endswith('.js')] | ||
| 128 | + else: | ||
| 129 | + return [] | ||
| 130 | + | ||
| 131 | + def ListTests(self, current_path, path, arch, mode): | ||
| 132 | + all_tests = [current_path + [t] for t in self.Ls(self.root)] | ||
| 133 | + result = [] | ||
| 134 | + # Skip these tests on Windows, as pseudo terminals are not available | ||
| 135 | + if utils.IsWindows(): | ||
| 136 | + print ("Skipping pseudo-tty tests, as pseudo terminals are not available" | ||
| 137 | + " on Windows.") | ||
| 138 | + return result | ||
| 139 | + for test in all_tests: | ||
| 140 | + if self.Contains(path, test): | ||
| 141 | + file_prefix = join(self.root, reduce(join, test[1:], "")) | ||
| 142 | + file_path = file_prefix + ".js" | ||
| 143 | + output_path = file_prefix + ".out" | ||
| 144 | + if not exists(output_path): | ||
| 145 | + print "Could not find %s" % output_path | ||
| 146 | + continue | ||
| 147 | + result.append(TTYTestCase(test, file_path, output_path, | ||
| 148 | + arch, mode, self.context, self)) | ||
| 149 | + return result | ||
| 150 | + | ||
| 151 | + def GetBuildRequirements(self): | ||
| 152 | + return ['sample', 'sample=shell'] | ||
| 153 | + | ||
| 154 | + def GetTestStatus(self, sections, defs): | ||
| 155 | + status_file = join(self.root, 'message.status') | ||
| 156 | + if exists(status_file): | ||
| 157 | + test.ReadConfigurationInto(status_file, sections, defs) | ||
| 158 | + | ||
| 159 | + | ||
| 160 | + def GetConfiguration(context, root): | ||
| 161 | + return TTYTestConfiguration(context, root) | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -575,11 +575,17 @@ def RunProcess(context, timeout, args, **rest): | |||
| 575 | 575 | error_mode = SEM_NOGPFAULTERRORBOX; | |
| 576 | 576 | prev_error_mode = Win32SetErrorMode(error_mode); | |
| 577 | 577 | Win32SetErrorMode(error_mode | prev_error_mode); | |
| 578 | + | ||
| 579 | + faketty = rest.pop('faketty', False) | ||
| 580 | + pty_out = rest.pop('pty_out') | ||
| 581 | + | ||
| 578 | 582 | process = subprocess.Popen( | |
| 579 | 583 | shell = utils.IsWindows(), | |
| 580 | 584 | args = popen_args, | |
| 581 | 585 | **rest | |
| 582 | 586 | ) | |
| 587 | + if faketty: | ||
| 588 | + os.close(rest['stdout']) | ||
| 583 | 589 | if utils.IsWindows() and context.suppress_dialogs and prev_error_mode != SEM_INVALID_VALUE: | |
| 584 | 590 | Win32SetErrorMode(prev_error_mode) | |
| 585 | 591 | # Compute the end time - if the process crosses this limit we | |
@@ -591,6 +597,29 @@ def RunProcess(context, timeout, args, **rest): | |||
| 591 | 597 | # loop and keep track of whether or not it times out. | |
| 592 | 598 | exit_code = None | |
| 593 | 599 | sleep_time = INITIAL_SLEEP_TIME | |
| 600 | + output = '' | ||
| 601 | + if faketty: | ||
| 602 | + while True: | ||
| 603 | + if time.time() >= end_time: | ||
| 604 | + # Kill the process and wait for it to exit. | ||
| 605 | + KillProcessWithID(process.pid) | ||
| 606 | + exit_code = process.wait() | ||
| 607 | + timed_out = True | ||
| 608 | + break | ||
| 609 | + | ||
| 610 | + # source: http://stackoverflow.com/a/12471855/1903116 | ||
| 611 | + # related: http://stackoverflow.com/q/11165521/1903116 | ||
| 612 | + try: | ||
| 613 | + data = os.read(pty_out, 9999) | ||
| 614 | + except OSError as e: | ||
| 615 | + if e.errno != errno.EIO: | ||
| 616 | + raise | ||
| 617 | + break # EIO means EOF on some systems | ||
| 618 | + else: | ||
| 619 | + if not data: # EOF | ||
| 620 | + break | ||
| 621 | + output += data | ||
| 622 | + | ||
| 594 | 623 | while exit_code is None: | |
| 595 | 624 | if (not end_time is None) and (time.time() >= end_time): | |
| 596 | 625 | # Kill the process and wait for it to exit. | |
@@ -603,7 +632,7 @@ def RunProcess(context, timeout, args, **rest): | |||
| 603 | 632 | sleep_time = sleep_time * SLEEP_TIME_FACTOR | |
| 604 | 633 | if sleep_time > MAX_SLEEP_TIME: | |
| 605 | 634 | sleep_time = MAX_SLEEP_TIME | |
| 606 | - return (process, exit_code, timed_out) | ||
| 635 | + return (process, exit_code, timed_out, output) | ||
| 607 | 636 | ||
| 608 | 637 | ||
| 609 | 638 | def PrintError(str): | |
@@ -625,29 +654,43 @@ def CheckedUnlink(name): | |||
| 625 | 654 | PrintError("os.unlink() " + str(e)) | |
| 626 | 655 | break | |
| 627 | 656 | ||
| 628 | - def Execute(args, context, timeout=None, env={}): | ||
| 629 | - (fd_out, outname) = tempfile.mkstemp() | ||
| 630 | - (fd_err, errname) = tempfile.mkstemp() | ||
| 657 | + def Execute(args, context, timeout=None, env={}, faketty=False): | ||
| 658 | + if faketty: | ||
| 659 | + import pty | ||
| 660 | + (out_master, fd_out) = pty.openpty() | ||
| 661 | + fd_err = fd_out | ||
| 662 | + pty_out = out_master | ||
| 663 | + else: | ||
| 664 | + (fd_out, outname) = tempfile.mkstemp() | ||
| 665 | + (fd_err, errname) = tempfile.mkstemp() | ||
| 666 | + pty_out = None | ||
| 631 | 667 | ||
| 632 | 668 | # Extend environment | |
| 633 | 669 | env_copy = os.environ.copy() | |
| 634 | 670 | for key, value in env.iteritems(): | |
| 635 | 671 | env_copy[key] = value | |
| 636 | 672 | ||
| 637 | - (process, exit_code, timed_out) = RunProcess( | ||
| 673 | + (process, exit_code, timed_out, output) = RunProcess( | ||
| 638 | 674 | context, | |
| 639 | 675 | timeout, | |
| 640 | 676 | args = args, | |
| 641 | 677 | stdout = fd_out, | |
| 642 | 678 | stderr = fd_err, | |
| 643 | - env = env_copy | ||
| 679 | + env = env_copy, | ||
| 680 | + faketty = faketty, | ||
| 681 | + pty_out = pty_out | ||
| 644 | 682 | ) | |
| 645 | - os.close(fd_out) | ||
| 646 | - os.close(fd_err) | ||
| 647 | - output = file(outname).read() | ||
| 648 | - errors = file(errname).read() | ||
| 649 | - CheckedUnlink(outname) | ||
| 650 | - CheckedUnlink(errname) | ||
| 683 | + if faketty: | ||
| 684 | + os.close(out_master) | ||
| 685 | + errors = '' | ||
| 686 | + else: | ||
| 687 | + os.close(fd_out) | ||
| 688 | + os.close(fd_err) | ||
| 689 | + output = file(outname).read() | ||
| 690 | + errors = file(errname).read() | ||
| 691 | + CheckedUnlink(outname) | ||
| 692 | + CheckedUnlink(errname) | ||
| 693 | + | ||
| 651 | 694 | return CommandOutput(exit_code, timed_out, output, errors) | |
| 652 | 695 | ||
| 653 | 696 | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments