#!/usr/bin/env python3 """Runs ./ninja and checks if the output is correct. In order to simulate a smart terminal it uses the 'script' command. """ import os import platform import subprocess import sys import tempfile import unittest default_env = dict(os.environ) if 'NINJA_STATUS' in default_env: del default_env['NINJA_STATUS'] if 'CLICOLOR_FORCE' in default_env: del default_env['CLICOLOR_FORCE'] default_env['TERM'] = '' NINJA_PATH = os.path.abspath('./ninja') def run(build_ninja, flags='', pipe=False, env=default_env): with tempfile.TemporaryDirectory() as d: os.chdir(d) with open('build.ninja', 'w') as f: f.write(build_ninja) f.flush() ninja_cmd = '{} {}'.format(NINJA_PATH, flags) try: if pipe: output = subprocess.check_output([ninja_cmd], shell=True, env=env) elif platform.system() == 'Darwin': output = subprocess.check_output(['script', '-q', '/dev/null', 'bash', '-c', ninja_cmd], env=env) else: output = subprocess.check_output(['script', '-qfec', ninja_cmd, '/dev/null'], env=env) except subprocess.CalledProcessError as err: sys.stdout.buffer.write(err.output) raise err final_output = '' for line in output.decode('utf-8').splitlines(True): if len(line) > 0 and line[-1] == '\r': continue final_output += line.replace('\r', '') return final_output @unittest.skipIf(platform.system() == 'Windows', 'These test methods do not work on Windows') class Output(unittest.TestCase): BUILD_SIMPLE_ECHO = '\n'.join(( 'rule echo', ' command = printf "do thing"', ' description = echo $out', '', 'build a: echo', '', )) def test_issue_1418(self): self.assertEqual(run( '''rule echo command = sleep $delay && echo $out description = echo $out build a: echo delay = 3 build b: echo delay = 2 build c: echo delay = 1 ''', '-j3'), '''[1/3] echo c\x1b[K c [2/3] echo b\x1b[K b [3/3] echo a\x1b[K a ''') def test_issue_1214(self): print_red = '''rule echo command = printf '\x1b[31mred\x1b[0m' description = echo $out build a: echo ''' # Only strip color when ninja's output is piped. self.assertEqual(run(print_red), '''[1/1] echo a\x1b[K \x1b[31mred\x1b[0m ''') self.assertEqual(run(print_red, pipe=True), '''[1/1] echo a red ''') # Even in verbose mode, colors should still only be stripped when piped. self.assertEqual(run(print_red, flags='-v'), '''[1/1] printf '\x1b[31mred\x1b[0m' \x1b[31mred\x1b[0m ''') self.assertEqual(run(print_red, flags='-v', pipe=True), '''[1/1] printf '\x1b[31mred\x1b[0m' red ''') # CLICOLOR_FORCE=1 can be used to disable escape code stripping. env = default_env.copy() env['CLICOLOR_FORCE'] = '1' self.assertEqual(run(print_red, pipe=True, env=env), '''[1/1] echo a \x1b[31mred\x1b[0m ''') def test_issue_1966(self): self.assertEqual(run( '''rule cat command = cat $rspfile $rspfile > $out rspfile = cat.rsp rspfile_content = a b c build a: cat ''', '-j3'), '''[1/1] cat cat.rsp cat.rsp > a\x1b[K ''') def test_pr_1685(self): # Running those tools without .ninja_deps and .ninja_log shouldn't fail. self.assertEqual(run('', flags='-t recompact'), '') self.assertEqual(run('', flags='-t restat'), '') def test_status(self): self.assertEqual(run(''), 'ninja: no work to do.\n') self.assertEqual(run('', pipe=True), 'ninja: no work to do.\n') self.assertEqual(run('', flags='--quiet'), '') def test_ninja_status_default(self): 'Do we show the default status by default?' self.assertEqual(run(Output.BUILD_SIMPLE_ECHO), '[1/1] echo a\x1b[K\ndo thing\n') def test_ninja_status_quiet(self): 'Do we suppress the status information when --quiet is specified?' output = run(Output.BUILD_SIMPLE_ECHO, flags='--quiet') self.assertEqual(output, 'do thing\n') def test_entering_directory_on_stdout(self): output = run(Output.BUILD_SIMPLE_ECHO, flags='-C$PWD', pipe=True) self.assertEqual(output.splitlines()[0][:25], "ninja: Entering directory") def test_tool_inputs(self): plan = ''' rule cat command = cat $in $out build out1 : cat in1 build out2 : cat in2 out1 build out3 : cat out2 out1 | implicit || order_only ''' self.assertEqual(run(plan, flags='-t inputs out3'), '''implicit in1 in2 order_only out1 out2 ''') if __name__ == '__main__': unittest.main()