1#!/usr/bin/env python3 2 3"""Runs ./ninja and checks if the output is correct. 4 5In order to simulate a smart terminal it uses the 'script' command. 6""" 7 8import os 9import platform 10import subprocess 11import sys 12import tempfile 13import unittest 14 15default_env = dict(os.environ) 16if 'NINJA_STATUS' in default_env: 17 del default_env['NINJA_STATUS'] 18if 'CLICOLOR_FORCE' in default_env: 19 del default_env['CLICOLOR_FORCE'] 20default_env['TERM'] = '' 21NINJA_PATH = os.path.abspath('./ninja') 22 23def run(build_ninja, flags='', pipe=False, env=default_env): 24 with tempfile.TemporaryDirectory() as d: 25 os.chdir(d) 26 with open('build.ninja', 'w') as f: 27 f.write(build_ninja) 28 f.flush() 29 ninja_cmd = '{} {}'.format(NINJA_PATH, flags) 30 try: 31 if pipe: 32 output = subprocess.check_output([ninja_cmd], shell=True, env=env) 33 elif platform.system() == 'Darwin': 34 output = subprocess.check_output(['script', '-q', '/dev/null', 'bash', '-c', ninja_cmd], 35 env=env) 36 else: 37 output = subprocess.check_output(['script', '-qfec', ninja_cmd, '/dev/null'], 38 env=env) 39 except subprocess.CalledProcessError as err: 40 sys.stdout.buffer.write(err.output) 41 raise err 42 final_output = '' 43 for line in output.decode('utf-8').splitlines(True): 44 if len(line) > 0 and line[-1] == '\r': 45 continue 46 final_output += line.replace('\r', '') 47 return final_output 48 49@unittest.skipIf(platform.system() == 'Windows', 'These test methods do not work on Windows') 50class Output(unittest.TestCase): 51 BUILD_SIMPLE_ECHO = '\n'.join(( 52 'rule echo', 53 ' command = printf "do thing"', 54 ' description = echo $out', 55 '', 56 'build a: echo', 57 '', 58 )) 59 60 def test_issue_1418(self): 61 self.assertEqual(run( 62'''rule echo 63 command = sleep $delay && echo $out 64 description = echo $out 65 66build a: echo 67 delay = 3 68build b: echo 69 delay = 2 70build c: echo 71 delay = 1 72''', '-j3'), 73'''[1/3] echo c\x1b[K 74c 75[2/3] echo b\x1b[K 76b 77[3/3] echo a\x1b[K 78a 79''') 80 81 def test_issue_1214(self): 82 print_red = '''rule echo 83 command = printf '\x1b[31mred\x1b[0m' 84 description = echo $out 85 86build a: echo 87''' 88 # Only strip color when ninja's output is piped. 89 self.assertEqual(run(print_red), 90'''[1/1] echo a\x1b[K 91\x1b[31mred\x1b[0m 92''') 93 self.assertEqual(run(print_red, pipe=True), 94'''[1/1] echo a 95red 96''') 97 # Even in verbose mode, colors should still only be stripped when piped. 98 self.assertEqual(run(print_red, flags='-v'), 99'''[1/1] printf '\x1b[31mred\x1b[0m' 100\x1b[31mred\x1b[0m 101''') 102 self.assertEqual(run(print_red, flags='-v', pipe=True), 103'''[1/1] printf '\x1b[31mred\x1b[0m' 104red 105''') 106 107 # CLICOLOR_FORCE=1 can be used to disable escape code stripping. 108 env = default_env.copy() 109 env['CLICOLOR_FORCE'] = '1' 110 self.assertEqual(run(print_red, pipe=True, env=env), 111'''[1/1] echo a 112\x1b[31mred\x1b[0m 113''') 114 115 def test_pr_1685(self): 116 # Running those tools without .ninja_deps and .ninja_log shouldn't fail. 117 self.assertEqual(run('', flags='-t recompact'), '') 118 self.assertEqual(run('', flags='-t restat'), '') 119 120 def test_status(self): 121 self.assertEqual(run(''), 'ninja: no work to do.\n') 122 self.assertEqual(run('', pipe=True), 'ninja: no work to do.\n') 123 124 def test_ninja_status_default(self): 125 'Do we show the default status by default?' 126 self.assertEqual(run(Output.BUILD_SIMPLE_ECHO), '[1/1] echo a\x1b[K\ndo thing\n') 127 128 def test_ninja_status_quiet(self): 129 'Do we suppress the status information when --quiet is specified?' 130 output = run(Output.BUILD_SIMPLE_ECHO, flags='--quiet') 131 self.assertEqual(output, 'do thing\n') 132 133 def test_entering_directory_on_stdout(self): 134 output = run(Output.BUILD_SIMPLE_ECHO, flags='-C$PWD', pipe=True) 135 self.assertEqual(output.splitlines()[0][:25], "ninja: Entering directory") 136 137 def test_tool_inputs(self): 138 plan = ''' 139rule cat 140 command = cat $in $out 141build out1 : cat in1 142build out2 : cat in2 out1 143build out3 : cat out2 out1 | implicit || order_only 144''' 145 self.assertEqual(run(plan, flags='-t inputs out3'), 146'''implicit 147in1 148in2 149order_only 150out1 151out2 152''') 153 154 155if __name__ == '__main__': 156 unittest.main() 157