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 28from __future__ import print_function 29 30import test 31import os 32from os.path import join, exists, basename, dirname, isdir 33import re 34import sys 35import utils 36from functools import reduce 37 38FLAGS_PATTERN = re.compile(r"//\s+Flags:(.*)") 39PTY_HELPER = join(dirname(__file__), 'pty_helper.py') 40 41class TTYTestCase(test.TestCase): 42 43 def __init__(self, path, file, expected, input_arg, arch, mode, context, config): 44 super(TTYTestCase, self).__init__(context, path, arch, mode) 45 self.file = file 46 self.expected = expected 47 self.input = input_arg 48 self.config = config 49 self.arch = arch 50 self.mode = mode 51 self.parallel = True 52 53 def IgnoreLine(self, str_arg): 54 """Ignore empty lines and valgrind output.""" 55 if not str_arg.strip(): return True 56 else: return str_arg.startswith('==') or str_arg.startswith('**') 57 58 def IsFailureOutput(self, output): 59 f = open(self.expected) 60 # Convert output lines to regexps that we can match 61 env = { 'basename': basename(self.file) } 62 patterns = [ ] 63 for line in f: 64 if not line.strip(): 65 continue 66 pattern = re.escape(line.rstrip() % env) 67 pattern = pattern.replace('\\*', '.*') 68 pattern = '^%s$' % pattern 69 patterns.append(pattern) 70 # Compare actual output with the expected 71 raw_lines = (output.stdout + output.stderr).split('\n') 72 outlines = [ s.rstrip() for s in raw_lines if not self.IgnoreLine(s) ] 73 if len(outlines) != len(patterns): 74 print(" length differs.") 75 print("expect=%d" % len(patterns)) 76 print("actual=%d" % len(outlines)) 77 print("patterns:") 78 for i in range(len(patterns)): 79 print("pattern = %s" % patterns[i]) 80 print("outlines:") 81 for i in range(len(outlines)): 82 print("outline = %s" % outlines[i]) 83 return True 84 for i in range(len(patterns)): 85 if not re.match(patterns[i], outlines[i]): 86 print(" match failed") 87 print("line=%d" % i) 88 print("expect=%s" % patterns[i]) 89 print("actual=%s" % outlines[i]) 90 return True 91 return False 92 93 def GetLabel(self): 94 return "%s %s" % (self.mode, self.GetName()) 95 96 def GetName(self): 97 return self.path[-1] 98 99 def GetCommand(self): 100 result = [self.config.context.GetVm(self.arch, self.mode)] 101 source = open(self.file).read() 102 flags_match = FLAGS_PATTERN.search(source) 103 if flags_match: 104 result += flags_match.group(1).strip().split() 105 result.append(self.file) 106 return result 107 108 def GetSource(self): 109 return (open(self.file).read() 110 + "\n--- expected output ---\n" 111 + open(self.expected).read()) 112 113 def RunCommand(self, command, env): 114 fd = None 115 if self.input is not None and exists(self.input): 116 fd = os.open(self.input, os.O_RDONLY) 117 full_command = self.context.processor(command) 118 full_command = [sys.executable, PTY_HELPER] + full_command 119 output = test.Execute(full_command, 120 self.context, 121 self.context.GetTimeout(self.mode), 122 env, 123 stdin=fd) 124 if fd is not None: 125 os.close(fd) 126 return test.TestOutput(self, 127 full_command, 128 output, 129 self.context.store_unexpected_output) 130 131 132class TTYTestConfiguration(test.TestConfiguration): 133 def Ls(self, path): 134 if isdir(path): 135 return [f[:-3] for f in os.listdir(path) if f.endswith('.js')] 136 else: 137 return [] 138 139 def ListTests(self, current_path, path, arch, mode): 140 all_tests = [current_path + [t] for t in self.Ls(self.root)] 141 result = [] 142 # Skip these tests on Windows, as pseudo terminals are not available 143 if utils.IsWindows(): 144 print ("Skipping pseudo-tty tests, as pseudo terminals are not available" 145 " on Windows.") 146 return result 147 for tst in all_tests: 148 if self.Contains(path, tst): 149 file_prefix = join(self.root, reduce(join, tst[1:], "")) 150 file_path = file_prefix + ".js" 151 input_path = file_prefix + ".in" 152 output_path = file_prefix + ".out" 153 if not exists(output_path): 154 raise Exception("Could not find %s" % output_path) 155 result.append(TTYTestCase(tst, file_path, output_path, 156 input_path, arch, mode, self.context, self)) 157 return result 158 159 def GetBuildRequirements(self): 160 return ['sample', 'sample=shell'] 161 162 163def GetConfiguration(context, root): 164 return TTYTestConfiguration(context, root, 'pseudo-tty') 165