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