1#!/usr/bin/env python
2#
3# Copyright 2019 Espressif Systems (Shanghai) CO LTD
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#     http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17import os
18import subprocess
19import sys
20import unittest
21
22try:
23    from StringIO import StringIO
24except ImportError:
25    from io import StringIO
26
27try:
28    import idf
29except ImportError:
30    sys.path.append('..')
31    import idf
32
33current_dir = os.path.dirname(os.path.realpath(__file__))
34idf_py_path = os.path.join(current_dir, '..', 'idf.py')
35extension_path = os.path.join(current_dir, 'test_idf_extensions', 'test_ext')
36link_path = os.path.join(current_dir, '..', 'idf_py_actions', 'test_ext')
37
38
39class TestExtensions(unittest.TestCase):
40    def test_extension_loading(self):
41        try:
42            os.symlink(extension_path, link_path)
43            os.environ['IDF_EXTRA_ACTIONS_PATH'] = os.path.join(current_dir, 'extra_path')
44            output = subprocess.check_output([sys.executable, idf_py_path, '--help'],
45                                             env=os.environ).decode('utf-8', 'ignore')
46
47            self.assertIn('--test-extension-option', output)
48            self.assertIn('test_subcommand', output)
49            self.assertIn('--some-extension-option', output)
50            self.assertIn('extra_subcommand', output)
51        finally:
52            os.remove(link_path)
53
54    def test_extension_execution(self):
55        try:
56            os.symlink(extension_path, link_path)
57            os.environ['IDF_EXTRA_ACTIONS_PATH'] = ';'.join([os.path.join(current_dir, 'extra_path')])
58            output = subprocess.check_output(
59                [sys.executable, idf_py_path, '--some-extension-option=awesome', 'test_subcommand', 'extra_subcommand'],
60                env=os.environ).decode('utf-8', 'ignore')
61            self.assertIn('!!! From some global callback: awesome', output)
62            self.assertIn('!!! From some subcommand', output)
63            self.assertIn('!!! From test global callback: test', output)
64            self.assertIn('!!! From some subcommand', output)
65        finally:
66            os.remove(link_path)
67
68    def test_hidden_commands(self):
69        try:
70            os.symlink(extension_path, link_path)
71            os.environ['IDF_EXTRA_ACTIONS_PATH'] = ';'.join([os.path.join(current_dir, 'extra_path')])
72            output = subprocess.check_output([sys.executable, idf_py_path, '--help'],
73                                             env=os.environ).decode('utf-8', 'ignore')
74            self.assertIn('test_subcommand', output)
75            self.assertNotIn('hidden_one', output)
76
77        finally:
78            os.remove(link_path)
79
80
81class TestDependencyManagement(unittest.TestCase):
82    def test_dependencies(self):
83        result = idf.init_cli()(
84            args=['--dry-run', 'flash'],
85            standalone_mode=False,
86        )
87        self.assertEqual(['flash'], list(result.keys()))
88
89    def test_order_only_dependencies(self):
90        result = idf.init_cli()(
91            args=['--dry-run', 'build', 'fullclean', 'all'],
92            standalone_mode=False,
93        )
94        self.assertEqual(['fullclean', 'all'], list(result.keys()))
95
96    def test_repeated_dependencies(self):
97        result = idf.init_cli()(
98            args=['--dry-run', 'fullclean', 'app', 'fullclean', 'fullclean'],
99            standalone_mode=False,
100        )
101        self.assertEqual(['fullclean', 'app'], list(result.keys()))
102
103    def test_complex_case(self):
104        result = idf.init_cli()(
105            args=['--dry-run', 'clean', 'monitor', 'clean', 'fullclean', 'flash'],
106            standalone_mode=False,
107        )
108        self.assertEqual(['fullclean', 'clean', 'flash', 'monitor'], list(result.keys()))
109
110    def test_dupplicated_commands_warning(self):
111        capturedOutput = StringIO()
112        sys.stderr = capturedOutput
113        idf.init_cli()(
114            args=['--dry-run', 'clean', 'monitor', 'build', 'clean', 'fullclean', 'all'],
115            standalone_mode=False,
116        )
117        sys.stderr = sys.__stderr__
118        self.assertIn(
119            'WARNING: Commands "all", "clean" are found in the list of commands more than once.',
120            capturedOutput.getvalue())
121
122        sys.stderr = capturedOutput
123        idf.init_cli()(
124            args=['--dry-run', 'clean', 'clean'],
125            standalone_mode=False,
126        )
127        sys.stderr = sys.__stderr__
128        self.assertIn(
129            'WARNING: Command "clean" is found in the list of commands more than once.', capturedOutput.getvalue())
130
131
132class TestVerboseFlag(unittest.TestCase):
133    def test_verbose_messages(self):
134        output = subprocess.check_output(
135            [
136                sys.executable,
137                idf_py_path,
138                '-C%s' % current_dir,
139                '-v',
140                'test-verbose',
141            ], env=os.environ).decode('utf-8', 'ignore')
142
143        self.assertIn('Verbose mode on', output)
144
145    def test_verbose_messages_not_shown_by_default(self):
146        output = subprocess.check_output(
147            [
148                sys.executable,
149                idf_py_path,
150                '-C%s' % current_dir,
151                'test-verbose',
152            ], env=os.environ).decode('utf-8', 'ignore')
153
154        self.assertIn('Output from test-verbose', output)
155        self.assertNotIn('Verbose mode on', output)
156
157
158class TestGlobalAndSubcommandParameters(unittest.TestCase):
159    def test_set_twice_same_value(self):
160        """Can set -D twice: globally and for subcommand if values are the same"""
161
162        idf.init_cli()(
163            args=['--dry-run', '-DAAA=BBB', '-DCCC=EEE', 'build', '-DAAA=BBB', '-DCCC=EEE'],
164            standalone_mode=False,
165        )
166
167    def test_set_twice_different_values(self):
168        """Cannot set -D twice: for command and subcommand of idf.py (with different values)"""
169
170        with self.assertRaises(idf.FatalError):
171            idf.init_cli()(
172                args=['--dry-run', '-DAAA=BBB', 'build', '-DAAA=EEE', '-DCCC=EEE'],
173                standalone_mode=False,
174            )
175
176
177class TestDeprecations(unittest.TestCase):
178    def test_exit_with_error_for_subcommand(self):
179        try:
180            subprocess.check_output([sys.executable, idf_py_path, '-C%s' % current_dir, 'test-2'], env=os.environ,
181                                    stderr=subprocess.STDOUT)
182        except subprocess.CalledProcessError as e:
183            self.assertIn('Error: Command "test-2" is deprecated and was removed.', e.output.decode('utf-8', 'ignore'))
184
185    def test_exit_with_error_for_option(self):
186        try:
187            subprocess.check_output([sys.executable, idf_py_path, '-C%s' % current_dir, '--test-5=asdf'],
188                                    env=os.environ, stderr=subprocess.STDOUT)
189        except subprocess.CalledProcessError as e:
190            self.assertIn('Error: Option "test_5" is deprecated since v2.0 and was removed in v3.0.',
191                          e.output.decode('utf-8', 'ignore'))
192
193    def test_deprecation_messages(self):
194        output = subprocess.check_output(
195            [
196                sys.executable,
197                idf_py_path,
198                '-C%s' % current_dir,
199                '--test-0=a',
200                '--test-1=b',
201                '--test-2=c',
202                '--test-3=d',
203                'test-0',
204                '--test-sub-0=sa',
205                '--test-sub-1=sb',
206                'ta',
207                'test-1',
208            ],
209            env=os.environ, stderr=subprocess.STDOUT).decode('utf-8', 'ignore')
210
211        self.assertIn('Warning: Option "test_sub_1" is deprecated and will be removed in future versions.', output)
212        self.assertIn(
213            'Warning: Command "test-1" is deprecated and will be removed in future versions. '
214            'Please use alternative command.', output)
215        self.assertIn('Warning: Option "test_1" is deprecated and will be removed in future versions.', output)
216        self.assertIn(
217            'Warning: Option "test_2" is deprecated and will be removed in future versions. '
218            'Please update your parameters.', output)
219        self.assertIn('Warning: Option "test_3" is deprecated and will be removed in future versions.', output)
220        self.assertNotIn('"test-0" is deprecated', output)
221        self.assertNotIn('"test_0" is deprecated', output)
222
223
224if __name__ == '__main__':
225    unittest.main()
226