1695b41eeSopenharmony_ci#!/usr/bin/env python3
2695b41eeSopenharmony_ci
3695b41eeSopenharmony_ci"""Runs ./ninja and checks if the output is correct.
4695b41eeSopenharmony_ci
5695b41eeSopenharmony_ciIn order to simulate a smart terminal it uses the 'script' command.
6695b41eeSopenharmony_ci"""
7695b41eeSopenharmony_ci
8695b41eeSopenharmony_ciimport os
9695b41eeSopenharmony_ciimport platform
10695b41eeSopenharmony_ciimport subprocess
11695b41eeSopenharmony_ciimport sys
12695b41eeSopenharmony_ciimport tempfile
13695b41eeSopenharmony_ciimport unittest
14695b41eeSopenharmony_ci
15695b41eeSopenharmony_cidefault_env = dict(os.environ)
16695b41eeSopenharmony_cidefault_env.pop('NINJA_STATUS', None)
17695b41eeSopenharmony_cidefault_env.pop('CLICOLOR_FORCE', None)
18695b41eeSopenharmony_cidefault_env['TERM'] = ''
19695b41eeSopenharmony_ciNINJA_PATH = os.path.abspath('./ninja')
20695b41eeSopenharmony_ci
21695b41eeSopenharmony_cidef run(build_ninja, flags='', pipe=False, env=default_env):
22695b41eeSopenharmony_ci    with tempfile.TemporaryDirectory() as d:
23695b41eeSopenharmony_ci        with open(os.path.join(d, 'build.ninja'), 'w') as f:
24695b41eeSopenharmony_ci            f.write(build_ninja)
25695b41eeSopenharmony_ci            f.flush()
26695b41eeSopenharmony_ci        ninja_cmd = '{} {}'.format(NINJA_PATH, flags)
27695b41eeSopenharmony_ci        try:
28695b41eeSopenharmony_ci            if pipe:
29695b41eeSopenharmony_ci                output = subprocess.check_output([ninja_cmd], shell=True, cwd=d, env=env)
30695b41eeSopenharmony_ci            elif platform.system() == 'Darwin':
31695b41eeSopenharmony_ci                output = subprocess.check_output(['script', '-q', '/dev/null', 'bash', '-c', ninja_cmd],
32695b41eeSopenharmony_ci                                                 cwd=d, env=env)
33695b41eeSopenharmony_ci            else:
34695b41eeSopenharmony_ci                output = subprocess.check_output(['script', '-qfec', ninja_cmd, '/dev/null'],
35695b41eeSopenharmony_ci                                                 cwd=d, env=env)
36695b41eeSopenharmony_ci        except subprocess.CalledProcessError as err:
37695b41eeSopenharmony_ci            sys.stdout.buffer.write(err.output)
38695b41eeSopenharmony_ci            raise err
39695b41eeSopenharmony_ci    final_output = ''
40695b41eeSopenharmony_ci    for line in output.decode('utf-8').splitlines(True):
41695b41eeSopenharmony_ci        if len(line) > 0 and line[-1] == '\r':
42695b41eeSopenharmony_ci            continue
43695b41eeSopenharmony_ci        final_output += line.replace('\r', '')
44695b41eeSopenharmony_ci    return final_output
45695b41eeSopenharmony_ci
46695b41eeSopenharmony_ci@unittest.skipIf(platform.system() == 'Windows', 'These test methods do not work on Windows')
47695b41eeSopenharmony_ciclass Output(unittest.TestCase):
48695b41eeSopenharmony_ci    BUILD_SIMPLE_ECHO = '\n'.join((
49695b41eeSopenharmony_ci        'rule echo',
50695b41eeSopenharmony_ci        '  command = printf "do thing"',
51695b41eeSopenharmony_ci        '  description = echo $out',
52695b41eeSopenharmony_ci        '',
53695b41eeSopenharmony_ci        'build a: echo',
54695b41eeSopenharmony_ci        '',
55695b41eeSopenharmony_ci    ))
56695b41eeSopenharmony_ci
57695b41eeSopenharmony_ci    def test_issue_1418(self):
58695b41eeSopenharmony_ci        self.assertEqual(run(
59695b41eeSopenharmony_ci'''rule echo
60695b41eeSopenharmony_ci  command = sleep $delay && echo $out
61695b41eeSopenharmony_ci  description = echo $out
62695b41eeSopenharmony_ci
63695b41eeSopenharmony_cibuild a: echo
64695b41eeSopenharmony_ci  delay = 3
65695b41eeSopenharmony_cibuild b: echo
66695b41eeSopenharmony_ci  delay = 2
67695b41eeSopenharmony_cibuild c: echo
68695b41eeSopenharmony_ci  delay = 1
69695b41eeSopenharmony_ci''', '-j3'),
70695b41eeSopenharmony_ci'''[1/3] echo c\x1b[K
71695b41eeSopenharmony_cic
72695b41eeSopenharmony_ci[2/3] echo b\x1b[K
73695b41eeSopenharmony_cib
74695b41eeSopenharmony_ci[3/3] echo a\x1b[K
75695b41eeSopenharmony_cia
76695b41eeSopenharmony_ci''')
77695b41eeSopenharmony_ci
78695b41eeSopenharmony_ci    def test_issue_1214(self):
79695b41eeSopenharmony_ci        print_red = '''rule echo
80695b41eeSopenharmony_ci  command = printf '\x1b[31mred\x1b[0m'
81695b41eeSopenharmony_ci  description = echo $out
82695b41eeSopenharmony_ci
83695b41eeSopenharmony_cibuild a: echo
84695b41eeSopenharmony_ci'''
85695b41eeSopenharmony_ci        # Only strip color when ninja's output is piped.
86695b41eeSopenharmony_ci        self.assertEqual(run(print_red),
87695b41eeSopenharmony_ci'''[1/1] echo a\x1b[K
88695b41eeSopenharmony_ci\x1b[31mred\x1b[0m
89695b41eeSopenharmony_ci''')
90695b41eeSopenharmony_ci        self.assertEqual(run(print_red, pipe=True),
91695b41eeSopenharmony_ci'''[1/1] echo a
92695b41eeSopenharmony_cired
93695b41eeSopenharmony_ci''')
94695b41eeSopenharmony_ci        # Even in verbose mode, colors should still only be stripped when piped.
95695b41eeSopenharmony_ci        self.assertEqual(run(print_red, flags='-v'),
96695b41eeSopenharmony_ci'''[1/1] printf '\x1b[31mred\x1b[0m'
97695b41eeSopenharmony_ci\x1b[31mred\x1b[0m
98695b41eeSopenharmony_ci''')
99695b41eeSopenharmony_ci        self.assertEqual(run(print_red, flags='-v', pipe=True),
100695b41eeSopenharmony_ci'''[1/1] printf '\x1b[31mred\x1b[0m'
101695b41eeSopenharmony_cired
102695b41eeSopenharmony_ci''')
103695b41eeSopenharmony_ci
104695b41eeSopenharmony_ci        # CLICOLOR_FORCE=1 can be used to disable escape code stripping.
105695b41eeSopenharmony_ci        env = default_env.copy()
106695b41eeSopenharmony_ci        env['CLICOLOR_FORCE'] = '1'
107695b41eeSopenharmony_ci        self.assertEqual(run(print_red, pipe=True, env=env),
108695b41eeSopenharmony_ci'''[1/1] echo a
109695b41eeSopenharmony_ci\x1b[31mred\x1b[0m
110695b41eeSopenharmony_ci''')
111695b41eeSopenharmony_ci
112695b41eeSopenharmony_ci    def test_issue_1966(self):
113695b41eeSopenharmony_ci        self.assertEqual(run(
114695b41eeSopenharmony_ci'''rule cat
115695b41eeSopenharmony_ci  command = cat $rspfile $rspfile > $out
116695b41eeSopenharmony_ci  rspfile = cat.rsp
117695b41eeSopenharmony_ci  rspfile_content = a b c
118695b41eeSopenharmony_ci
119695b41eeSopenharmony_cibuild a: cat
120695b41eeSopenharmony_ci''', '-j3'),
121695b41eeSopenharmony_ci'''[1/1] cat cat.rsp cat.rsp > a\x1b[K
122695b41eeSopenharmony_ci''')
123695b41eeSopenharmony_ci
124695b41eeSopenharmony_ci
125695b41eeSopenharmony_ci    def test_pr_1685(self):
126695b41eeSopenharmony_ci        # Running those tools without .ninja_deps and .ninja_log shouldn't fail.
127695b41eeSopenharmony_ci        self.assertEqual(run('', flags='-t recompact'), '')
128695b41eeSopenharmony_ci        self.assertEqual(run('', flags='-t restat'), '')
129695b41eeSopenharmony_ci
130695b41eeSopenharmony_ci    def test_issue_2048(self):
131695b41eeSopenharmony_ci        with tempfile.TemporaryDirectory() as d:
132695b41eeSopenharmony_ci            with open(os.path.join(d, 'build.ninja'), 'w'):
133695b41eeSopenharmony_ci                pass
134695b41eeSopenharmony_ci
135695b41eeSopenharmony_ci            with open(os.path.join(d, '.ninja_log'), 'w') as f:
136695b41eeSopenharmony_ci                f.write('# ninja log v4\n')
137695b41eeSopenharmony_ci
138695b41eeSopenharmony_ci            try:
139695b41eeSopenharmony_ci                output = subprocess.check_output([NINJA_PATH, '-t', 'recompact'],
140695b41eeSopenharmony_ci                                                 cwd=d,
141695b41eeSopenharmony_ci                                                 env=default_env,
142695b41eeSopenharmony_ci                                                 stderr=subprocess.STDOUT,
143695b41eeSopenharmony_ci                                                 text=True
144695b41eeSopenharmony_ci                                                 )
145695b41eeSopenharmony_ci
146695b41eeSopenharmony_ci                self.assertEqual(
147695b41eeSopenharmony_ci                    output.strip(),
148695b41eeSopenharmony_ci                    "ninja: warning: build log version is too old; starting over"
149695b41eeSopenharmony_ci                )
150695b41eeSopenharmony_ci            except subprocess.CalledProcessError as err:
151695b41eeSopenharmony_ci                self.fail("non-zero exit code with: " + err.output)
152695b41eeSopenharmony_ci
153695b41eeSopenharmony_ci    def test_status(self):
154695b41eeSopenharmony_ci        self.assertEqual(run(''), 'ninja: no work to do.\n')
155695b41eeSopenharmony_ci        self.assertEqual(run('', pipe=True), 'ninja: no work to do.\n')
156695b41eeSopenharmony_ci        self.assertEqual(run('', flags='--quiet'), '')
157695b41eeSopenharmony_ci
158695b41eeSopenharmony_ci    def test_ninja_status_default(self):
159695b41eeSopenharmony_ci        'Do we show the default status by default?'
160695b41eeSopenharmony_ci        self.assertEqual(run(Output.BUILD_SIMPLE_ECHO), '[1/1] echo a\x1b[K\ndo thing\n')
161695b41eeSopenharmony_ci
162695b41eeSopenharmony_ci    def test_ninja_status_quiet(self):
163695b41eeSopenharmony_ci        'Do we suppress the status information when --quiet is specified?'
164695b41eeSopenharmony_ci        output = run(Output.BUILD_SIMPLE_ECHO, flags='--quiet')
165695b41eeSopenharmony_ci        self.assertEqual(output, 'do thing\n')
166695b41eeSopenharmony_ci
167695b41eeSopenharmony_ci    def test_entering_directory_on_stdout(self):
168695b41eeSopenharmony_ci        output = run(Output.BUILD_SIMPLE_ECHO, flags='-C$PWD', pipe=True)
169695b41eeSopenharmony_ci        self.assertEqual(output.splitlines()[0][:25], "ninja: Entering directory")
170695b41eeSopenharmony_ci
171695b41eeSopenharmony_ci    def test_tool_inputs(self):
172695b41eeSopenharmony_ci        plan = '''
173695b41eeSopenharmony_cirule cat
174695b41eeSopenharmony_ci  command = cat $in $out
175695b41eeSopenharmony_cibuild out1 : cat in1
176695b41eeSopenharmony_cibuild out2 : cat in2 out1
177695b41eeSopenharmony_cibuild out3 : cat out2 out1 | implicit || order_only
178695b41eeSopenharmony_ci'''
179695b41eeSopenharmony_ci        self.assertEqual(run(plan, flags='-t inputs out3'),
180695b41eeSopenharmony_ci'''implicit
181695b41eeSopenharmony_ciin1
182695b41eeSopenharmony_ciin2
183695b41eeSopenharmony_ciorder_only
184695b41eeSopenharmony_ciout1
185695b41eeSopenharmony_ciout2
186695b41eeSopenharmony_ci''')
187695b41eeSopenharmony_ci
188695b41eeSopenharmony_ci
189695b41eeSopenharmony_ciif __name__ == '__main__':
190695b41eeSopenharmony_ci    unittest.main()
191